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都帮咱们生成好了。