UScript在VS下的阅读及调试

看了一段视频之后才发现VS原来可以像支持代码一样支持UScript,再搜了下,发现调试也是很方便的,看来得尽快把思路从UDK的纯工具路线转回到UE3的代码思路上来,继续深入底层!

 

 

非常5Z地转自独行剑侠的Blog,(因为Hikari的原因结识,没想到大牛竟然这么牛,再次默默立誓,要努力。。。)

 

 

http://hi.baidu.com/_%E2d_%B7%B3_%DE%B2%C2%D2/blog/item/a5b7c813240ca08d6438db42.html

 

 

这个教程教你如何创建UDKUnrealScript开发环境。

 

 

     首先,下载nFringe最新版,地址点我。最新版支持VS2005VS2008以及VS2010UDK专属的序列号:

 (旧版地址,http://ishare.iask.sina.com.cn/f/6374066.html,新版可以用非商业版注册~)

 

      NameUDK

 

 

      Key DB8K-6JF6-7TCW-DVMG-BBBB

 

 

      注意,这个序列号只能支持名字为UDK.exeDebug。高亮什么的都有,非商业版不能调试。如果提示时间已过期不妨把系统时间改到以前。

 

 

      clip_image002
创建UnrealEngine3 Lincensee Project,不要创建解决方案目录,位置必须在UDKDevelopment/Src下面,名称随便你。

补充下具体过程

"

The log way:

  1. Open the file Visual Studio
  2. Select FileNewProject...
  3. From Project types, select UnrealScript. From Templates, select UnrealEngine 3 Licensee Project. Make sure to select the following:
    1. Name the project UdkProject.
    2. For location, select C:\UDK\UDK-2010-05\Development\Src (or the equivalent on your machine)
    3. Uncheck "Create directory for solution"
    4. Click OK.
  4. Close Visual Studio, and open Windows Explorer to the folder C:\UDK\UDK-2010-05\Development\Src\UdkProject.
  5. Select the files UdkProject.sln, UdkProject.ucproj, and (if you have hidden files visible) UdkProject.suo. Press Ctrl+X or right click and select Cut.
  6. Move up one folder to C:\UDK\UDK-2010-05\Development\Src. Paste the files here.
  7. From now on, you can simply open UdkProject.sln to begin working.

" 

 

 

       创建成功之后,点击项目属性,在General中作如下设置:

 

 

clip_image004
选择Target GameUnrealEngine3 Mode.

 

 

        UCC PathU3编译器的简写,设置成UDK.exe

 

 

      相关代码路径选择UDKDevelopment/Src

 

 

       切换到生成选项卡,

 

 

       clip_image005
选择脚本编译器编译就诶过输出到UDKGame\Script

 

 

    切换到调试页面:

 

 

clip_image006
可执行文件选择UDK.exe

 

 

      地图你可以自己选,你可以指定游戏模式,如果不指定就会是默认的UTDeathMatch,我这里指定的是我创建的游戏DBGame。勾上Enable unpublished mods

 

 

     UDK\UDK-2010-05\UDKGame\Config下面找到UDKEngine.ini,打开,搜索ModEditPackages,解开;ModEditPackages=myMod的注释,改成ModEditPackages=DBGame,这里DBGame替换成你创建的项目名称。

 

 

     现在关掉VS,重新用VS打开项目。现在VS会帮我们把相关的引用代码都加载进来:

 

 

clip_image007

 

 

       右击项目,添加》添加新文件夹,输入Classes。这个步骤必须是这样的,它会在你的游戏目录下面创建Classes目录:

 

 

      clip_image008

 

 

       现在让我们在Classes随便创建一个DBGame.uc,在里面输入类似这样的代码:

 

 

class DBGame extends UTDeathMatch;

 

 

defaultproperties

 

 

{
}

 

 

      编译,我推荐大家用我推荐的方式,打开控制台(开始+R,输入cmd,回车).找到UDK.exe,把它拖到控制台,追加编译参数make -debug,如果是全部编译就是make -debug -full

 

 

     C:\Users\董波>D:\UDK\UDK-2010-05\Binaries\Win32\UDK.exe make -debug

 

 

      C:\Users\董波>D:\UDK\UDK-2010-05\Binaries\Win32\UDK.exe make -debug -full

 

 

     回车,这样UDK会自动帮我们编译了。要重新编译只需要按方向键上就可以取到上一个命令了,这样可以看到UE3控制台,这样会更有帮助。直接在VS中编译我还没去琢磨,等我琢磨出来且觉得更好用的时候再告诉大家吧。

 

 

     注意,创建文件的时候千万不要用VS的向导创建,因为这好像在文件编码方面会有问题,如果你看到类似下面这样的错误:

 

 

      bad class definition ''/'UTDeathMatch'/62/62

 

 

      很有可能就是这个原因,这个时候我们可以用另外的方法替代。之所以不提前告诉大家是因为我不确定在你们自己的机器上是不是也会出现这个问题,因为毕竟用向导还是方便得多。

 

 

      去游戏目录下面的Classes文件夹,创建普通文本文件(记事本),然后把扩展名改成.uc就好了,这样可以确保文件编码就是ASCII了。

 

 

      编译成功的标志是Script文件夹下面会出现对应的.u文件,这是一个二进制文件,里面包含了类似汇编代码的指令代码序列,它们运行于UE3的虚拟机中,每个元操作都对应着一个C++函数。

 

 

  clip_image009

 

 

      现在让我们用类似的方法去开发UDK程序吧。

 

 

        调试方法和C++一样,在配置正确和编译成功之后,按F5就可以了。

 

 

        下面是设置断点的截图:

 

 

clip_image010

 

 

        调用堆栈:

 

 

clip_image011

 

 

      OK,有问题请留言。

 

 

      下面分享一些代码:

 

 

/**
* @brief DBGame
* @author dongbo
* @date 2010.7.4
*/
class DBGame extends UTDeathMatch;

 

 

defaultproperties
{
PlayerControllerClass = class'DBGamePlayerController'
DefaultPawnClass = class'DBGamePawn'
}

 

 

/**
* @brief DBGamePC
* @author dongbo
* @date 2010.7.4
*/
class DBGamePlayerController extends UTPlayerController;

 

 

var() DBGameHUDChainMsgRender GameMsgProvider;
var() Texture2D               KillTex;

 

 

var() int MsgCount;

 

 

simulated event PostBeginPlay()
{
super.PostBeginPlay();

 

 

SetTimer( 3.0, true, nameof(DrawGameInfoTimer) );
}

 

 

simulated function DrawGameInfoTimer()
{
GameMsgProvider.AddStringElement( "
游戏数据: " @ MsgCount, 5, true, true, CMIAT_Center );
++MsgCount;
}

 

 

function DrawHUD( HUD H )
{
super.DrawHUD( H );

 

 

if( GameMsgProvider != none && H.Canvas != none )
{
GameMsgProvider.Render( H.Canvas );
}
}

 

 

reliable function client ClientReceiveMsg( coerce string msg )
{
if( GameMsgProvider != none )
{
GameMsgProvider.AddStringElement( msg, 2.0, false, true, CMIAT_Center );
}
}

 

 

// test only
reliable function client ClientNotifyKillPawn()
{
if( GameMsgProvider != none )
{
GameMsgProvider.AddTextureElement( KillTex, 4.0, true, true, CMIAT_Center );
}
}

 

 

defaultproperties
{
begin object class=DBGameHUDChainMsgRender name=GameMsgProviderImp
end object
GameMsgProvider = GameMsgProviderImp

 

 

KillTex = Texture2D'GFxUTHud.gun1_large'
}

 

 

/**
* @brief DBGamePawn
* @author dongbo
* @date 2010.7.4
*/
class DBGamePawn extends UTPawn;

 

 

/**
* We override TakeDamage and allow the weapon to modify it
* @See Pawn.TakeDamage
*/
event TakeDamage(int Damage, Controller EventInstigator, vector HitLocation, vector Momentum, class<DamageType> DamageType, optional TraceHitInfo HitInfo, optional Actor DamageCauser)
{
local int OldHealth;
local int ActualDamage;
local DBGamePlayerController DBPC;

 

 

OldHealth = Health;

 

 

super.TakeDamage( Damage, EventInstigator, HitLocation, Momentum, DamageType, HitInfo, DamageCauser );

 

 

ActualDamage = FClamp( OldHealth - Health, 0, 100000 );

 

 

if( ActualDamage > 0 )
{
DBPC = DBGamePlayerController( EventInstigator );

 

 

   if( DBPC != none )
{
DBPC.ClientReceiveMsg( "Damage " @ GetHumanReadableName() @ ActualDamage );
}
}
}

 

 

function bool Died(Controller Killer, class<DamageType> damageType, vector HitLocation)
{
local DBGamePlayerController DBPC;
local bool bResult;

 

 

bResult = super.Died( Killer, damageType, HitLocation );

 

 

if( bResult )
{
DBPC = DBGamePlayerController( Killer );

 

 

   if( DBPC != none )
{
DBPC.ClientNotifyKillPawn();
}
}

 

 

return bResult;
}

 

 

defaultproperties
{
}

 

 

/**
* @brief
可绘制信息
* @author dongbo
* @date 2010.7.3
*/
class DBGameHUDChainMsgInfo extends Object;

 

 

/** @brief 初始化时间 */
var() protected float             StartTime;
/** @brief
存在的时间 */
var() protected float             LifeTime;
/** @brief
是否淡入 */
var() protected bool              bFadeIn;
/** @brief
是否淡出 */
var() protected bool              bFadeOut;

 

 

enum DBGameHUDChainMsgInfoAlignType
{
CMIAT_Nothing,
CMIAT_Left,
CMIAT_Center,
CMIAT_Right
};

 

 

/** @brief 对齐方式 */
var() DBGameHUDChainMsgInfoAlignType AlignType;

 

 

/**
* @brief
初始化当前信息
* @param fLifeTime
存在的时间
* @param bUseFadeIn
* @param bUseFadeOut
* @param eAlignType
*/
simulated function bool InitChainMsgInfo( float fLifeTime = 1.0, bool bUseFadeIn = false, bool bUseFadeOut = true, DBGameHUDChainMsgInfoAlignType eAlignType = CMIAT_Nothing )
{
if( fLifeTime <= 0 )
{
return false;
}

 

 

StartTime = GetCurrentTimeSeconds();
LifeTime = fLifeTime;
bFadeIn = bUseFadeIn;
bFadeOut = bUseFadeOut;
AlignType = eAlignType;

 

 

return true;
}

 

 

/**
* @brief
这是对绘制过程的包装 模板方法模式
* @remarks
这个函数将对对齐进行处理并包装基类不能覆盖当前函数 基类只能通过重载OnDraw来实现自定义的行为
* @return
返回当前绘制占用的屏幕大小的绝对值
*/
final simulated function Vector2D Draw( Canvas myCanvas )
{
local Vector2D   DrawSize;
local float      OldX;

 

 

DrawSize = GetDrawSize( myCanvas );

 

 

if( AlignType == CMIAT_Nothing )
{
self.OnDraw( myCanvas );
}
else
{
OldX = myCanvas.CurX;

 

 

   if( AlignType == CMIAT_Left )
{
myCanvas.SetPos( 0, myCanvas.CurY );
}
else
{
if( AlignType == CMIAT_Right )
{
myCanvas.SetPos( myCanvas.ClipX - DrawSize.X, myCanvas.CurY );
}
else
{
myCanvas.SetPos( myCanvas.ClipX/2 - DrawSize.X/2, myCanvas.CurY );
}
}

 

 

   OnDraw( myCanvas );

 

 

   myCanvas.SetPos( OldX, myCanvas.CurY );
}

 

 

return DrawSize;
}

 

 

/**
* @brief
当前绘制需要占用的空间大小
*/
simulated function Vector2D GetDrawSize( Canvas myCanvas )
{
local Vector2D DrawSize;

 

 

`log( "you must overwrite this function!!!" );
ScriptTrace();

 

 

`assert( false );

 

 

return DrawSize;
}

 

 

/**
* @brief
绘制部分的代码 子类可以通过重载它实现自己的绘制过程
* @param myCanvas
*/
simulated protected function OnDraw( Canvas myCanvas );

 

 

/**
* @brief
检查是否已经应该被卸载掉了
*/
simulated function bool IsOutOfLifeTime()
{
local float DeltaTime;

 

 

DeltaTime = GetCurrentTimeSeconds() - StartTime;

 

 

return DeltaTime > LifeTime;
}

 

 

/**
* @brief
获取逝去的时间
*/
simulated function float GetElapseTime()
{
return GetCurrentTimeSeconds() - StartTime;
}

 

 

/**
* @brief
获取逝去时间的比率
*/
simulated function float GetElapseTimeRate()
{
return FClamp( GetElapseTime() / LifeTime, 0.0, 1.0 );
}

 

 

/**
* @brief
获取透明比率
*/
simulated function float GetAlphaFactor()
{
local float TimeRate;
local float Factor;

 

 

TimeRate = GetElapseTimeRate();

 

 

if( bFadeIn && !bFadeOut )
{
Factor = TimeRate;
}
else if( !bFadeIn && bFadeOut )
{
Factor = 1.0 - TimeRate;
}
else if( !bFadeIn && !bFadeOut )
{
Factor = 1.0;
}
else
{
if( TimeRate < 0.5 )
{
Factor = TimeRate/0.5;
}
else
{
Factor = 1.0 - (TimeRate - 0.5)/0.5;
}
}

 

 

return Factor;
}

 

 

/**
* @brief
获取当前系统时间
*/
static simulated function float GetCurrentTimeSeconds()
{
local float CurrentTime;

 

 

CurrentTime = class'Engine'.static.GetCurrentWorldInfo().TimeSeconds;

 

 

return CurrentTime;
}

 

 

/**
* @brief
获取字符串长度信息
* @param myCanvas Canvas
* @param msg
目标字符串
* @param DrawFont
目标字体
*/
static simulated function Vector2D StringSize( Canvas myCanvas, coerce string msg, Font DrawFont )
{
local Font OldFont;
local Vector2D Size;

OldFont = myCanvas.Font;
myCanvas.Font = DrawFont;

 

 

myCanvas.TextSize( msg, Size.X, Size.Y );

myCanvas.Font = OldFont;

 

 

return Size;
}

 

 

/**
* @brief
绘制字符串的函数
* @param myCanvas
* @param msg
目标字符串
* @param DrawFont
目标字体 @note 如果字符串中有中文而字体不支持中文那么会出现乱码或者正方形
* @remarks
导入中文字体:http://www.udkcn.com/bbs/viewthread.php?tid=186
* @param DrawColor
目标颜色 @note 这个颜色是0-255
*/
static simulated function DrawString( Canvas myCanvas, coerce string msg, Font DrawFont, Color DrawColor )
{
local Font OldFont;
local Color OldColor;

 

 

OldFont = myCanvas.Font;
OldColor = myCanvas.DrawColor;

 

 

myCanvas.Font = DrawFont;
myCanvas.DrawColor = DrawColor;

 

 

myCanvas.DrawText( msg );

 

 

myCanvas.Font = OldFont;
myCanvas.DrawColor = OldColor;
}

 

 

static simulated function DrawTexture( Canvas myCanvas, Texture2D Tex, coerce Vector2D Size, coerce TextureCoordinates Coord, LinearColor DrawColor )
{
myCanvas.DrawTile( Tex, Size.X, Size.Y, Coord.U, Coord.V, Coord.UL, Coord.VL, DrawColor );
}

 

 

DefaultProperties
{
}

 

 

/**
* @brief
字符串可绘制信息
* @author dongbo
* @date 2010.7.4
*/
class DBGameHUDChainMsgInfo_String extends DBGameHUDChainMsgInfo;

 

 

var() protected string MsgInfo;
var() Font              DrawFont;
var() Color             DrawColor;

 

 

/**
* @brief
设置绘制目标字符串
* @param msg
*/
simulated function SetMsgInfo( coerce string msg )
{
MsgInfo = msg;
}

 

 

/**
* @brief
绘制部分的代码 子类可以通过重载它实现自己的绘制过程
* @param myCanvas
*/
simulated protected function OnDraw( Canvas myCanvas )
{
local Color ActualColor;
local float Factor;

 

 

ActualColor = DrawColor;

 

 

Factor = GetAlphaFactor();

 

 

ActualColor.A *= Factor;

 

 

DrawString( myCanvas, MsgInfo, DrawFont, ActualColor );
}

 

 

/**
* @brief
当前绘制需要占用的空间大小
*/
simulated function Vector2D GetDrawSize( Canvas myCanvas )
{
local Vector2D DrawSize;

 

 

DrawSize = StringSize( myCanvas, MsgInfo, DrawFont );

 

 

return DrawSize;
}

 

 

defaultproperties
{
DrawFont = Font'DBGameContent.Font.SongChs12'
DrawColor = (R=255,G=255,B=0,A=255)
}

 

 

/**
* @brief
绘制纹理
* @author dongbo
* @date 2010.7.4
*/
class DBGameHUDChainMsgInfo_Texture extends DBGameHUDChainMsgInfo;

 

 

/** @brief 目标纹理 */
var() protected Texture2D              DrawTex;
var() protected float                  Scale;
var() protected TextureCoordinates     Coord;

 

 

/**
* @brief
初始化
*/
simulated function bool InitTexture( Texture2D Tex, float fScale = 1.0 )
{
if( Tex == none )
{
return false;
}

 

 

DrawTex = Tex;
Scale = fScale;

 

 

Coord.U = 0;
Coord.V = 0;
Coord.UL = Tex.SizeX;
Coord.VL = Tex.SizeY;

 

 

return true;
}

 

 

simulated function bool InitTextureEx( Texture2D Tex, coerce TextureCoordinates Coordinate, float fScale = 1.0 )
{
if( Tex == none )
{
return false;
}

 

 

DrawTex = Tex;
Scale = fScale;
Coord = Coordinate;

 

 

return true;
}

 

 

/**
* @brief
当前绘制需要占用的空间大小
*/
simulated function Vector2D GetDrawSize( Canvas myCanvas )
{
local Vector2D DrawSize;

 

 

if( DrawTex == none )
{
DrawSize.X = 0.0;
DrawSize.Y = 0.0;
return DrawSize;
}

 

 

DrawSize.X = DrawTex.SizeX * Scale;
DrawSize.Y = DrawTex.SizeY * Scale;

 

 

return DrawSize;
}

 

 

/**
* @brief
绘制部分的代码 子类可以通过重载它实现自己的绘制过程
* @param myCanvas
*/
simulated protected function OnDraw( Canvas myCanvas )
{
local float Factor;
local LinearColor ActualColor;

 

 

Factor = GetAlphaFactor();
ActualColor.R = 1.0;
ActualColor.G = 1.0;
ActualColor.B = 1.0;
ActualColor.A = Factor;

 

 

DrawTexture( myCanvas, DrawTex, GetDrawSize( myCanvas ), Coord, ActualColor );
}

 

 

defaultproperties
{
Scale = 1.0
}

 

 

/**
* @brief
实现HUD绘制渐变物体
* @author dongbo
* @date 2010.7.3
*/
class DBGameHUDChainMsgRender extends Object;

 

 

/** @brief 在屏幕上面的相对位置 */
var() Vector2D                RelativeStartPosition;

 

 

/** @brief Y轴间隔的相对值 这个值会乘以屏幕高度 */
var() float                   RelativeBlank;

 

 

/** @brief 最大元素个数 **/
var() int                     MaxCount;

 

 

/** @brief 元素集合 */
var() protected array<DBGameHUDChainMsgInfo> Elements;

 

 

/** @brief 元素个数检查 **/
simulated singular protected event CheckCount()
{
if( GetCount() > MaxCount )
{
LifeTimeCheck();

if( GetCount() > MaxCount )
{
Elements.Remove( 0, GetCount() - MaxCount );
}
}
}

 

 

/** @brief 检查所有元素 删除已经度过生命期的然后删除掉 */
simulated protected event LifeTimeCheck()
{
local int i;
local DBGameHUDChainMsgInfo Info;

 

 

i = 0;

 

 

while( i < Elements.Length )
{
Info = Elements[i];

 

 

   if( Info.IsOutOfLifeTime() )
{
Elements.Remove( i, 1 );
}
else
{
++i;
}
}
}

 

 

/**
* @brief
HUD的参数Canvas调用只是对Render的简单包装
*/
simulated function Draw( Canvas myCanvas )
{
Render( myCanvas );
}

 

 

/**
* @brief
绘制部分的代码
* @remarks
从数据元素的中依次取出相应的代码 然后通过元素的绘制函数返回当前绘制占用的大小
*/
simulated function Render( Canvas myCanvas )
{
local Vector2D StartPlace;
local Vector2D DrawSize;
local float    Blank;
local int i;

 

 

self.LifeTimeCheck();

 

 

if( Elements.Length <= 0 )
{
return;
}

 

 

Blank = myCanvas.ClipY * RelativeBlank;

 

 

StartPlace.X = RelativeStartPosition.X * myCanvas.ClipX;
StartPlace.Y = RelativeStartPosition.Y * myCanvas.ClipY;

 

 

for( i=0; i<Elements.Length; ++i )
{
myCanvas.SetPos( StartPlace.X, StartPlace.Y );
DrawSize = Elements[i].Draw( myCanvas );
StartPlace.Y += DrawSize.Y;
StartPlace.Y += Blank;
}
}

 

 

simulated function int GetCount()
{
return Elements.Length;
}

 

 

simulated function bool AddElement( DBGameHUDChainMsgInfo BaseInfo )
{
LifeTimeCheck();
CheckCount();

 

 

// 已经满了
if( GetCount() == MaxCount && GetCount() > 0 )
{
Elements.Remove( 0, 1 );
}

 

 

Elements.AddItem( BaseInfo );

 

 

return true;
}

 

 

simulated function bool AddStringElement( coerce string msg, float fLifeTime = 1.0, bool bUseFadeIn = false, bool bUseFadeOut = true, DBGameHUDChainMsgInfoAlignType eAlignType = CMIAT_Nothing )
{
local DBGameHUDChainMsgInfo_String strInfo;

strInfo = new class'DBGameHUDChainMsgInfo_String';

if( !strInfo.InitChainMsgInfo( fLifeTime, bUseFadeIn, bUseFadeOut, eAlignType ) )
{
`log( "Error For DBGameHUDChainMsgRender AddStringElement.Can't Init StringMsgInfo..." );
return false;
}

strInfo.SetMsgInfo( msg );

 

 

self.AddElement( strInfo );

return true;
}

 

 

simulated function bool AddTextureElement( Texture2D tex, float fLifeTime = 1.0, bool bUseFadeIn = false, bool bUseFadeOut = true, DBGameHUDChainMsgInfoAlignType eAlignType = CMIAT_Nothing )
{
local DBGameHUDChainMsgInfo_Texture texInfo;

 

 

texInfo = new class'DBGameHUDChainMsgInfo_Texture';

 

 

if( !texInfo.InitChainMsgInfo( fLifeTime, bUseFadeIn, bUseFadeOut, eAlignType ) )
{
return false;
}

if( !texInfo.InitTexture( tex ) )
{
return false;
}

 

 

AddElement( texInfo );

 

 

return true;
}

 

 

DefaultProperties
{
RelativeStartPosition=(X=0.4,Y=0.35)
RelativeBlank = 0.005
MaxCount = 4
}

 

 

效果图:

 

 

clip_image012

 

 

clip_image013

 

 

         在公司就是用类似这样的方法实现的,但是不知道为何在Cook之后出错了,今天在家试了了一次,感觉没什么问题啊。可能还是需要COOK一次之后才会出现吧。明天去看看就知道了。

 

 

------------------------------------

 

 

关于Cook之后的问题,只要把使用begin object初始化Object的方式改成去函数中动态的new就可以了。原因尚未查明,但是这确实可以解决这个与垃圾回收冲突的问题。

 

 

2010.9.9 PATCH:

 

 

clip_image014补充说明一下,最新版的NFringe好像序列号已经过期了,大家可以用一个早期的版本来做,步骤和上面说的一样,但是不能支持2010,支持2008.

 

 

posted @ 2012-02-09 15:29  Zephyroal  阅读(1425)  评论(0编辑  收藏  举报