1、簡介 java
利用有限狀態機來控制對象的行爲,其原理就是利用多態,經常咱們本身寫代碼,須要很大篇幅,萬一須要再加一個或者幾個狀態,那麼咱們本身維護時就會很麻煩,SMC這個工具能夠幫助咱們解決這個問題。c++
使用這個工具以前咱們須要jdk環境,最新版的SMC支持jdk1.7,以前版本的SMC支持jdk1.6,要想中間沒有問題,咱們必定要將電腦上的jdk版本與SMC的jdk版本對應起來。框架
下載地址:http://sourceforge.net/projects/smc/files/函數
SMC能夠經過一個配置文件,生成有限狀態機所需的全部狀態類以及狀態機類,同時還包括了全部的狀態間的轉換邏輯。工具
SMC支持多種開發語言:C、JavaScript、Python、C++、Lua、Ruby、C#、Objective-C、Scala、Groovy、Perl、TCL、Java、PHP、VB.net,而咱們所須要作的惟一的工做就是編寫擁有這些狀態的主體類。this
2、編寫smc文件spa
首先寫一個sm的文件 Hero.sm:.net
1 // entity class
2 %class Hero 3
4 // entity class header
5 %header Hero.h 6
7 // inital state
8 %start HeroMap::STOP 9
10 // entity state map
11 %map HeroMap 12 %%
13 STOP 14 Entry { 15 stop(); 16 } 17 Exit { 18 exit(); 19 } 20 { 21 walk WALK {} 22 } 23
24 WALK 25 Entry { 26 walk(); 27 } 28 Exit { 29 exit(); 30 } 31 { 32 stop STOP {} 33 turn TURN {} 34 } 35
36 TURN 37 Entry { 38 turn(); 39 } 40 Exit { 41 exit(); 42 } 43 { 44 walk WALK {} 45 } 46 %%
其中:code
一、%class Hero: %class標籤用於定義狀態的主體對象,也就是說,指定哪一個類具有這些狀態和行爲。好比:Hero(Hero.h和Hero.cpp)對象
二、%header Hero.h:%header標籤用於定義主體對象的頭文件。
三、%start HeroMap::STOP:%start標籤用於定義對象的初始狀態,STOP是一個狀態類,對應的類是:HeroMap_STOP。
四、%map HeroMap:重點來了,%map標籤用於定義狀態表對象的名稱。什麼是狀態表呢?SMC在生成狀態機代碼時,會將對象的各類狀態都建立爲靜態對象。而狀態表存放全部的靜態變量(包含所有狀態)。對應的是:HeroMap。
五、%%...%%:這一對%%中間定義了各個狀態類以及狀態的各類行爲。Entry{}表明在切換到該狀態時要執行的事件,Ext{}表示離開改狀態時要執行的事件,Entry和Exit是有限狀態機經常使用的技巧,能夠在開始和結束時執行一些動做。後面有一對花括號用於定義狀態的行爲,好比狀態在執行到哪一個函數後切換到另一個狀態,以及切換狀態時要執行的動做。也就是狀態存活期間的行爲。格式以下:
1 STOP // 狀態名
2
3 Entry { 4 // 執行這個函數進入該狀態
5 stop(); 6 } 7
8 Exit { 9 // 執行這個函數退出該狀態
10 exit(); 11 } 12
13 { 14 // 狀態切換邏輯
15 walk WALK {} 16 }
當運行下面的命令,會自動生成文件:Hero_sm.h和Hero_sm.cpp。連同自帶的statemap.h一塊兒加入到項目中。
1 java -jar [$PATH]Smc.jar -c++ Hero.sm //[PATH] smc.jar的路徑
3、添加實體類
業務邏輯仍然要咱們本身實現,那就是寫Hero.h和Hero.cpp。不過此次寫Hero類須要按必定的規則,下面是源代碼:
1 // Hero.h 2 //
3 #ifndef HERO_H_ 4 #define HERO_H_
5
6 #include "cocos2d.h"
7 USING_NS_CC; 8
9 #include "Hero_sm.h"
10
11
12 #define MAX_STOP_TIME 3
13 #define MAX_WALK_TIME 10
14 #define MAX_WALK_DIST 200
15
16
17 class Hero : public Node 18 { 19 public: 20 CREATE_FUNC(Hero); 21
22 virtual bool init(); 23
24 void stop(); 25
26 void walk(); 27
28 void turn(); 29
30 void exit(); 31
32 private: 33 HeroContext * _fsm; 34
35 int _step; 36 int _curPos; 37 time_t _curTime; 38
39 // Sprite * _sprite;
40
41 private: 42 void onStop(float dt) 43 { 44 int d = (int) (time(0) - _curTime); 45 if (d > MAX_STOP_TIME) { 46 _fsm->walk(); 47 } 48 } 49
50
51 void onWalk(float dt) 52 { 53 if (_curPos > MAX_WALK_DIST || _curPos < -MAX_WALK_DIST) { 54 _fsm->turn(); 55 } 56
57 int d = (int) (time(0) - _curTime); 58 if (d > MAX_WALK_TIME) { 59 _fsm->stop(); 60 } 61
62 _curPos += _step; 63 } 64
65
66 void onTurn(float dt) 67 { 68 _fsm->walk(); 69 } 70 }; 71
72 #endif // HERO_H_
上面的on***是觸發狀態的回調函數,實體狀態改變的業務邏輯在這裏實現。
1 // Hero.cpp 2 //
3 #include "Hero.h"
4
5 #include <time.h>
6 #include <assert.h>
7
8
9 void Hero::exit() 10 { 11 this->unscheduleAllCallbacks(); 12 CCLOG("exit()"); 13 } 14
15
16 bool Hero::init() 17 { 18 _step = 1; 19 _curPos = 0; 20 _curTime = time(0); 21
22 _fsm = new HeroContext(*this); 23 assert(_fsm); 24
25 _fsm->setDebugFlag(true); 26 _fsm->enterStartState(); 27
28 return true; 29 } 30
31
32 void Hero::stop() 33 { 34 _curTime = time(0); 35
36 CCLOG("stop(): pos=%d", _curPos); 37
38 this->schedule(schedule_selector(Hero::onStop), 0.1f); 39 } 40
41
42 void Hero::walk() 43 { 44 _curTime = time(0); 45
46 CCLOG("walk(): pos=%d", _curPos); 47
48 this->schedule(schedule_selector(Hero::onWalk), 0.1f); 49 } 50
51
52 void Hero::turn() 53 { 54 _step *= -1; 55 CCLOG("turn(): step=%d", _step); 56
57 this->schedule(schedule_selector(Hero::onTurn), 0.1f); 58 }
4、狀態機類
框架代碼Smc已經幫咱們生成好了:Hero_sm.h和Hero_sm.cpp。
5、使用
1 Hero* hero= Hero::create(); 2 addChild(hero);
6、總結
FSM是一種固定的範式,所以採用工具幫咱們實現能夠減小犯錯誤的機會。輸入的文件就是:實體.sm。咱們把重點放在業務邏輯上,因此與狀態有關的代碼smc都幫咱們生成好了。