Pengertian Finite State Machine ( FSM )
Finite state machine adalah suatu perangkat
atau model perangkat yang memiliki sejumlah state dan pada satu waktu dapat
berada dalam salah satu state tersebut. Dia dapat memproses input dan
menghasilkan transisi dari state satu ke state lain atau menghasilkan output
berupa aksi.
Finite State Machines (FSM) adalah sebuah
metodologi perancangan sistem kontrol yang menggambarkan tingkah laku atau
prinsip kerja sistem dengan menggunakan tiga hal berikut: State (Keadaan),
Event (kejadian) dan Action (aksi). Pada satu saat dalam periode
waktu yang cukup signifikan, sistem akan berada pada salah satu state yang
aktif. Sistem dapat beralih atau bertransisi menuju state lain jika mendapatkan
masukan atau event tertentu, baik yang berasal dari perangkat luar atau
komponen dalam sistemnya itu sendiri (misal interupsi timer). Transisi keadaan
ini umumnya juga disertai oleh aksi yang dilakukan oleh sistem ketika
menanggapi masukan yang terjadi. Aksi yang dilakukan tersebut dapat berupa aksi
yang sederhana atau melibatkan rangkaian proses yang relative kompleks.
Berdasarkan sifatnya, metode FSM ini sangat cocok
digunakan sebagai basis perancangan perangkat lunak pengendalian yang bersifat
reaktif dan real time. Salah satu keuntungan nyata penggunaan FSM adalah
kemampuannya dalam mendekomposisi aplikasi yang relative besar dengan hanya
menggunakan sejumlah kecil item state. Selain untuk bidang kontrol, Penggunaan
metode ini pada kenyataannya juga umum digunakan sebagai basis untuk perancangan
protokol-protokol komunikasi, perancangan perangkat lunak game, aplikasi WEB
dan sebagainya.
Dalam bahasa pemrograman prosedural seperti bahasa
C, FSM ini umumnya direalisasikan dengan menggunakan statemen kontrol switch
case atau/dan if..then. Dengan menggunakan statemen-statemen kontrol ini,
aliran program secara praktis akan mudah dipahami dan dilacak jika terjadi
kesalahan logika.
DIAGRAM
KEADAAN
Diagram keadaan pada dasarnya merupakan salah satu bentuk representasi dari FSM. Diagram ini secara visual menggambarkan tingkah laku yang dimiliki oleh sistem kontrol yang kompleks kedalam bentuk yang lebih sederhana dan relative mudah dipahami.
Dalam diagram ini, state-state yang terdapat pada
sebuah sistem digambarkan sebagai lingkaran yang diberi label unik, sedangkan
transisi state yang diakibatkan oleh event tertentu direpresentasikan sebagai
anak panah yang berasal dari state yang ditinggalkan menuju state yang aktif.
Setiap transisi yang terjadi umumnya juga diikuti oleh aksi yang dilakukan oleh
sistem yang dirancang. Secara praktis setiap diagram state yang dirancang akan
selalu memiliki sebuah transisi awal (inisial) yang menuju salah satu state
sejak sistem kontrol tersebut mulai dihidupkan. Gambar berikut memperlihatkan
contoh penggambaran diagram state:
Diagram tersebut memperlihatkan FSM dengan dua buah
state (S0 dan S1) dan dua buah input (e1 dan e2) serta dua buah aksi (a1 dan
a2) output yang berbeda : seperti terlihat pada gambar, ketika sistem mulai
dihidupkan, sistem akan bertransisi menuju state0, pada keadaan ini sistem akan
menghasilkan Action2 jika terjadi masukan Event2, sedangkan jika terjadi Event1
maka Action1 akan dieksekusi kemudian sistem selanjutnya bertransisi ke keadaan
State1 dan seterusnya.
Berikut adalah potongan program untuk gambar
tersebut
//program gambar diatas
….
#define TRUE 1
enum {state0,state1}
……
unsigned char state;
void Action1(void);
void Action2(void);
….
#define TRUE 1
enum {state0,state1}
……
unsigned char state;
void Action1(void);
void Action2(void);
…..
main()
{
//init
………
While(TRUE)
{
switch(state)
{
case state0: If(input==Event2)
{Action2();
state=state0;}
break;
case state1: If(input==Event1)
{Action1()
state=State1;}
break;
}
}
…….
}
main()
{
//init
………
While(TRUE)
{
switch(state)
{
case state0: If(input==Event2)
{Action2();
state=state0;}
break;
case state1: If(input==Event1)
{Action1()
state=State1;}
break;
}
}
…….
}
Hierarchy Finite Machine Strategi Menyerang
Hierarchy Finite State Machine untuk strategi menyerang dibuat berdasarkan skenario strategi. Secara garis besar hirarki yang dimaksud terbagi menjadi
dua bagian, yaitu hirarki untuk NPC Scout dan hirarki untuk NPC Sniper,
seperti dijelaskan pada Gambar 7.
Top Level Finite State Machine
Top level finite state machine untuk gerak menghindar pada penelitian ini merupakan gabungan antara FSM NPC Angler dan FSM NPC Support,
sebagaimana di jelaskan pada gambar 8.
Contoh pengunaan finite state machine cukup banyak.
Contoh yang gampang adalah demo aplikasi Animasi 2D dengan Direct3D. Di demo
tersebut karakter spiderman dapat berjalan, memukul, menendang dan sebagainya.
Berjalan, memukul dan menendang adalah beberapa state dimana spiderman dapat
berada. State berjalan, memukul dan menendang di atas menghasilkan output
berupa aksi yakni animasi berjalan, memukul dan animasi menendang.
Di artikel ini kita akan memperbaiki sistem
manajemen state demo Animasi 2D dengan Direct3D menggunakan object-oriented
finite state machine. Pada object-oriented finite state machine, state kita
modelkan sebagai obyek yang dapat mengeksekusi aksi. Aksi yang dieksekusi dapat
berupa aksi murni atau aksi yang menyebabkan terjadinya transisi state menjadi
state lain.
Pada demo diatas, state spiderman kita simpan dalam
variabel. Perhatikan potongan source code demonya.
procedure T2DEngine.Draw;
begin
FBackground.Draw;
FSprites.BeginDraw;
//beri delay
agar animasi
//tidak
terlalu cepat
if
delay>2 then
begin
case anim
of
animIdle:begin
//khusus animasi idle buat lebih slow
//soallnya framenya sedikit
if delay_idle>2 then
begin
Spiderman.Texture:=SpidermanIdleTexture[animIndex];
Spiderman.Y:=200;
inc(animIndex);
if animIndex>9 then
animIndex:=0;
delay_idle:=0;
end;
inc(delay_idle);
end;
animWalk:begin
Spiderman.Texture:=SpidermanWalk[animIndex];
Spiderman.Y:=205;
case dirType of
dirLeft:Spiderman.X:=Spiderman.X-6;
dirRight:Spiderman.X:=Spiderman.X+6;
end;
inc(animIndex);
if animIndex>11 then
begin
//animasi punch selesai
//kembalikan Spiderman ke animasi idle
anim:=animIdle;
animIndex:=0;
end;
end;
animPunch:begin
Spiderman.Texture:=SpidermanPunchTexture[animIndex];
Spiderman.Y:=202;
inc(animIndex);
if animIndex>5 then
begin
//animasi punch selesai
//kembalikan Spiderman ke animasi idle
anim:=animIdle;
animIndex:=0;
end;
end;
animHeavyPunch:begin
Spiderman.Texture:=SpidermanHeavyPunchTexture[animIndex];
Spiderman.Y:=175;
inc(animIndex);
if animIndex>9 then
begin
//animasi heavy punch selesai
//kembalikan Spiderman
ke animasi idle
anim:=animIdle;
animIndex:=0;
end;
end;
animKick:begin
Spiderman.Texture:=SpidermanKickTexture[animIndex];
Spiderman.Y:=171;
inc(animIndex);
if animIndex>8 then
begin
anim:=animIdle;
animIndex:=0;
end;
end;
end;
delay:=0;
end;
Spiderman.Draw;
FSprites.EndDraw;
inc(delay);
end;
Kita menggunakan case..of untuk menghasilkan output
masing-masing state. Jika jumlah statenya tidak terlalu banyak model seperti
ini tidak buruk, tapi bayangkan jika jumlah statenya bertambah, misalnya 20
atau 30 state, maka kode case of ini akan semakin panjang dan dapat dipastikan
semakin besar peluang kode menjadi ruwet dan susah dirawat, susah didebug,
kontrol alir program menjadi ruwet dan lain-lain.
Desain dan Implementasi Finite State Machine
Berorientasi Objek
Bayangkan situasi berikut: kita memiliki robot
cerdas berbentuk anjing sebut saja Dog. Dog dapat melakukan atraksi seperti
bermain bola, berguling-guling, makan dan tidur. Tiap atraksi dijalankan dengan
menggunakan cartridge-cartridge berisi program yang dicolok ke sebuah slot
dalam body Dog.
Cartridge ini mewakili state. Tanpa cartdridge, Dog
hanyalah sebuah patung yang tidak bisa apa-apa. Kemampuan utama selain
memproses cartridge dan menjalankan aksi yang diprogram di dalam cartridge
(memproses input dan menghasilkan output berupa aksi) adalah kemampuan untuk
mengganti cartridge dengan cartridge lain (menghasilkan transisi state ke state
lain). Doog juga memiliki sensor untuk memantau level baterai. Jika level
baterai turun, Dog akan mengubah cartridge yang sedang berada dalam slot.
Object-oriented finite state machine yang kita buat
akan mirip dengan sistem cartridge di atas. Obyek state (TAIAction) dapat
berisi perintah untuk melakukan aksi atau melakukan transisi ke obyek state
lain. Untuk mengeksekusi perintah, obyek state akan dilengkapi
metode ExecuteAction. Karena obyek state adalah obyek dasar, apa yang
dikerjakan didalam ExecuteAction sepenuhnya menjadi tanggung jawab kelas
turunan obyek state. Oleh karena itu ExecuteAction harus berupa metode virtual
agar dapat di-override oleh kelas turunannya.
State juga memiliki metode yang akan dieksekusi
ketika akan terjadi perubahan state yakni ketika hendak memasukan state
yakni EnterAction dan ketika hendak keluar dari state
yakni ExitAction. Kegunaan EnterAction dan ExitAction adalah sebagai
berikut: bayangkan anda sedang membuat program game seperti WarCraft. Anda
ingin karakter prajurit yang sedang berperang akan meneriakkan "Hore"
ketika terjadi perubahan dari state berperang menjadi state stand by ketika
musuh yang dihadapi telah kalah. Kode aksi teriakan "Hore" bisa
diletakkan di dalam ExitAction state berperang atau EnterAction state stand by.
Jadi framework kita akan menjadi seperti dibawah
ini:
TAIAction=class(TObject)
private
public
procedure
EnterAction(entity:TAIEntity);virtual;
procedure
ExecuteAction(entity:TAIEntity);virtual;
procedure
ExitAction(entity:TAIEntity);virtual;
end;
State-state dieksekusi oleh karakter dalam game yang
selanjutnya kita sebut entity (TAIEntity). Entity menyimpan state dimana dia
sedang berada. Jika state ini diubah, maka ExitAction state lama dieksekusi,
kemudian state yang baru disimpan dan metode EnterAction state baru dieksekusi.
Entity juga memiliki property untuk menyimpan state
sebelumnya. Kita membutuhkannya untuk contoh berikut: di game Warcraft, pekerja
yang sedang menambang emas, ketika emas yang diambil sudah cukup akan
meletakkan emas kedalam lumbung, kemudian akan kembali lagi ke tempat
penambangan untuk melanjutkan pekerjaan menambang. Di sini terjadi transisi
state dari state menambang menjadi state meletakkan emas. Setelah state
meletakkan emas dieksekusi, state dikembalikan ke state sebelumnya (state
menambang) dan mengeksekusi state tersebut.
Dengan memiliki kemampuan menyimpan state
sebelumnya, proses reverse ke state sebelumnya jadi mudah.
type
TAIAction=class;
TAIEntity=class(TObject)
private
FPreviousAction: TAIAction;
FCurrentAction: TAIAction;
procedure
SetCurrentAction(const Value: TAIAction);
procedure
SetPreviousAction(const Value: TAIAction);
public
procedure
Update;
published
property
CurrentAction:TAIAction read FCurrentAction write SetCurrentAction;
property
PreviousAction:TAIAction read FPreviousAction write SetPreviousAction;
end;
Entity memiliki metode Update. Di metode ini,
ExecuteAction state akan dieksekusi. Metode ini dimaksudkan untuk eksekusi
ExcuteAction yang aman untuk menghindari access violation karena CurrentAction
mungkin saja nil. Ok berikut ini adalah implementasi lengkap kodenya
{-------------------------------
Implementasi Finite State Machine
menggunakan object-oriented
programming
--------------------------------
-------------------------------}
unit u_ai;
interface
uses classes,sysutils;
type
TAIAction=class;
TAIEntity=class(TObject)
private
FPreviousAction: TAIAction;
FCurrentAction: TAIAction;
procedure
SetCurrentAction(const Value: TAIAction);
procedure
SetPreviousAction(const Value: TAIAction);
public
procedure
Update;
published
property
CurrentAction:TAIAction read FCurrentAction write SetCurrentAction;
property
PreviousAction:TAIAction read FPreviousAction write SetPreviousAction;
end;
TAIAction=class(TObject)
private
public
procedure
EnterAction(entity:TAIEntity);virtual;
procedure
ExecuteAction(entity:TAIEntity);virtual;
procedure
ExitAction(entity:TAIEntity);virtual;
end;
implementation
{ TAIEntity }
procedure TAIEntity.SetCurrentAction(const Value:
TAIAction);
begin
//run
ExitAction aksi lama sebelum berpindah ke aksi baru
if
FCurrentAction<>nil then
FCurrentAction.ExitAction(self);
//set aksi
baru
FCurrentAction := Value;
//run
EnterAction aksi baru
if
FCurrentAction<>nil then
FCurrentAction.EnterAction(self);
end;
procedure TAIEntity.SetPreviousAction(const Value:
TAIAction);
begin
FPreviousAction := Value;
end;
procedure TAIEntity.Update;
begin
if FCurrentAction<>nil
then
FCurrentAction.ExecuteAction(self);
end;
{ TAIAction }
procedure TAIAction.EnterAction(entity: TAIEntity);
begin
//do nothing
end;
procedure TAIAction.ExecuteAction(entity:
TAIEntity);
begin
//do nothing
end;
procedure TAIAction.ExitAction(entity: TAIEntity);
begin
//do nothing
end;
end.
TAIAction defaultnya tidak melakukan apa-apa. Untuk
menggunakan framework ini, aplikasi harus menurunkan TAIEntity dan TAIAction
dan mendefinisikan aksi yang harus dijalankan. Kembali ke tujuan utama kita
yakni memperbaiki demo Animasi 2D dengan Direct3D.
Kita belum menambahkan aksi lain untuk demo ini,
jadi aksi spiderman hanya aksi berjalan, memukul, memukul dengan keras,
menendang dan idle/stand by. Tidak berubah dari demo aslinya.
TBaseAction
Semua aksi akan diturunkan dari TBaseAction yang
merupakan turunan TAIAction. Aksi yang dikerjakan pada ExecuteAction
TBaseAction adalah mengupdate indeks animasi ke frame animasi berikutnya.
EnterAction kita isi dengan kode untuk mereset animasi ke frame animasi pertama.
Semua aksi melakukan hal yang sama oleh karena itu kita turunkan dari
TBaseAction. TBaseAction akan dilengkapi dengan metode virtual Load yang
berguna untuk melakukan loading frame-frame animasi. Karena jumlah frame
animasi tiap aksi berbeda, Load sesungguhnya akan didelegasi ke turunan
TBaseAction. TBaseAction akan menyimpan instance texture collection yang akan
dipergunakan untuk membuat instance TTexture.
TBaseAction=class(TAIAction)
private
FMaxAnim:
integer;
FTextures:
TTextureCollection;
procedure
SetMaxAnim(const Value: integer);
procedure
SetTextures(const Value: TTextureCollection);
public
procedure
EnterAction(aientity:TAIEntity);override;
procedure
ExecuteAction(aientity:TAIEntity);override;
procedure
Load(const images_dir:string);virtual;
published
property
Textures:TTextureCollection read FTextures write SetTextures;
property
MaxAnim:integer read FMaxAnim write SetMaxAnim;
end;
Berikut ini adalah implementasinya. Fungsi Load
adalah fungsi virtual yang tidak melakukan apa-apa.
{TBaseAction}
procedure TBaseAction.Load;
begin
end;
Kita override EnterAction untuk memastikan semua
animasi dimulai dari indeks ke-0 untuk semua aksi. Perhatikkan bahwa kita
melakukan typecast TAIEntity ke T2DEntity. Typecast ini aman dengan asumsi
bahwa, entity spiderman akan diturunkan dari T2DEntity. Kita akan membahas
kelas T2DEntity segera.
procedure TBaseAction.EnterAction(aientity:
TAIEntity);
var ent:T2DEntity;
begin
ent:=T2DEntity(aiEntity);
ent.AnimIndex:=0;
end;
ExecuteAction kita isi dengan kode yang akan
menambah indeks animasi. Jika indeks animasi ini melebih jumlah total frame
animasi (MaxAnim) berarti animasi telah selesai dan animasi spiderman kita
kembalikan ke state sebelumnya (PreviousAction). Harap diperhatikan karena idle
adalah default state spiderman, maka PreviousAction ini kita isi dengan state
idle dan selama demo berlangsung PreviousAction tidak pernah berubah, dengan
cara ini tiap kali animasi (misal animasi memukul) selesai maka spiderman akan
otomatis masuk ke state idle.
ExecuteAction milik TBaseAction harus dijalankan
terakhir oleh karena itu kelas-kelas turunan TBaseAction yang meng-override
ExecuteAction harus memanggil ExecuteAction TBaseAction ini paling akhir
setelah mengupdate animasi.
procedure
TBaseAction.ExecuteAction(aientity:TAIEntity);
var ent:T2DEntity;
begin
ent:=T2DEntity(aiEntity);
ent.AnimIndex:=ent.AnimIndex+1;
if
ent.AnimIndex>MaxAnim then
begin
ent.CurrentAction:=ent.PreviousAction;
end;
end;
TIdleAction
Aksi idle akan kita enkapsulasi dalam kelas
TIdleAction. Dalam TIdleAction, akan disimpan sebuah variabel array TTexture
(SpidermanIdleTexture) berisi animasi idle spiderman. Metode Load, EnterAction
dan ExecuteAction kita override.
TIdleAction=class(TBaseAction)
private
SpidermanIdleTexture:TSpidermanIdleTexture;
FDelay:
integer;
procedure
SetDelay(const Value: integer);
public
procedure
EnterAction(aientity:TAIEntity);override;
procedure
ExecuteAction(aientity:TAIEntity);override;
procedure
Load(const images_dir:string);override;
published
property
Delay:integer read FDelay write SetDelay;
end;
Berikut ini adalah implementasinya
{TIdleAction}
procedure TIdleAction.EnterAction(aientity:
TAIEntity);
begin
inherited;
Fdelay:=0;
end;
procedure
TIdleAction.ExecuteAction(aientity:TAIEntity);
var ent:T2DEntity;
begin
if
FDelay>3 then
begin
ent:=T2DEntity(aiEntity);
ent.Sprite.Texture:=SpidermanIdleTexture[ent.AnimIndex];
ent.Sprite.Y:=200;
FDelay:=0;
inherited;
end;
inc(FDelay);
end;
procedure TIdleAction.Load(const images_dir:string);
var i:integer;
begin
for i:=0 to
9 do
begin
SpidermanIdleTexture[i]:=FTextures.Add as TTexture;
SpidermanIdleTexture[i].ColorKey:=0;
SpidermanIdleTexture[i].LoadFromFile(images_dir+
'standbystandby'+inttostr(i)+'.png');
end;
MaxAnim:=9;
end;
procedure TIdleAction.SetDelay(const Value:
integer);
begin
FDelay :=
Value;
end;
Di kelas TIdleAction, kita override lagi
EnterAction. Perhatikan bahwa animasi idle ini menggunakan delay tambahan
selain delay animasi keseluruhan. Tanpa delay tambahan ini, animasi idle masih
terlalu cepat sehingga terlihat jelek. Kita perlu mengoverride untuk memastikan
delay ini direset ke 0, tiap kali spderman masuk state idle.
ExecuteAction kita override. Disini kita tambahkan
pengecekan delay, jika delay lebih dari 2, animasi kita update dengan frame
animasi berikutnya, delay kita kembalikan lagi ke 0 dan terakhir kita tambahkan
pemanggilan ExecuteAction ansestor. Pemanggilan ExecuteAction ancestor (TBaseAction)
ini penting agar AnimIndex diupdate, tanpa itu kita tidak akan melihat animasi
idle spiderman.
Metode Load kita override dengan kode yang berguna
untuk loading gambar-gambar animasi idle. Karena animasi idle hanya terdiri
atas 10 frame animasi (dari 0-9) kita harus mengeset MaxAnim dengan 9. Jika
kita lupa, animasi tidak akan terlihat.
Tekstur-tekstur yang tersimpan di
SpidermanIdleTexture, tidak difree karena FTextures otomatis akan membuang
memorinya ketika FTextures di free, tapi jika diinginkan, tekstur-tekstur
tersebut dapat saja di free didalam TIdleAction.
TPunchAction
Aksi memukul dijalankan oleh TPunchAction
TPunchAction=class(TBaseAction)
private
SpidermanPunchTexture:TSpidermanPunchTexture;
public
procedure
ExecuteAction(aientity:TAIEntity);override;
procedure
Load(const images_dir:string);override;
end;
Kita hanya mengoverride ExecuteAction dan Load,
karena kita tidak membutuhkan aksi tertentu pada saat EnterAction maupun
ExitAction
{TPunchAction}
procedure
TPunchAction.ExecuteAction(aientity:TAIEntity);
var ent:T2DEntity;
begin
ent:=T2DEntity(aiEntity);
ent.Sprite.Texture:=SpidermanPunchTexture[ent.AnimIndex];
ent.Sprite.Y:=202;
inherited;
end;
procedure TPunchAction.Load(const images_dir:string);
var i:integer;
begin
for i:=0 to
5 do
begin
SpidermanPunchTexture[i]:=FTextures.Add as TTexture;
SpidermanPunchTexture[i].ColorKey:=0;
SpidermanPunchTexture[i].LoadFromFile(images_dir+
'punchpunch'+inttostr(i)+'.png');
end;
MaxAnim:=5;
end;
Terlihat bahwa kodenya secara umum tidak terlalu
jauh beda dengan kode implementasi TIdleAction, kecuali bahwa animasi tidak
perlu didelay lagi.
THeavyPunchAction
Heavy punch adalah aksi memukul spiderman dengan
tenaga penuh, dienkapsulasi dalam kelas
THeavyPunchAction
THeavyPunchAction=class(TBaseAction)
private
SpidermanHeavyPunchTexture:TSpidermanHeavyPunchTexture;
public
procedure
ExecuteAction(aientity:TAIEntity);override;
procedure
Load(const images_dir:string);override;
end;
Implementasi THeavyPunchAction adalah sebagai
berikut:
{THeavyPunchAction}
procedure
THeavyPunchAction.ExecuteAction(aientity:TAIEntity);
var ent:T2DEntity;
begin
ent:=T2DEntity(aiEntity);
ent.Sprite.Texture:=SpidermanHeavyPunchTexture[ent.AnimIndex];
ent.Sprite.Y:=175;
inherited;
end;
procedure THeavyPunchAction.Load(const images_dir:
string);
var i:integer;
begin
for i:=0 to
9 do
begin
SpidermanHeavyPunchTexture[i]:=FTextures.Add as TTexture;
SpidermanHeavyPunchTexture[i].ColorKey:=0;
SpidermanHeavyPunchTexture[i].LoadFromFile(images_dir+
'heavy_punchhpunch'+inttostr(i)+'.png');
end;
MaxAnim:=9;
end;
TKickAction
Aksi menendang dijalankan dalam TKickAction, kodenya
mirip dengan TPunchAction
TKickAction=class(TBaseAction)
private
SpidermanKickTexture:TSpidermanKickTexture;
public
procedure
ExecuteAction(aientity:TAIEntity);override;
procedure
Load(const images_dir:string);override;
end;
.....
{ TKickAction }
procedure TKickAction.ExecuteAction(aientity:
TAIEntity);
var ent:T2DEntity;
begin
ent:=T2DEntity(aiEntity);
ent.Sprite.Texture:=SpidermanKickTexture[ent.animIndex];
ent.Sprite.Y:=171;
inherited;
end;
procedure TKickAction.Load(const images_dir:string);
var i:integer;
begin
for i:=0 to
8 do
begin
SpidermanKickTexture[i]:=FTextures.Add as TTexture;
SpidermanKickTexture[i].ColorKey:=0;
SpidermanKickTexture[i].LoadFromFile(images_dir+
'kickkick'+inttostr(i)+'.png');
end;
MaxAnim:=8;
end;
TWalkAction
Aksi berjalan dienkapsulasi oleh TWalkAction
TWalkAction=class(TBaseAction)
private
SpidermanWalkTexture:TSpidermanWalkTexture;
public
procedure
ExecuteAction(aientity:TAIEntity);override;
procedure Load(const
images_dir:string);override;
end;
Implementasi TWalkAction adalah sebagai berikut.
Pada ExecuteAction selain menampilkan animasi berjalan, kita juga harus
memindahkan posisi spiderman berdasarkan arah mana spirderman menghadap. Jika
menghadap ke kiri posisi x kita kurangi dan jika menghadap kanan posisi x kita
tambah.
{TWalkAction}
procedure
TWalkAction.ExecuteAction(aientity:TAIEntity);
var ent:T2DEntity;
begin
ent:=T2DEntity(aiEntity);
ent.Sprite.Texture:=SpidermanWalkTexture[ent.AnimIndex];
ent.Sprite.Y:=205;
case
ent.Direction of
dirLeft:ent.Sprite.X:=ent.Sprite.X-6;
dirRight:ent.Sprite.X:=ent.Sprite.X+6;
end;
inherited;
end;
procedure TWalkAction.Load(const images_dir:
string);
var i:integer;
begin
for i:=0 to
11 do
begin
SpidermanWalkTexture[i]:=FTextures.Add as TTexture;
SpidermanWalkTexture[i].ColorKey:=0;
SpidermanWalkTexture[i].LoadFromFile(images_dir+'walkwalk'+inttostr(i)+'.png');
end;
MaxAnim:=11;
end;
TChangeDirectionAction
Aksi mengubah arah kiri/kanan spiderman dijalankan
TChangeDirectionAction. Karena TChangeDirection tidak membutuhkan animasi,
metode Load tidak perlu kita overide.
TChangeDirectionAction=class(TBaseAction)
public
procedure
ExecuteAction(aientity:TAIEntity);override;
end;
Implementasi TChangeDirectionAction terlihat
dipotongan kode berikut. Penjelasan bagaimana membuat pencerminan sprite yang
menghadap kearah berbeda telah kita bahas di artikel Animasi 2D dengan Direct3D
Part 2. Yang harus diperhatikan bahwa kita tidak perlu memanggil ExecuteAction
ancestor karena aksi memindahkan arah tidak memerlukan animasi.
{ TChangeDirectionAction }
procedure
TChangeDirectionAction.ExecuteAction(aientity: TAIEntity);
var ent:T2DEntity;
begin
ent:=T2DEntity(aiEntity);
case
ent.Direction of
dirLeft:begin
ent.Sprite.scaling:=SetD3DXVector2(-1,1);
ent.Sprite.X:=ent.Sprite.X+ent.Sprite.Texture.Width;
end;
dirRight:begin
ent.Sprite.scaling:=SetD3DXVector2(1,1);
ent.Sprite.X:=ent.Sprite.X-ent.Sprite.Texture.Width;
end;
end;
inherited;
end;
TWalkOrChangeDirAction
Aksi berjalan atau mengubah arah dijalankan
menggunakan penekanan tombol yang sama yakni tombol panah kiri/kanan. Jika pada
saat tombol panah kiri ditekan, spiderman menghadap ke kanan, maka aksi yang
dijalankan adalah mengubah arah spiderman ke kiri. Jika spiderman sudah
menghadap ke kiri, aksi yang dijalankan adalah berjalan ke kiri. Hal yang sama
terjadi pada saat penekanan tombol panah kanan. Dengan cara ini, spiderman
selalu akan berjalan maju.
Aksi menentukan aksi berjalan atau mengubah arah
dienkapsulasi dalam TWalkOrChangeDirAction. Kelas ini adalah contoh
implementasi state yang menghasilkan output berupa transisi ke state yang lain.
TWalkOrChangeDirAction=class(TBaseAction)
private
FChangeDir: TChangeDirectionAction;
FWalk:
TWalkAction;
FNewDirection: TDirectionType;
procedure
SetChangeDir(const Value: TChangeDirectionAction);
procedure
SetWalk(const Value: TWalkAction);
procedure
SetNewDirection(const Value: TDirectionType);
public
procedure
ExecuteAction(aientity:TAIEntity);override;
published
property
NewDirection:TDirectionType read FNewDirection write SetNewDirection;
property
Walk:TWalkAction read FWalk write SetWalk;
property
ChangeDir:TChangeDirectionAction read FChangeDir write SetChangeDir;
end;
Implementasinya adalah sebagai berikut. Perhatikan
bahwa Load tidak perlu di override karena tidak relevan, karena aksi ini tidak
menghasilkan output berupa animasi. Kita perlu menambahkan property Walk dan
ChangeDir untuk menyimpan instance kelas TWalkAction dan
TChangeDirectionAction, serta NewDirection yang akan berisi arah menghadap
spiderman.
{ TWalkOrChangeDirAction }
procedure
TWalkOrChangeDirAction.ExecuteAction(aientity: TAIEntity);
var ent:T2DEntity;
begin
ent:=T2DEntity(aiEntity);
if
ent.Direction<>FNewDirection then
begin
ent.Direction:=FNewDirection;
ent.CurrentAction:=FChangeDir;
end else
begin
ent.CurrentAction:=FWalk;
end;
end
Tidak ada komentar:
Posting Komentar