ActionScript 3.0: Hierarchical State Machine a.k.a HSM


Menyambung dari post sebelumnya ttg masalah “Alarm Behaviour” dari FSM, dan sudah dibahas solusinya yaitu dengan membuat sebuah global state, nah pada akhirnya sy sudah bisa mengimplementasikan Hierarchical State Machine alias HSM alias HFSM alias Hierarchicaly-Nested Finite State Machine alias Hierarchy of Nested Finite State Machine. Bingung dengan banyaknya alias2x tsb? gw aja juga bingung. Tapi sepertinya memang ada perbedaan pendapat dari para developer mengenai implementasi HSM itu sendiri.

Organization Hierarchy
======================================================================
Sekedar warning saja: Source code untuk HSM ini sudah saya revisi lumayan banyak, udah jauh beda dari yg saya post di sini, jadi bagi yang mau pake yang terbaru sebaiknya donlot saja di GitHub. Contoh implementasinya juga sudah disertakan
======================================================================

Sebelum masuk ke HSM yang saya buat, sepertinya memang ada baiknya jika kita sedikit membahas dulu perbedan persepsi yang sy sebut tadi. Jadi, setelah sy mencari2x referensi mengenai HSM ini (sumbernya antara lain: AIGameDev dan AI for Games 2nd Ed), sy menemukan 2 jenis HSM:

1.  Making Modular FSM and Assembling them Hierarchically
HSM2
Beberapa state (state A, B, dan C), yang mempunyai transisi yang sama menuju ke State M dibungkus/dikelompokkan menjadi satu ke dalam State L. Sehingga seperti yang sudah dibahas sebelumnya di FSM–with Global State, state A, B, dan C tidak perlu lagi mendefinisikan transisi menuju state M, karena transisi tsb sudah didefinisikan di state yang membungkus mereka yaitu State L.

Hanya saja pada bentuk ini transasi hanya dimungkinkan terjadi pada state dengan level yang sama. Sebagai contoh: State L dan State M berada di level yang sama, sehingga memungkinkan terjadinya transisi. Begitu juga dengan State A, B, dan C, mereka berada dalam satu level yaitu di bawah State L, shg juga memngkinakn terjadi transisi di antara mereka. Sedangkan State M dengan State A, B, atau C tidak berada dalam satu level, alhasil ya tidak dari State2x tsb tidak memungkinkan adanya transisi.

2. Using Groups of States to Share Transitions
HSM1
Sebenarnya sama seperti bentuk yang sudah dijelaskan pada point 1, hanya perbedaan yang mendasar adalah dimungkinkannya terjadi transisi pada state yang berbeda levelnya. Bisa dilihat di gambar dari State M bisa melakukan transisi ke State C dan dari State B juga bisa melakukan transisi ke State N, sementara parent state dari state A,B,C juga tidak kehilangan fungsinya untuk bisa melakukan transisi ke State M.

Dan menurut AIGameDev, bentuk pertama dinamakan Hierarchy of Nested FSM, yap, dikarenakan bentuknya adalah berupa FSM yang dikelompokkan sehingga menjadi lebih modular, dan tidak akan terkait dengan FSM lain di luar parent/kelompok nya.

Sedangkan bentuk kedua lah yang dinamakan HSM/HFSM. Dan pada buku AI for Games 2nd Ed, bentuk HSM nya juga serupa dengan bentuk yang kedua.

Tapi di sini gw pake istilah HSM buat bentuk yang pertama. Bodo amad lah, toh ga jauh2x amad perbedaanya….
Bush confused a little
“So, which one is better?”

Ya, kalo pendapat saya sih, bentuk yang kedua jelas lebih baik krn lebih komplit, jika bisa melakukan transisi ke semua level state, berarti transisi ke state yang level nya sama jelas bukan masalah lagi. Mau dibawa ke bentuk pertama jelas sangat bisa sekali. Yang artinya jelas bentuk dua merupakan superset dari bentuk pertama.

Tapi, sekali lagi, berhubung ilmu saya juga masih cetek, maka yang akan gw bahas di sini adalah HSM bentuk pertama, di mana transisi hanya bisa dilakukan untuk state2x yang levelnya sama.
trollface
wkwkwkw…. maklum otak mentok di sini doank…. jadi ya harap maklum… Lagian bentuk pertama juga sudah cukup powerful untuk menyelesaikan masalah alarm behaviour tadi…

Dan sebelum masuk ke pembahasan, sebenernya ada referensi yang sangat komplit mengenai HSM ini, tapi jujur aja kepala gw udah ngebul duluan waktu baca2x ini: HSM by Samek. Buat yang mau baca2x ya silakan, dan yang merasa otaknya ga mampu ya mending ga usah dibaca, daripada nanti malah puyeng….

1. Pendahuluan

Seperti yang sudah berkali2x sy kemukakan di atas dan di post sebelumnya, inti dari HSM ini adalah untuk menghindari adanya “Alarm Behaviour” yg menyebabkan terjadinya transisi yang redundant. Sementara secara fungsionalitasnya sendiri hampir tidak ada perbedaan dengan bentuk FSM yang biasa.

Jadi untuk kasus alarm behaviour pada robot cleaning service di post sebelumnya, ketika terjadi kondisi seperti ini:
robot1

selain diselesaikan dengan penggunaan global state, ada baiknya jika mengimplementasikan HSM:


robot2

Bisa dilihat, state2x yang mempunyai transisi yang sama menuju state GetPower dikelompokkan menjadi satu ke dalam state Clean Up.

Berhubung itu gambar sy ambil dari bukunya Ian Millington dan David Funge: AI for Games 2nd ed, di mana mereka mengimplementasikan HSM model bentuk kedua, maka di sana terdapat sebuah ‘history state’ disimbolkan dengan H*, di mana state ini menyimpan informasi mengenai state yang sedang berjalan ketika terjadi transisi ke state GetPower. Misal pada saat berada pada state Search dan tiba2x battere habis dan berpindah ke state GetPower, maka pada saat kondisi battere sudah penuh, dia akan langsung kembali ke state sebelumnya yaitu state Search.

Nah, berhubung saya mengimplementasikan HSM model pertama, maka penggunaan history state jelas tidak dimungkinkan, karena ya itu tadi, transisi hanya terjadi pada state yang berada di level yang sama.

Dan ketika menggunakan bentuk pertama maka ada kemungkinan di mana terjadi kondisi ‘no state’ alias kondisi tanpa state. Misal dari state GetPower kembali ke state CleanUp. State CleanUp sendiri merupakan parent state dari state Search, Head4Trash, dll, memang sih bisa dibilang masih berada dalam suatu state, yaitu state CleanUp, tapi biasanya pada parent state hanya mendefinisikan transisi tidak mendefinisikan behaviour sebuah contex, sehingga ketika masuk ke state CleanUp, jelas sebuah contex/obyek tidak akan melakukan aksi apapun, karena behaviour2 yang konkret berada di child state nya.

Untuk mengakalinya adalah dengan mendefinisikan sebuah initial state pada sebuah parent state, sehingga ketika terjadi transisi ke parent state tersebut, secara otomatis dia akan masuk ke child state yang sudah ditunjuk sebagai initial state.
Kurang lebih kayak begini lah bentuknya:

yang buletan item itulah yang dinamakan initial state. Sama aja sih dengan initial state yang ada di FSM2x biasa, hanya ini berada pada tiap2x parent state.

Sementara jika HSM yang dipake adalah bentuk kedua, maka initial state hanya terletak pada level tertinggi saja, pada super state2x di bawahnya tidak ada, karena ya itu tadi sudah terdapat history state dan juga tiap state dapat ber-transisi ke state manapun, jadinya ya initial state tidak diperlukan.
aqilahmad1081439
into the code…

2. Struktur
Struktur HSM yang mau dibuat juga belum saya cantumkan , jadi akan saya pasang di sini. Kira2x strukturnya ya seperti ini:

HSMStruk

Kalau dilihat memang tidak ada bedanya dengan struktur FSM biasa, ya bisa dibilang begitu karena memang HSM ini sejatinya juga sebuah FSM, hanya saja dibuat sedemikian rupa sehingga mampu mengakomodasi bentuk hierarki.

Hanya saja state2x di HSM dibagi menjadi dua jenis, yaitu:
1. CompositeState: merupakan state yang mempunyai subState lagi
2. AtomicState(di diagram di atas ditulis AtomicClass, itu salah tulis): merupakan state yang sudah tidak punya subState lagi.

Dan tidak seperti pada FSM di mana state2xnya mengimplements interface IState, di HSM state2xnya mengextends class BaseHSMState. Mengapa begitu? ya dikarenakan di dalam state2x HSM diperlukan informasi2x tambahan untuk melakukan operasi, shg penggunaan interface saja tidak cukup.

Di dalam class ini terdapat variabel2x:
1. myEntity: cukup jelas, mrpkn obyek yang memakai state machine ini
2. parentState: menyimpan informasi mengenai parent state. jika sebuah state tidak mempunyai parent, maka parent state ini akan bernilai null
3. initState: seperti yang sudah dibahas sebelumnya, initState akan mendefinisikan state awal ketika masuk ke sebuah superState sehingga tidak akan ada kondisi tanpa state.
4. childStateArray: menyimpan subState2x dari state tersebut
5. level: jelas untuk menyimpan informasi

Sebenernya struktur ini banyak kesalahan dan kelemahan karena baik compositeState dan atomicState semuanya mengextends class BaseHSMState, maka sebuah atomicState pun punya variabel2x dan method2x yang seharusnya tidak ada pada state tersebut. Contohnya adalah variable initState dan childStateArray. Pada atomicState kedua variabel tersebut jelas tidak diperlukan. Serta method addChildState() juga seharusnya tidak ada. Dengan kelemahan2x ini jelas memungkinkan terjadinya illegal assignment, misal kita melakukan aksi addChildState() pada state atomic, atau memasukkan initState pada state atomic, dll.

Kelemahan2x tsb sebenernya jelas bisa diatasi, cm berhubung saya males, jadi ya pake yang ada dulu deh….

Yang penting sih teliti aja ketika membangun state machine nya, shg ga kejadian atomicState bisa jadi compositeState gara2x dimasukkan nilai initState ke dalamnya…. atau menambahkan childState padahal itu adalah sebuah atomicState….

Nah, untuk class StateMachine nya sendiri, intinya sama dengan FSM, tapi jelas isi dalam method2xnya laen (akan dibahas kalo sudah masuk ke kode).

Dan kalo ngomongin masalah hierarchy, biasanya ga jauh2x dari yang namanya Tree, Traversal, dan Recursive. Yap, struktur HSM ini sebenernya adalah sebuah tree, untuk mengakses tiap2x node dalam tree perlu melakukan yang namanya traversal, dan metode yang biasa digunakan untuk melakukan traversal adalah dengan melakukan rekursi. Jadi di dalam state machine nya itu sendiri nantinya akan banyak melakukan operasi rekursi.

Sebagai contoh adalah state2x robot cleaning service yang ada di atas kalau direpresentasikan menjadi sebuah tree:
HSMtree

3. Implementasi
Seperti biasa, sebelum masuk ke coding ada baiknya kita berdoa terlebih dahulu…
Sama seperti di pembahasan FSM, saya akan mengambil contoh kasus seorang miner bernama Bob.

miner02
Hello again!

Dengan state2x seperti di bawah ini:
HSMMiner

Di level paling atas terdapat dua composite state yaitu AtHomeState dan AtMineState, dan keduanya dapat saling melakukan transisi.

Pada level di bawahnya, pada state AtHomeState, terdapat dua atomic state yaitu WatchTV dan Sleep, dengan state WatchTV sebagai initial statenya.

Sedang pada satu level di bawah state AtMine, terdapat 1 atomic state SearchState dan 1 composite state DigState, dengan SearchState sebagai initialStatenya.

Dan satu level di bawah DigState juga terdapat dua state atomic smashingState dan putGoldState

Karena memakai bentuk HSM yang pertama, maka dapat dilihat di sini transisi juga hanya terjadi pada state2x yang levelnya sama.

Sementara untuk transisi2xnya, untuk mempermudah saja, saya hanya membuat transisi terjadi secara random. Cukup lah kalo sebagai contoh saja…

1. Class BaseHSMState
package com.pzuh.hierarchicalstatemachine 
{
 public class BaseHSMState
 {
  protected var parentState:BaseHSMState;
  protected var childStateArray:Array;
  protected var initState:BaseHSMState;
  
  protected var level:int;
  
  protected var myEntity:Object
  
  public function BaseHSMState(entity:Object, initState:BaseHSMState = null) 
  {
   childStateArray = new Array();
   
   myEntity = entity;
   
   this.initState = initState;
  }
  
  public function addChildState(state:BaseHSMState):void
  {
   if (childStateArray.indexOf(state) == -1)
   {
    childStateArray.push(state);
    state.parentState = this;
    state.level = this.level + 1;
   }
   else
   {
    throw new Error("ERROR: Duplicate state detected");
   }
  }
  
  private function removeChildState():void
  {
   for (var i:int = 0; i < childStateArray.length; i++)
   {
    childStateArray[i] = null;
    childStateArray.splice(i, 1);
   }
  }
  
  public function removeSelf():void
  {
   removeChildState();
   
   parentState = null;
   initState = null;
   
   myEntity = null;
  }
  
  public function getParent():BaseHSMState
  {
   return parentState;
  }
  
  public function getInitState():BaseHSMState
  {
   return initState;
  }
  
  public function getLevel():int
  {
   return level;
  }
  
  public function enter():void 
  {
   throw new Error("ERROR: This method must be overriden");
  }
  
  public function update():void 
  {
   throw new Error("ERROR: This method must be overriden");
  }
  
  public function exit():void 
  {
   throw new Error("ERROR: This method must be overriden");
  }  
 }
}

5-11: Karena sudah dijelaskan di bagian sebelumnya, jadi sudah tidak perlu lagi saya jelaskan

13: Constructor, isinya cuma inisiasi childStateArray dan passing nilai parameter ke variabel myEntity dan initState

22: Method addChildState(), method yang dipakai untuk memasukkan state2x yang akan menjadi subState dari state ini.
Yang pertama dilakukan adalah dengan melakukan checking apakah state tersebut sudah ada di dalam array atau belum, jika sudah ada maka akan mengeluarkan pesan error dan operasi tidak dapat dilanjutkan
Selanjutnya adalah memasukkan state dalam parameter tsb kedalam childStateArray dan menjadikan state ini (this) menjadi parentState dari state dlm parameter tsb. Serta memasukkan nilai level ke state dlm parameter tsb dengan nilai level milik state ini (this) ditambahkan dengan 1. Maka jika level parentState adlh 1 maka level childStatenya tentu bernilai 2.
Sepertinya hanya sampai di sini bagian yang penting dari class BaseHSMState, karena method2x selanjutnya hanyalah method untuk cleanUp object dan getter/setter saja

2. Class HierarchicalStateMachine
package com.pzuh.hierarchicalstatemachine
{
 public class HierarchicalStateMachine 
 {
  private var currentState:BaseHSMState; 
  
  public function HierarchicalStateMachine() 
  {
   currentState = null;
  } 
  
  public function update():void
  {
   currentState.update();
   
   updateAllParentState(currentState);
  }
  
  private function updateAllParentState(currentState:BaseHSMState):void
  {
   if (currentState.getParent() != null)
   {
    currentState.getParent().update();
    
    updateAllParentState(currentState.getParent());
   }
  }
  
  public function changeState(targetState:BaseHSMState):void
  {
   if (currentState != null)
   {
    currentState.exit();
    exitParentState(currentState, targetState.getLevel());
    
    currentState = null;
   }
   
   currentState = getLowestLevelState(targetState);
   currentState.enter();
  }
  
  public function exitParentState(currentState:BaseHSMState, level:int):void
  {
   if (currentState.getParent() != null)
   {
    if (currentState.getLevel() > level)
    {
     currentState.getParent().exit();
     
     exitParentState(currentState.getParent(), level);
    }
   }
  }
  
  public function getLowestLevelState(targetState:BaseHSMState):BaseHSMState
  {
   var tempState:BaseHSMState;
   
   if (targetState != null)
   {
    if (targetState.getInitState() != null) 
    {
     targetState.enter();
     tempState = getLowestLevelState(targetState.getInitState());
    }
    else
    {
     tempState = targetState;
    }
   }   
   
   return tempState;
  }
  
  public function getCurrentState():BaseHSMState
  {
   return currentState;
  }
 }
}
7: Seperti di FSM kita definisikan dulu currentState nya

14: Method update(), layaknya pada FSM biasa, di sini juga akan dilakukan update pada currentState nya, hanya saja perlu diingat bahwa semua state di atas currentState juga harus melakukan update karena di sana juga ada proses checking terjadinya transisi.
Oleh sebab itu, dipanggil lah method updateAllParentState() yang akan menghandle proses update untuk semua state di atas currentState

21: Method updateAllParentState, melakukan rekursi untuk mengupdate semua state yang level nya di atas currentState.
Idenya adalah dengan melakukan checking apakah currentState (parameter) mempunyai parentState atau tidak. Jika punya maka parentStete tsb dilakukan update, dan selanjutnya secara rekusif memanggil method updateAllParentState() dengan memasukkan nilai parentState tsb ke dalam parameternya. Begitu seterusnya sampai state paling atas yang tidak punya parent state lagi alias parentState nya bernilai null

31: Method changeState(), sekali lagi mirip dengan pada FSM biasa, hanya saja operasi yang dilakukan berbeda.
Langkah pertama adalah cek apakah currentState bernilai null atau tidak. CurrentState dapat bernilai null ketika awal inisiasi sebuah HSM.
Jika tidak bernilai null, maka yang harus dilakukan pertama adalah dengan memanggil method exit() pada currentState. Selain itu pemanggilan method exit() juga harus dilakukan pada parentState2x nya dengan memanggil method exitParentState(). Nah di sini dilakukan operasi rekursi lagi.

45: Method exitParentState(), method ini mempunyai dua parameter yaitu currentState dan level. Untuk currentState saya rasa cukup jelas. Sementara untuk level adalah level dari target state atau state yang dituju.
Idenya adalah dengan kita melakukan rekursi melakukan exit() pada tiap parentState sampai dengan parentState di mana parentState tersebut memiliki level yang sama dengan level  targetState.

41: Kembali ke method changeState(), jika currentState tadi bernilai null atau proses peng-exit()-an currentState beserta parent2xnya sudah selesai, maka operasi selanjutnya adalah dengan memasukkan state baru sebagai currentState.
Nah di sini terjadi rekursi lagi untuk mendapatkan subState yang paling bawah dari targetState (parameter) dan juga sekaligus melakukan pemanggilan method enter() untuk setiap state2x yang dilewati sebelum sampai ke state terbawah. Operasi ini dipecah lagi menjadi metod tersendiri yaitu method getLowestLevelState().

58: Operasi yang dilakukan oleh method getLowestLevelState() ini adalah dengan melakukan checking apakah targetState yang didefinisikan via parameter tadi mempunyai initialState atau tidak, jika ya maka dia akan memanggil method enter() dan juga kembali melakukan pemanggilan method getLowestLevelState() dengan memasukkan nilai initialState nya ke dalam parameter method tsb.
Jika sudah tercapai sebuah state yang tidak punya initialState maka tinggal mengembalikan/return state tersebut menjadi currentState. Dan selanjutnya juga tidak lupa memanggil method enter() untuk currentState yang baru tsb.
Hmpf, akhirnya bagian yang paling sulit dari HSM sudah terlewati

3. State
Di sini akan saya bagi menjadi dua karena memang statenya ada dua jenis: Composite dan Atomic.
Yang pertama kita bahas dulu contoh state yang Composite:

Class AtHomeState
package compositeState 
{
 import entity.Miner;
 
 import com.pzuh.hierarchicalstatemachine.*;
 
 public class AtHomeState extends BaseHSMState
 {
  public function AtHomeState(entity:Object, initState:BaseHSMState) 
  {
   super(entity,initState); 
  }  
  
  override public function update():void
  {
   trace("Bob: Home sweet home");
   
   var prob:int = Math.ceil(Math.random() * 5);
   
   if (prob == 1)
   {
    myEntity.changeState(Miner.AT_MINE_STATE);
   }
  }
  
  override public function enter():void
  {
   trace("Bob: Entering mah home");
  }
  
  override public function exit():void
  {
   trace("Bob: Leavin' mah tiny old shack");
  }
 }
}
9: Yang perlu digarisbawahi adalah karena ini merupakan compositeState maka perlu memasukkan nilai initialState ke dalam constructor

Method2x selanjutnya sama persis seperti FSM, yaitu enter(), update(), exit(). Jadi ga perlu lagi saya jelaskan

Selanjutnya yaitu state yang Atomic:

Class WatchTVState
package state 
{
 import com.pzuh.hierarchicalstatemachine.BaseHSMState;
 
 import entity.Miner;
 
 public class WatchTVState extends BaseHSMState
 {
  public function WatchTVState (entity:Object) 
  {
   super(entity);
  } 
  
  override public function update():void
  {
   trace("Bob: Watching tha TV");
   
   var prob:int = Math.ceil(Math.random() * 15);
   
   if (prob == 1)
   {
    myEntity.changeState(Miner.SLEEP_STATE);
   }
  }
  
  override public function enter():void
  {
   trace("Bob: turn on tha TV");
  }
  
  override public function exit():void
  {
   trace("Bob: turn off tha TV");
  }
 }
}
9: Perbedaan mendasar antara atomic dan composite yaitu pada state atomic tidak perlu mendefinisikan initialState, jadi di sini nilai initialState dari state2x atomic adalah null

3. Class Miner
Class ini adalah sebuah context, isinya jelas akan menginstatiate object HierarchicalStateMachine serta state2x yang diperlukan. Tidak jauh beda denga FSM, namun ada beberapa bagian yang perlu digarisbawahi yaitu pada saat menginstatiate composite state
package entity 
{
 import state.*;
 import compositeState.*;
 
 import com.pzuh.hierarchicalstatemachine.*;
 
 public class Miner 
 {
  private var myStateMachine:HierarchicalStateMachine;
  
  private var atMineState:BaseHSMState;
  private var atHomeState:BaseHSMState;
  private var digState:BaseHSMState;
  private var searchState:BaseHSMState;
  private var smashRockState:BaseHSMState;
  private var putGoldState:BaseHSMState;
  private var watchTVState:BaseHSMState;
  private var sleepState:BaseHSMState;
  
  public static const AT_MINE_STATE:String = "at_mine_state";
  public static const AT_HOME_STATE:String = "at_home_state";
  public static const DIG_STATE:String = "dig_state";
  public static const SEARCH_STATE:String = "search_state";
  public static const SMASH_ROCK_STATE:String = "smash_rock_state";
  public static const PUT_GOLD_STATE:String = "put_gold_state";
  public static const WATCH_TV_STATE:String = "watch_tv_state";
  public static const SLEEP_STATE:String = "sleep_state";
  
  public function Miner() 
  {
   initState();
   
   myStateMachine = new HierarchicalStateMachine();
   changeState(AT_HOME_STATE);
  }
  
  private function initState():void
  {
   searchState = new SearchState(this);
   smashRockState = new SmashRockState(this);
   putGoldState = new PutGoldState(this); 
   sleepState = new SleepState(this);
   watchTVState = new WatchTVState(this);
   
   digState = new DigState(this, smashRockState); 
   digState.addChildState(smashRockState);
   digState.addChildState(putGoldState);
   
   atMineState = new AtMineState(this, searchState);
   atMineState.addChildState(digState);
   atMineState.addChildState(searchState);   
   
   atHomeState = new AtHomeState(this, watchTVState); 
   atHomeState.addChildState(watchTVState);
   atHomeState.addChildState(sleepState);
  }
  
  public function update():void
  {
   myStateMachine.update();
  }
  
  public function changeState(state:String):void
  {
   var tempState:BaseHSMState;
   
   switch(state)
   {
    case AT_HOME_STATE:
     tempState = atHomeState;
     break;
     
    case AT_MINE_STATE:
     tempState = atMineState;
     break;
     
    case DIG_STATE:
     tempState = digState;
     break;
     
    case SEARCH_STATE:
     tempState = searchState;
     break;
     
    case SMASH_ROCK_STATE:
     tempState = smashRockState;
     break;
     
    case PUT_GOLD_STATE:
     tempState = putGoldState;
     break;
     
    case WATCH_TV_STATE:
     tempState = watchTVState;
     break;
     
    case SLEEP_STATE:
     tempState = sleepState;
     break;
   }
   
   myStateMachine.changeState(tempState);
  }
  
  public function getStateMachine():HierarchicalStateMachine
  {
   return myStateMachine;
  }
 }
}
38: Untuk menginstatiate atomic state, sama saja dengan di FSM. Sedangkan untuk composite state, kita perlu memasukkan state sebagai initialState ke dalam parameter, serta memasukkan subState2x dari state tsb dengan cara memanggil method addChildState().
Selebihnya, kurang lebih sama seperti FSM biasa

4. Class Main
package 
{
 import entity.Miner;
 
 import flash.display.*;
 import flash.events.*;
 import flash.utils.*;
 
 public class Main extends Sprite 
 {
  private var myMiner:Miner;
  
  private var updateTimer:Timer;
  
  public function Main():void 
  {
   if (stage) init();
   else addEventListener(Event.ADDED_TO_STAGE, init);
  }
  
  private function init(e:Event = null):void 
  {
   removeEventListener(Event.ADDED_TO_STAGE, init);
   // entry point
   
   myMiner = new Miner();
   
   updateTimer = new Timer(1000, 0);
   updateTimer.addEventListener(TimerEvent.TIMER, updateTimerHandler, false, 0, true);
   updateTimer.start();
  }
  
  private function updateTimerHandler(event:TimerEvent):void
  {
   myMiner.update();
  }
 } 
}
Harusnya sudah sangat terang benderang fungsi dari kelas ini, hanya menginstatiate object Miner tadi, dan kemudian juga memanggil method update() dari Miner di main loop nya, yang kebetulan method update() juga namanya….
funny-cat-run
RunTime!
4. Tes
HSMRun
Bisa dilihat di sini, ketika currentState nya adalah searching, selain dia melakukan update terhadap currentState nya dengan mengeluarkan pesan “Searching, searching, searching”, dia juga melakukan update terhadap parent dari currentState searching, yaitu state AtMine, dengan menegluarkan pesan “Mining, mining, mining”.

Begitu juga ketika keluar dari state pickUpGold menuju ke state AtHome, dia akan melakukan exit() terhadap currentState: pickUpGold, parentStatenya: DigState, dan juga parentState dari DigState: AtMine. Dapat dilihat dari pesan yang dikeluarkan yaitu: “No gold anymore”, “Stop Digging”, dan kemudian “Finally I can see the sun”. Baru kemudian masuk ke state AtHome

Fuh, sekian pembahasan HSM yang ternyata lumayan panjang ini….

Untuk source codenya bisa didonlot di bawah ini

5. Source Code

6. Referensi

Freelance 2D game artist, occassional game developers, lazy blogger, and professional procrasctinator

0 comments: