SMC --- COCOS2D-X 状态机代码生成工具

一、简介      

        利用有限状态机来控制对象的行为,其原理就是利用多态,常常我们自己写代码,需要很大篇幅,万一需要再加一个或者几个状态,那么我们自己维护时就会很麻烦,SMC这个工具可以帮助我们解决这个问题。

      使用这个工具之前我们需要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,而我们所需要做的唯一的工作就是编写拥有这些状态的主体类。

二、编写smc文件

       首先写一个sm的文件     Hero.sm:

 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 %%

其中:

  1、%class Hero: %class标签用于定义状态的主体对象,也就是说,指定哪个类具备这些状态和行为。比如:Hero(Hero.h和Hero.cpp)

  2、%header Hero.h:%header标签用于定义主体对象的头文件。

  3、%start HeroMap::STOP:%start标签用于定义对象的初始状态,STOP是一个状态类,对应的类是:HeroMap_STOP。

  4、%map HeroMap:重点来了,%map标签用于定义状态表对象的名称。什么是状态表呢?SMC在生成状态机代码时,会将对象的各种状态都创建为静态对象。而状态表存放所有的静态变量(包含全部状态)。对应的是:HeroMap。

  5、%%...%%:这一对%%中间定义了各个状态类以及状态的各种行为。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的路径

三、添加实体类

  业务逻辑仍然要我们自己实现,那就是写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 }

四、状态机类

  框架代码Smc已经帮我们生成好了:Hero_sm.h和Hero_sm.cpp。

五、使用

1 Hero* hero= Hero::create();  
2 addChild(hero);

六、总结

  FSM是一种固定的范式,因此采用工具帮我们实现可以减少犯错误的机会。输入的文件就是:实体.sm。我们把重点放在业务逻辑上,所以与状态有关的代码smc都帮我们生成好了。

posted @ 2015-06-05 23:23  【Winco】  阅读(1638)  评论(1编辑  收藏  举报