cocos2d-x 游戏开发之有限状态机(FSM) (四)

虽然我们了解了FSM,并且可以写自己的FSM,但是有更好的工具帮我们完成这个繁琐的工作。SMC(http://smc.sourceforge.net/)就是这样的工具。下载地址:

http://sourceforge.net/projects/smc/files/latest/download

在bin下面的Smc.jar是用于生成状态类的命令行工具。使用如下命令:

  1. $ java -jar Smc.jar Monkey.sm

1 真实世界的FSM

首先定义一个状态机纯文本文件:Monkey.sm,内容如下:

  1. // cheungmine
  2. // 2015-01-22
  3.  
  4. // entity class
  5. %class Monkey
  6.  
  7. // entity class header
  8. %header Monkey.h
  9.  
  10. // inital state
  11. %start MonkeyMap::STOP
  12.  
  13. // entity state map
  14. %map MonkeyMap
  15. %%
  16. STOP
  17. Entry {
  18. stop();
  19. }
  20. Exit {
  21. exit();
  22. }
  23. {
  24. walk WALK {}
  25. }
  26.  
  27. WALK
  28. Entry {
  29. walk();
  30. }
  31. Exit {
  32. exit();
  33. }
  34. {
  35. stop STOP {}
  36. turn TURN {}
  37. }
  38.  
  39. TURN
  40. Entry {
  41. turn();
  42. }
  43. Exit {
  44. exit();
  45. }
  46. {
  47. walk WALK {}
  48. }
  49. %%

其中%class Monkey 说明实体类的名字:Monkey (Monkey.h和Monkey.cpp)

%header 指定头文件:Monkey.h

%map 指明状态图类,这个类包含全部状态。这里是:MonkeyMap

%start 指明其实的状态,这里是STOP,对应的类是:MonkeyMap_STOP

%%...%%之间的部分定义每个状态。格式如下:

  1. STOP // 状态名
  2.  
  3. Entry {
  4.  
  5. // 执行这个函数进入该状态
  6.  
  7. stop();
  8.  
  9. }
  10.  
  11. Exit {
  12.  
  13. // 执行这个函数退出该状态
  14.  
  15. exit();
  16.  
  17. }
  18.  
  19. {
  20.  
  21. // 状态切换逻辑
  22.  
  23. walk WALK {}
  24.  
  25. }

当运行下面的命令,会自动生成文件:Monkey_sm.h和Monkey_sm.cpp。连同自带的statemap.h一起加入到项目中。

  1. java -jar Smc.jar Monkey.sm

2 实体类

业务逻辑仍然要我们自己实现,那就是写Monkey.h和Monkey.cpp。不过这次写Monkey类需要按一定的规则,下面是源代码:

  1. // Monkey.h
  2. //
  3. #ifndef MONKEY_H_
  4. #define MONKEY_H_
  5.  
  6. #include "cocos2d.h"
  7. USING_NS_CC;
  8.  
  9. #include "Monkey_sm.h"
  10.  
  11. #define MAX_STOP_TIME 3
  12. #define MAX_WALK_TIME 10
  13. #define MAX_WALK_DIST 200
  14.  
  15. class Monkey : public Node
  16. {
  17. public:
  18. CREATE_FUNC(Monkey);
  19.  
  20. virtual bool init();
  21.  
  22. void stop();
  23.  
  24. void walk();
  25.  
  26. void turn();
  27.  
  28. void exit();
  29.  
  30. private:
  31. MonkeyContext * _fsm;
  32.  
  33. int _step;
  34. int _curPos;
  35. time_t _curTime;
  36.  
  37. // Sprite * _sprite;
  38.  
  39. private:
  40. void onIdleStop(float dt)
  41. {
  42. int d = (int) (time(0) - _curTime);
  43. if (d > MAX_STOP_TIME) {
  44. _fsm->walk();
  45. }
  46. }
  47.  
  48. void onIdleWalk(float dt)
  49. {
  50. if (_curPos > MAX_WALK_DIST || _curPos < -MAX_WALK_DIST) {
  51. _fsm->turn();
  52. }
  53.  
  54. int d = (int) (time(0) - _curTime);
  55. if (d > MAX_WALK_TIME) {
  56. _fsm->stop();
  57. }
  58.  
  59. _curPos += _step;
  60. }
  61.  
  62. void onIdleTurn(float dt)
  63. {
  64. _fsm->walk();
  65. }
  66. };
  67.  
  68. #endif // MONKEY_H_

上面的onIdle????是触发状态的回调函数,实体状态改变的业务逻辑在这里实现。

  1. // Monkey.cpp
  2. //
  3. #include "Monkey.h"
  4.  
  5. #include <time.h>
  6. #include <assert.h>
  7.  
  8. void Monkey::exit()
  9. {
  10. this->unscheduleAllCallbacks();
  11. cocos2d::log("exit()");
  12. }
  13.  
  14. bool Monkey::init()
  15. {
  16. _step = 1;
  17. _curPos = 0;
  18. _curTime = time(0);
  19.  
  20. // _sprite = Sprite::create("monkey.png");
  21. // addChild(_sprite);
  22.  
  23. _fsm = new MonkeyContext(*this);
  24. assert(_fsm);
  25.  
  26. _fsm->setDebugFlag(true);
  27. _fsm->enterStartState();
  28.  
  29. return true;
  30. }
  31.  
  32. void Monkey::stop()
  33. {
  34. _curTime = time(0);
  35.  
  36. cocos2d::log("stop(): pos=%d", _curPos);
  37.  
  38. this->schedule(schedule_selector(Monkey::onIdleStop), 0.1f);
  39. }
  40.  
  41. void Monkey::walk()
  42. {
  43. _curTime = time(0);
  44.  
  45. cocos2d::log("walk(): pos=%d", _curPos);
  46.  
  47. this->schedule(schedule_selector(Monkey::onIdleWalk), 0.1f);
  48. }
  49.  
  50. void Monkey::turn()
  51. {
  52. _step *= -1;
  53. cocos2d::log("turn(): step=%d", _step);
  54.  
  55. this->schedule(schedule_selector(Monkey::onIdleTurn), 0.1f);
  56. }

3 状态机类

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

  1. //
  2. // ex: set ro:
  3. // DO NOT EDIT.
  4. // generated by smc (http://smc.sourceforge.net/)
  5. // from file : Monkey.sm
  6. //
  7.  
  8. #ifndef MONKEY_SM_H
  9. #define MONKEY_SM_H
  10.  
  11. #define SMC_USES_IOSTREAMS
  12.  
  13. #include "statemap.h"
  14.  
  15. // Forward declarations.
  16. class MonkeyMap;
  17. class MonkeyMap_STOP;
  18. class MonkeyMap_WALK;
  19. class MonkeyMap_TURN;
  20. class MonkeyMap_Default;
  21. class MonkeyState;
  22. class MonkeyContext;
  23. class Monkey;
  24.  
  25. class MonkeyState :
  26. public statemap::State
  27. {
  28. public:
  29.  
  30. MonkeyState(const char * const name, const int stateId)
  31. : statemap::State(name, stateId)
  32. {};
  33.  
  34. virtual void Entry(MonkeyContext&) {};
  35. virtual void Exit(MonkeyContext&) {};
  36.  
  37. virtual void stop(MonkeyContext& context);
  38. virtual void turn(MonkeyContext& context);
  39. virtual void walk(MonkeyContext& context);
  40.  
  41. protected:
  42.  
  43. virtual void Default(MonkeyContext& context);
  44. };
  45.  
  46. class MonkeyMap
  47. {
  48. public:
  49.  
  50. static MonkeyMap_STOP STOP;
  51. static MonkeyMap_WALK WALK;
  52. static MonkeyMap_TURN TURN;
  53. };
  54.  
  55. class MonkeyMap_Default :
  56. public MonkeyState
  57. {
  58. public:
  59.  
  60. MonkeyMap_Default(const char * const name, const int stateId)
  61. : MonkeyState(name, stateId)
  62. {};
  63.  
  64. };
  65.  
  66. class MonkeyMap_STOP :
  67. public MonkeyMap_Default
  68. {
  69. public:
  70. MonkeyMap_STOP(const char * const name, const int stateId)
  71. : MonkeyMap_Default(name, stateId)
  72. {};
  73.  
  74. virtual void Entry(MonkeyContext&);
  75. virtual void Exit(MonkeyContext&);
  76. virtual void walk(MonkeyContext& context);
  77. };
  78.  
  79. class MonkeyMap_WALK :
  80. public MonkeyMap_Default
  81. {
  82. public:
  83. MonkeyMap_WALK(const char * const name, const int stateId)
  84. : MonkeyMap_Default(name, stateId)
  85. {};
  86.  
  87. virtual void Entry(MonkeyContext&);
  88. virtual void Exit(MonkeyContext&);
  89. virtual void stop(MonkeyContext& context);
  90. virtual void turn(MonkeyContext& context);
  91. };
  92.  
  93. class MonkeyMap_TURN :
  94. public MonkeyMap_Default
  95. {
  96. public:
  97. MonkeyMap_TURN(const char * const name, const int stateId)
  98. : MonkeyMap_Default(name, stateId)
  99. {};
  100.  
  101. virtual void Entry(MonkeyContext&);
  102. virtual void Exit(MonkeyContext&);
  103. virtual void walk(MonkeyContext& context);
  104. };
  105.  
  106. class MonkeyContext :
  107. public statemap::FSMContext
  108. {
  109. public:
  110.  
  111. explicit MonkeyContext(Monkey& owner)
  112. : FSMContext(MonkeyMap::STOP),
  113. _owner(&owner)
  114. {};
  115.  
  116. MonkeyContext(Monkey& owner, const statemap::State& state)
  117. : FSMContext(state),
  118. _owner(&owner)
  119. {};
  120.  
  121. virtual void enterStartState()
  122. {
  123. getState().Entry(*this);
  124. }
  125.  
  126. inline Monkey& getOwner()
  127. {
  128. return *_owner;
  129. };
  130.  
  131. inline MonkeyState& getState()
  132. {
  133. if (_state == NULL)
  134. {
  135. throw statemap::StateUndefinedException();
  136. }
  137.  
  138. return dynamic_cast<MonkeyState&>(*_state);
  139. };
  140.  
  141. inline void stop()
  142. {
  143. getState().stop(*this);
  144. };
  145.  
  146. inline void turn()
  147. {
  148. getState().turn(*this);
  149. };
  150.  
  151. inline void walk()
  152. {
  153. getState().walk(*this);
  154. };
  155.  
  156. private:
  157.  
  158. Monkey* _owner;
  159. };
  160.  
  161. #endif // MONKEY_SM_H
  162.  
  163. //
  164. // Local variables:
  165. // buffer-read-only: t
  166. // End:
  167. //

  1. //
  2. // ex: set ro:
  3. // DO NOT EDIT.
  4. // generated by smc (http://smc.sourceforge.net/)
  5. // from file : Monkey.sm
  6. //
  7.  
  8. #include "Monkey.h"
  9. #include "Monkey_sm.h"
  10.  
  11. using namespace statemap;
  12.  
  13. // Static class declarations.
  14. MonkeyMap_STOP MonkeyMap::STOP("MonkeyMap::STOP", 0);
  15. MonkeyMap_WALK MonkeyMap::WALK("MonkeyMap::WALK", 1);
  16. MonkeyMap_TURN MonkeyMap::TURN("MonkeyMap::TURN", 2);
  17.  
  18. void MonkeyState::stop(MonkeyContext& context)
  19. {
  20. Default(context);
  21. }
  22.  
  23. void MonkeyState::turn(MonkeyContext& context)
  24. {
  25. Default(context);
  26. }
  27.  
  28. void MonkeyState::walk(MonkeyContext& context)
  29. {
  30. Default(context);
  31. }
  32.  
  33. void MonkeyState::Default(MonkeyContext& context)
  34. {
  35. throw (
  36. TransitionUndefinedException(
  37. context.getState().getName(),
  38. context.getTransition()));
  39.  
  40. }
  41.  
  42. void MonkeyMap_STOP::Entry(MonkeyContext& context)
  43.  
  44. {
  45. Monkey& ctxt = context.getOwner();
  46.  
  47. ctxt.stop();
  48. }
  49.  
  50. void MonkeyMap_STOP::Exit(MonkeyContext& context)
  51.  
  52. {
  53. Monkey& ctxt = context.getOwner();
  54.  
  55. ctxt.exit();
  56. }
  57.  
  58. void MonkeyMap_STOP::walk(MonkeyContext& context)
  59. {
  60.  
  61. context.getState().Exit(context);
  62. context.setState(MonkeyMap::WALK);
  63. context.getState().Entry(context);
  64.  
  65. }
  66.  
  67. void MonkeyMap_WALK::Entry(MonkeyContext& context)
  68.  
  69. {
  70. Monkey& ctxt = context.getOwner();
  71.  
  72. ctxt.walk();
  73. }
  74.  
  75. void MonkeyMap_WALK::Exit(MonkeyContext& context)
  76.  
  77. {
  78. Monkey& ctxt = context.getOwner();
  79.  
  80. ctxt.exit();
  81. }
  82.  
  83. void MonkeyMap_WALK::stop(MonkeyContext& context)
  84. {
  85.  
  86. context.getState().Exit(context);
  87. context.setState(MonkeyMap::STOP);
  88. context.getState().Entry(context);
  89.  
  90. }
  91.  
  92. void MonkeyMap_WALK::turn(MonkeyContext& context)
  93. {
  94.  
  95. context.getState().Exit(context);
  96. context.setState(MonkeyMap::TURN);
  97. context.getState().Entry(context);
  98.  
  99. }
  100.  
  101. void MonkeyMap_TURN::Entry(MonkeyContext& context)
  102.  
  103. {
  104. Monkey& ctxt = context.getOwner();
  105.  
  106. ctxt.turn();
  107. }
  108.  
  109. void MonkeyMap_TURN::Exit(MonkeyContext& context)
  110.  
  111. {
  112. Monkey& ctxt = context.getOwner();
  113.  
  114. ctxt.exit();
  115. }
  116.  
  117. void MonkeyMap_TURN::walk(MonkeyContext& context)
  118. {
  119.  
  120. context.getState().Exit(context);
  121. context.setState(MonkeyMap::WALK);
  122. context.getState().Entry(context);
  123.  
  124. }
  125.  
  126. //
  127. // Local variables:
  128. // buffer-read-only: t
  129. // End:
  130. //

4 总结

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

在cocos2d-x中使用很简单:

  1. bool HelloWorld::init()
  2. {
  3. //////////////////////////////
  4. // 1. super init first
  5. if ( !Layer::init() )
  6. {
  7. return false;
  8. }
  9.  
  10. auto rootNode = CSLoader::createNode("MainScene.csb");
  11.  
  12. addChild(rootNode);
  13.  
  14. auto closeItem = static_cast<ui::Button*>(rootNode->getChildByName("Button_1"));
  15. closeItem->addTouchEventListener(CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));
  16.  
  17. /////////////////// test ///////////////////////
  18. Monkey * mk = Monkey::create();
  19. addChild(mk);
  20.  
  21. return true;
  22. }

就这样了!不明白的地方请仔细阅读:

Cocos2d-x游戏开发之旅(钟迪龙)

cocos2d-x 游戏开发之有限状态机(FSM) (四)的更多相关文章

  1. cocos2d-x 游戏开发之有限状态机(FSM) (三)

    cocos2d-x 游戏开发之有限状态机(FSM) (三) 有限状态机简称FSM,现在我们创建一个专门的FSM类,负责管理对象(Monkey)的状态.然后Monkey类就实现了行为与状态分离.Monk ...

  2. cocos2d-x 游戏开发之有限状态机(FSM) (一)

    cocos2d-x 游戏开发之有限状态机(FSM) (一) 参考:http://blog.csdn.net/mgphuang/article/details/5845252<Cocos2d-x游 ...

  3. cocos2d-x 游戏开发之有限状态机(FSM) (二)

    cocos2d-x 游戏开发之有限状态机(FSM)  (二) 1 状态模式

  4. 《C++游戏开发》笔记十四 平滑过渡的战争迷雾(二) 实现:真正的迷雾来了

    本系列文章由七十一雾央编写,转载请注明出处.  http://blog.csdn.net/u011371356/article/details/9712321 作者:七十一雾央 新浪微博:http:/ ...

  5. iOS cocos2d 2游戏开发实战(第3版)书评

    2013是游戏爆发的一年,手游用户也是飞速暴增.虽然自己不做游戏,但也是时刻了解手机应用开发的新动向.看到CSDN的"写书评得技术图书赢下载分"活动,就申请了一本<iOS c ...

  6. java游戏开发杂谈 - 有限状态机

    在不同的阶段,游戏所运行的逻辑.所显示的界面,都是不同的. 以五子棋举例,游戏开始.游戏中.胜负已分,对应的界面和逻辑都不同. 在游戏中,又分为:自己下棋.对方下棋.游戏暂停.悔棋等多个状态. 再比如 ...

  7. Unity3D游戏开发从零单排(四) - 制作一个iOS游戏

    提要 此篇是一个国外教程的翻译,尽管有点老,可是适合新手入门. 自己去写代码.debug,布置场景,能够收获到非常多.游戏邦上已经有前面两部分的译文,这里翻译的是游戏的最后一个部分. 欢迎回来 在第一 ...

  8. DirectX12 3D 游戏开发与实战第四章内容(上)

    Direct3D的初始化(上) 学习目标 了解Direct3D在3D编程中相对于硬件所扮演的角色 理解组件对象模型COM在Direct3D中的作用 掌握基础的图像学概念,例如2D图像的存储方式,页面翻 ...

  9. DirectX12 3D 游戏开发与实战第四章内容(下)

    Direct3D的初始化(下) 学习目标 了解Direct3D在3D编程中相对于硬件所扮演的角色 理解组件对象模型COM在Direct3D中的作用 掌握基础的图像学概念,例如2D图像的存储方式,页面翻 ...

随机推荐

  1. Scala:函数式编程之下划线underscore

    http://blog.csdn.net/pipisorry/article/details/52913548 python参考[python函数式编程:apply, map, lambda和偏函数] ...

  2. Leetcode解题-树(5.0.0)基础类

    与第二章类似,LeetCode指定了TreeNode实现.为了方便后续习题的编写和测试,创建一个基础父类,包含TreeNode实现,以及create()和print()创建和打印树的方法.其中crea ...

  3. Redis集群功能预览

    目前Redis Cluster仍处于Beta版本,Redis 3.0将会加入,在此可以先对其主要功能和原理进行一个预览.参考<Redis Cluster - a pragmatic approa ...

  4. 在Linux上的虚拟机上启动Oracle上报ORA-00845: MEMORY_TARGET not supported on this system的问题解决

    解决办法: 1.将当前虚拟机的内容调整大一些(以下转载:http://jingyan.baidu.com/article/414eccf67b8baa6b421f0a60.html) VMware虚拟 ...

  5. 给定整数a1、a2、a3、...、an,判断是否可以从中选出若干个数,使得它们的和等于k(k任意给定,且满足-10^8 <= k <= 10^8)。

    给定整数a1.a2.a3.....an,判断是否可以从中选出若干个数,使得它们的和等于k(k任意给定,且满足-10^8 <= k <= 10^8). 分析:此题相对于本节"寻找满 ...

  6. Java基本语法-----java运算符

    这块的东西比较多 我写了太慢了 于是在word里写好贴出来供大家一起学习 运算符 -赋值运算符 -比较运算符 -逻辑运算符 -位运算符 -移位操作符 -三元运算符 [正在看本人博客的这位童鞋,我看你气 ...

  7. antlr v4 使用指南连载2——准备环境

    antlr v4 开发环境         从上一篇文章的例子中可以知道,antlr有一套自己的语法来声明目标语言的语法,因此它本身就需要编译或者使用antlr提供的api来读取这些语法规则,并使之可 ...

  8. Android 自定义View-android学习之旅(十四)

    自定义View的步骤 当andoid提供的系统组件不满足要求时候,完全可以集成View来派生自定义组件. 首定定义一个继承View的子类,然后重写他一个或几个方法. 重写的方法介绍 构造器:这是定制V ...

  9. UNIX/LINUX程序设计教程(1)-- 获取系统信息

          1.主机标识 每一台机器都有一个主机名,主机名由系统管理员指定,在网络中主机名可能是一个网络域名. 函数 gethostname() 和 sethostname() 可以用来获取和设置主机 ...

  10. JAVA之旅(三十)——打印流PrintWriter,合并流,切割文件并且合并,对象的序列化Serializable,管道流,RandomAccessFile,IO其他类,字符编码

    JAVA之旅(三十)--打印流PrintWriter,合并流,切割文件并且合并,对象的序列化Serializable,管道流,RandomAccessFile,IO其他类,字符编码 三十篇了,又是一个 ...