Tuesday, October 2, 2012

UDK-Toturial-CH6.UnrealScript-Enemy And GUI

1.Expand Our Enemy.
2.Write Score on Screen.(Dont Use Scaleform now)
3.Spawn Actor(Enemy).

 
A. Better Way to Create Enemy:
在前面已產生簡易的敵人,玩家會碰到而因此受傷,但並不會有追逐效果,僅適合使用在靜態陷阱之類的,做一點小改變讓敵人會追逐玩家,因此要修改SimpleEnemy.uc:
增加變數:
var Pawn PCEnemy;
為了要讓每個Frame要更新,且更新敵人移動的邏輯,在非PlayerController的script要使用了Tick(float DeltaTime),若是PlayerController下則要使用PlayerTick(float DeltaTime),因為這是每個Frame更新因此要避免一些可能會造成緩慢的程式碼放過多在裡面,例如ForEach…,DeltaTime每個Frame之間的時間,以UDK來說是每秒跑60Frame因此DeltaTime應該是 1/60=0.016667,因SimpleEnemy並沒繼承PlayerController因此增加Tick(float DeltaTime):
function Tick(float DeltaTime)
{
local CustomTopDownController player;
if(PCEnemy == none)
foreach LocalPlayerControllers(class'CustomTopDownController',player)
{
if(Player.Pawn != none)
{
PCEnemy= Player.Pawn;
`log("Enemy Target is :" @ PCEnemy)
}
}
}
現在為敵人增加追擊距離,宣告:
var float ChaseDistance;
設定預設值:
ChaseDistance=512.0
改變Tick(float DeltaTime):
function Tick(float DeltaTime)
{
local CustomTopDownController player;
local vector NewLocation;
if(PCEnemy == none)
{
foreach LocalPlayerControllers(class'CustomTopDownController',player)
{
if(Player.Pawn != none)
{
PCEnemy= Player.Pawn;
`log("Enemy Target is :" @ PCEnemy);
}
}
}
else if(VSize(Location - PCEnemy.Location) < ChaseDistance)
{
NewLocation=Location;
NewLocation +=(PCEnemy.Location - Location) * DeltaTime;
SetLocation(NewLocation);
}
}
將取得玩家操作的Actor,設定成敵人的Target,取的玩家與敵人自身位置,利用SetLocation()移動敵人位置,執行後,當玩家Actor距離跟敵人小於512則進行追擊:

為敵人設定一個攻擊範圍(敵人跟玩家距離小於某一定值則扣血),宣告:
var float AttackDistance;
設定範圍值:
AttackDistance=96.0
改變Tick(float DeltaTime):
function Tick(float DeltaTime)
{
local CustomTopDownController player;
local vector NewLocation;
if(PCEnemy == none)
{
foreach LocalPlayerControllers(class'CustomTopDownController',player)
{
if(Player.Pawn != none)
{
PCEnemy= Player.Pawn;
`log("Enemy Target is :" @ PCEnemy);
}
}
}
else if(VSize(Location - PCEnemy.Location) < ChaseDistance)
{
if(VSize(Location-PCEnemy.Location) < AttackDistance)
{
PCEnemy.Bump(self,CollisionComponent,vect(0,0,0));
}
else
{
NewLocation=Location;
NewLocation +=(PCEnemy.Location - Location) * DeltaTime;
SetLocation(NewLocation);
}
}
}
最基本的敵人原型已產生。
B. Basic GUI(No use ScaleForm)
現在必須新增GUI並去繼承 UTGFxHUDWrapper,產生一個CustomGUI.uc:
class CustomHUD extends UTGFxHUDWrapper;

simulated function PostBeginPlay()
{
super.PostBeginPlay();
`log("CustomHUD");
}
DefaultProperties
{
}

Compiler後確定CustomHUD.uc沒Error後,將CustomTopDownController.uc更改HUD成CustomHUD.uc,修改CustomTopDownController.uc:
reliable client function ClientSetHUD(class<HUD> newHUDType)
{
if(myHUD != none)
myHUD.Destory();
myHUD = spawn(class'CustomHUD',self);
}
Compiler後,在log將會看到CustomHUD代表確實執行。
接著實作CustomHUD.uc:
增加DrawHUD() event:
event DrawHUD()
{
super.DrawHUD();
if(PlayerOwner.Pawn != none && CustomWeapon(PlayerOwner.Pawn.Weapon) != none)
{
Canvas.DrawColor=WhiteColor;
Canvas.Font=class'Engine'.Static.GetLargeFont();
Canvas.SetPos(Canvas.ClipX * 0.1,Canvas.ClipY * 0.1);
Canvas.DrawText("WeaponLevel:" @CustomWeapon(PlayerOwner.Pawn.Weapon).CurrentWeaponLevel);
}
}
SetPost()的0.1代表遊戲畫面的百分比,跟Unity3D不一樣,UDK跟一般XNA等以螢幕左上角為起始點,代表在螢幕左上角,並在武器被撿起時才顯示文字:

接下來要顯示敵人數目因此修改FunnyGameInfo.uc:
新增參數:
var int EnemiesLeft;
修改PostBeginPlay():
simulated function PostBeginPlay()
{
local SimpleEnemy SE;
super.PostBeginPlay();
GoalScore=0;
foreach DynamicActors(class'SimpleEnemy',SE)
GoalScore++;
EnemiesLeft = GoalScore;
}
增加ScoreObjective() function:
function ScoreObjective(PlayerReplicationInfo playerScore,int score)
{
EnemiesLeft--;
super.PostBeginPlay(playerScore,score);
}
進行分數減少,接著更改在CustomHUD.uc 的DrawHUD function,將DrawColor和Font移到if statement 外,再增加一個If statement:
event DrawHUD()
{
super.DrawHUD();
Canvas.DrawColor=WhiteColor;
Canvas.Font=class'Engine'.Static.GetLargeFont();
if(PlayerOwner.Pawn != none && CustomWeapon(PlayerOwner.Pawn.Weapon) != none)
{
Canvas.SetPos(Canvas.ClipX * 0.1,Canvas.ClipY * 0.1);
Canvas.DrawText("WeaponLevel:" @CustomWeapon(PlayerOwner.Pawn.Weapon).CurrentWeaponLevel);
}
if(FunnyGameInfo(WorldInfo.Game) != none)
{
Canvas.SetPos(Canvas.ClipX * 0.1,Canvas.ClipY *0.15);
Canvas.DrawText("Enemies Left:" @ FunnyGameInfo(WorldInfo.Game).EnemiesLeft);
}
}
Compiler後結果:

若要顯示殺敵修改DrawHUD function:
if(FunnyGameInfo(WorldInfo) != none)
{
Canvas.SetPos(Canvas.ClipX *0.1,Canvas.ClipY *0.2);
Canvas.DrawText(("Enemies Left" @FunnyGameInfo(WorldInfo.Game).GoalScore - FunnyGameInfo(WorldInfo.Game).EnemiesLeft);
}
DrawHUD定義在UTGFxHudWrapper.uc,每當產生一個新的Function務必檢查一下是否名字已定義過,若定義過則會進行override,若沒定義才是自行定義的Function。
C. Spawn those Enemies:
現在為敵人做一個出生點,先為在敵人出身點產生圖示EnemySpawner.uc:
class EnemySpawnPoint extends AlexGameActor
placeable;
DefaultProperties
{
Begin Object Class=SpriteComponent Name=mySprite
Sprite=Texture2D'EditorResources.S_NavP'
HiddenGame=false;
End Object
Components.Add(mySprite)
}

定義function SpawnEnemy()
function SpawnEnemy()
{
`log("SpawnEnemy function");
}
必須在GameInfo定義出EnemySpawnPoint之敵人數,修改FunnyGameInfo.uc:
宣告變數:
var array<EnemySpawnPoint> enemySpawnPoint;
重寫PostBeginPlay() function:
simulated function PostBeginPlay()
{
local EnemySpawnPoint ESPs;
super.PostBeginPlay();
GoalScore=EnemiesLeft;
foreach DynamicActors(class'EnemySpawnPoint',ESPs)
enemySpawnPoint[enemySpawnPoint.length]=ESPs;
`log("Number of spawners:" @enemySpawnPoint.length);
}
給予敵人數目:
EnemiesLeft=10
Compiler後,放置數個 EnemySpawnPoint actor,log會顯示Point有幾個:
Number of spawners:4
但這只是設定出位置,並還不會自動生敵人因此在增加自訂function ActiveSpawner():
function ActiveSpawner()
{
local int i;
for(i=0; i< enemySpawnPoint.length;i++)
enemySpawnPoint[i].SpawnEnemy();
}
enemySpawnPoint[i].SpawnEnemy();這是EnemySpawnPoint.uc的SpawnEnemy(),因此在修改EnemySpawnPoint.uc的SpawnEnemy() function:
function SpawnEnemy()
{
`log("SpawnEnemy function");
spawn(class'SimpleEnemy',,,Location);
}
敵人會從出生點各生產一隻,但除非放10個出生點,否則無法達成遊戲目標。
Spawn()是在Actor.uc定義出來的:
native noexport final function coerce actor Spawn
(
class<actor> SpawnClass,
optional actor SpawnOwner,
optional name SpawnTag,
optional vector SpawnLocation,
optional rotator SpawnRotation,
optional Actor ActorTemplate,
optional bool bNoCollisionFail
);
Optional 在一般程式語言代表著overloading,Unrealscript用optional來表示所以當使用spawn(class'SimpleEnemy',,,Location);代表只給予 class<actor> vector(SpawnLocation)。
再更改 SpawmEnemy ()
function SpawnEnemy()
{
`log("SpawnEnemy function");
spawn(class'SimpleEnemy',self,,Location);
}
這是為了當敵人死後(SimpleEnemy)必須還要再spawn一個敵人。
修改SimpleEnemy.uc,新增兩行在Destroy()前TakeDamage event:
if(EnemySpawnPoint(Owner) != none)
EnemySpawnPoint(Owner).EnemyDied();
目前SimpleEnemy.uc:
class SimpleEnemy extends AlexGameActor
placeable;
var Pawn PCEnemy;
var float ChaseDistance;
var float AttackDistance;
var float BumpDamage;
event TakeDamage(int DamageAmount,Controller EventInstigator,vector HitLocation,vector Momentum,class<DamageType> DamageType,optional TraceHitInfo HitInfo,optional Actor DamageCauser)
{
if(EventInstigator != none && EventInstigator.PlayerReplicationInfo != none)
WorldInfo.Game.ScoreObjective(EventInstigator.PlayerReplicationInfo,1);

if(EnemySpawnPoint(Owner) != none)
EnemySpawnPoint(Owner).EnemyDied();
Destroy();
}
function Tick(float DeltaTime)
{
local CustomTopDownController player;
local vector NewLocation;

if(PCEnemy == none)
{
foreach LocalPlayerControllers(class'CustomTopDownController',player)
{
if(Player.Pawn != none)
{
PCEnemy= Player.Pawn;
`log("Enemy Target is :" @ PCEnemy);
}
}
}
else if(VSize(Location - PCEnemy.Location) < ChaseDistance)
{
if(VSize(Location-PCEnemy.Location) < AttackDistance)
{
PCEnemy.Bump(self,CollisionComponent,vect(0,0,0));
}
else
{
NewLocation=Location;
NewLocation +=(PCEnemy.Location - Location) * DeltaTime;
SetLocation(NewLocation);
}
}
}
DefaultProperties
{
ChaseDistance=512.0
AttackDistance=96.0
BumpDamage=5.0
bBlockActors=true
bCollideActors=true

Begin Object Class=DynamicLightEnvironmentComponent Name=myLight
bEnabled=true
End Object
Components.Add(myLight)

Begin Object Class=StaticMeshComponent Name=myMesh
StaticMesh=StaticMesh'UN_SimpleMeshes.TexPropCube_Dup'
Materials(0)=Material'EditorMaterials.WidgetMaterial_X'
LightEnvironment=myLight
Scale3D=(X=0.25,Y=0.25,Z=0.25)
End Object
Components.Add(myMesh)
Begin Object Class=CylinderComponent Name=myCylinder
CollisionRadius=32.0
CollisionHeight=64.0
BlockNonZeroExtent=true
BlockZeroExtent=true
BlockActors=true
CollideActors=true
End Object
CollisionComponent=myCylinder
Components.Add(myCylinder)
}
因為增加了EnemySpawnPoint(Owner).EnemyDied();因此要為EnemySpawnPoint增加EnemyDied() function,增加EnemySpawnPoint.uc:
function EnemyDied()
{
SpawnEnemy();
}
Compiler後,敵人死後會在產生新的人:

當然還是有小問題但之後再解決即可(超過10個還是會在生產敵人)。
File:CH6.7z

No comments:

Post a Comment