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) } |
function EnemyDied()
{
SpawnEnemy();
}
Compiler後,敵人死後會在產生新的人:
當然還是有小問題但之後再解決即可(超過10個還是會在生產敵人)。
File:CH6.7z
No comments:
Post a Comment