UScript在VS下的阅读及调试
看了一段视频之后才发现VS原来可以像支持代码一样支持UScript,再搜了下,发现调试也是很方便的,看来得尽快把思路从UDK的纯工具路线转回到UE3的代码思路上来,继续深入底层!
非常5Z地转自独行剑侠的Blog,(因为Hikari的原因结识,没想到大牛竟然这么牛,再次默默立誓,要努力。。。)
http://hi.baidu.com/_%E2d_%B7%B3_%DE%B2%C2%D2/blog/item/a5b7c813240ca08d6438db42.html
这个教程教你如何创建UDK的UnrealScript开发环境。
首先,下载nFringe最新版,地址点我。最新版支持VS2005、VS2008以及VS2010。UDK专属的序列号:
(旧版地址,http://ishare.iask.sina.com.cn/f/6374066.html,新版可以用非商业版注册~)
Name:UDK
Key: DB8K-6JF6-7TCW-DVMG-BBBB
注意,这个序列号只能支持名字为UDK.exe的Debug。高亮什么的都有,非商业版不能调试。如果提示时间已过期不妨把系统时间改到以前。
创建UnrealEngine3 Lincensee Project,不要创建解决方案目录,位置必须在UDK的Development/Src下面,名称随便你。
补充下具体过程
"
The log way:
- Open the file Visual Studio
- Select File→New→Project...
- From Project types, select UnrealScript. From Templates, select UnrealEngine 3 Licensee Project. Make sure to select the following:
- Name the project UdkProject.
- For location, select C:\UDK\UDK-2010-05\Development\Src (or the equivalent on your machine)
- Uncheck "Create directory for solution"
- Click OK.
- Close Visual Studio, and open Windows Explorer to the folder C:\UDK\UDK-2010-05\Development\Src\UdkProject.
- 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.
- Move up one folder to C:\UDK\UDK-2010-05\Development\Src. Paste the files here.
- From now on, you can simply open UdkProject.sln to begin working.
"
创建成功之后,点击项目属性,在General中作如下设置:
选择Target Game:UnrealEngine3 Mode.
UCC Path是U3编译器的简写,设置成UDK.exe
相关代码路径选择UDK的Development/Src。
切换到生成选项卡,
选择脚本编译器编译就诶过输出到UDKGame\Script。
切换到调试页面:
地图你可以自己选,你可以指定游戏模式,如果不指定就会是默认的UTDeathMatch,我这里指定的是我创建的游戏DBGame。勾上Enable unpublished mods。
去UDK\UDK-2010-05\UDKGame\Config下面找到UDKEngine.ini,打开,搜索ModEditPackages,解开;ModEditPackages=myMod的注释,改成ModEditPackages=DBGame,这里DBGame替换成你创建的项目名称。
现在关掉VS,重新用VS打开项目。现在VS会帮我们把相关的引用代码都加载进来:
右击项目,添加》添加新文件夹,输入Classes。这个步骤必须是这样的,它会在你的游戏目录下面创建Classes目录:
现在让我们在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++函数。
现在让我们用类似的方法去开发UDK程序吧。
调试方法和C++一样,在配置正确和编译成功之后,按F5就可以了。
下面是设置断点的截图:
调用堆栈:
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
}
效果图:
在公司就是用类似这样的方法实现的,但是不知道为何在Cook之后出错了,今天在家试了了一次,感觉没什么问题啊。可能还是需要COOK一次之后才会出现吧。明天去看看就知道了。
------------------------------------
关于Cook之后的问题,只要把使用begin object初始化Object的方式改成去函数中动态的new就可以了。原因尚未查明,但是这确实可以解决这个与垃圾回收冲突的问题。
2010.9.9 PATCH:
补充说明一下,最新版的NFringe好像序列号已经过期了,大家可以用一个早期的版本来做,步骤和上面说的一样,但是不能支持2010,支持2008.