cocos2d-x 游戏开发之有限状态机(FSM) (四)
cocos2d-x 游戏开发之有限状态机(FSM) (四)
虽然我们了解了FSM,并且可以写自己的FSM,但是有更好的工具帮我们完成这个繁琐的工作。SMC(http://smc.sourceforge.net/)就是这样的工具。下载地址:
http://sourceforge.net/projects/smc/files/latest/download
在bin下面的Smc.jar是用于生成状态类的命令行工具。使用如下命令:
$ java -jar Smc.jar Monkey.sm
1 真实世界的FSM
首先定义一个状态机纯文本文件:Monkey.sm,内容如下:
// cheungmine // 2015-01-22 // entity class %class Monkey // entity class header %header Monkey.h // inital state %start MonkeyMap::STOP // entity state map %map MonkeyMap %% STOP Entry { stop(); } Exit { exit(); } { walk WALK {} } WALK Entry { walk(); } Exit { exit(); } { stop STOP {} turn TURN {} } TURN Entry { turn(); } Exit { exit(); } { walk WALK {} } %%
其中%class Monkey 说明实体类的名字:Monkey (Monkey.h和Monkey.cpp)
%header 指定头文件:Monkey.h
%map 指明状态图类,这个类包含全部状态。这里是:MonkeyMap
%start 指明其实的状态,这里是STOP,对应的类是:MonkeyMap_STOP
%%...%%之间的部分定义每个状态。格式如下:
STOP // 状态名 Entry { // 执行这个函数进入该状态 stop(); } Exit { // 执行这个函数退出该状态 exit(); } { // 状态切换逻辑 walk WALK {} }
当运行下面的命令,会自动生成文件:Monkey_sm.h和Monkey_sm.cpp。连同自带的statemap.h一起加入到项目中。
java -jar Smc.jar Monkey.sm
2 实体类
业务逻辑仍然要我们自己实现,那就是写Monkey.h和Monkey.cpp。不过这次写Monkey类需要按一定的规则,下面是源代码:
// Monkey.h // #ifndef MONKEY_H_ #define MONKEY_H_ #include "cocos2d.h" USING_NS_CC; #include "Monkey_sm.h" #define MAX_STOP_TIME 3 #define MAX_WALK_TIME 10 #define MAX_WALK_DIST 200 class Monkey : public Node { public: CREATE_FUNC(Monkey); virtual bool init(); void stop(); void walk(); void turn(); void exit(); private: MonkeyContext * _fsm; int _step; int _curPos; time_t _curTime; // Sprite * _sprite; private: void onIdleStop(float dt) { int d = (int) (time(0) - _curTime); if (d > MAX_STOP_TIME) { _fsm->walk(); } } void onIdleWalk(float dt) { if (_curPos > MAX_WALK_DIST || _curPos < -MAX_WALK_DIST) { _fsm->turn(); } int d = (int) (time(0) - _curTime); if (d > MAX_WALK_TIME) { _fsm->stop(); } _curPos += _step; } void onIdleTurn(float dt) { _fsm->walk(); } }; #endif // MONKEY_H_
上面的onIdle????是触发状态的回调函数,实体状态改变的业务逻辑在这里实现。
// Monkey.cpp // #include "Monkey.h" #include <time.h> #include <assert.h> void Monkey::exit() { this->unscheduleAllCallbacks(); cocos2d::log("exit()"); } bool Monkey::init() { _step = 1; _curPos = 0; _curTime = time(0); // _sprite = Sprite::create("monkey.png"); // addChild(_sprite); _fsm = new MonkeyContext(*this); assert(_fsm); _fsm->setDebugFlag(true); _fsm->enterStartState(); return true; } void Monkey::stop() { _curTime = time(0); cocos2d::log("stop(): pos=%d", _curPos); this->schedule(schedule_selector(Monkey::onIdleStop), 0.1f); } void Monkey::walk() { _curTime = time(0); cocos2d::log("walk(): pos=%d", _curPos); this->schedule(schedule_selector(Monkey::onIdleWalk), 0.1f); } void Monkey::turn() { _step *= -1; cocos2d::log("turn(): step=%d", _step); this->schedule(schedule_selector(Monkey::onIdleTurn), 0.1f); }
3 状态机类
框架代码Smc已经帮我们生成好了:Monkey_sm.h和Monkey_sm.cpp:
// // ex: set ro: // DO NOT EDIT. // generated by smc (http://smc.sourceforge.net/) // from file : Monkey.sm // #ifndef MONKEY_SM_H #define MONKEY_SM_H #define SMC_USES_IOSTREAMS #include "statemap.h" // Forward declarations. class MonkeyMap; class MonkeyMap_STOP; class MonkeyMap_WALK; class MonkeyMap_TURN; class MonkeyMap_Default; class MonkeyState; class MonkeyContext; class Monkey; class MonkeyState : public statemap::State { public: MonkeyState(const char * const name, const int stateId) : statemap::State(name, stateId) {}; virtual void Entry(MonkeyContext&) {}; virtual void Exit(MonkeyContext&) {}; virtual void stop(MonkeyContext& context); virtual void turn(MonkeyContext& context); virtual void walk(MonkeyContext& context); protected: virtual void Default(MonkeyContext& context); }; class MonkeyMap { public: static MonkeyMap_STOP STOP; static MonkeyMap_WALK WALK; static MonkeyMap_TURN TURN; }; class MonkeyMap_Default : public MonkeyState { public: MonkeyMap_Default(const char * const name, const int stateId) : MonkeyState(name, stateId) {}; }; class MonkeyMap_STOP : public MonkeyMap_Default { public: MonkeyMap_STOP(const char * const name, const int stateId) : MonkeyMap_Default(name, stateId) {}; virtual void Entry(MonkeyContext&); virtual void Exit(MonkeyContext&); virtual void walk(MonkeyContext& context); }; class MonkeyMap_WALK : public MonkeyMap_Default { public: MonkeyMap_WALK(const char * const name, const int stateId) : MonkeyMap_Default(name, stateId) {}; virtual void Entry(MonkeyContext&); virtual void Exit(MonkeyContext&); virtual void stop(MonkeyContext& context); virtual void turn(MonkeyContext& context); }; class MonkeyMap_TURN : public MonkeyMap_Default { public: MonkeyMap_TURN(const char * const name, const int stateId) : MonkeyMap_Default(name, stateId) {}; virtual void Entry(MonkeyContext&); virtual void Exit(MonkeyContext&); virtual void walk(MonkeyContext& context); }; class MonkeyContext : public statemap::FSMContext { public: explicit MonkeyContext(Monkey& owner) : FSMContext(MonkeyMap::STOP), _owner(&owner) {}; MonkeyContext(Monkey& owner, const statemap::State& state) : FSMContext(state), _owner(&owner) {}; virtual void enterStartState() { getState().Entry(*this); } inline Monkey& getOwner() { return *_owner; }; inline MonkeyState& getState() { if (_state == NULL) { throw statemap::StateUndefinedException(); } return dynamic_cast<MonkeyState&>(*_state); }; inline void stop() { getState().stop(*this); }; inline void turn() { getState().turn(*this); }; inline void walk() { getState().walk(*this); }; private: Monkey* _owner; }; #endif // MONKEY_SM_H // // Local variables: // buffer-read-only: t // End: //
// // ex: set ro: // DO NOT EDIT. // generated by smc (http://smc.sourceforge.net/) // from file : Monkey.sm // #include "Monkey.h" #include "Monkey_sm.h" using namespace statemap; // Static class declarations. MonkeyMap_STOP MonkeyMap::STOP("MonkeyMap::STOP", 0); MonkeyMap_WALK MonkeyMap::WALK("MonkeyMap::WALK", 1); MonkeyMap_TURN MonkeyMap::TURN("MonkeyMap::TURN", 2); void MonkeyState::stop(MonkeyContext& context) { Default(context); } void MonkeyState::turn(MonkeyContext& context) { Default(context); } void MonkeyState::walk(MonkeyContext& context) { Default(context); } void MonkeyState::Default(MonkeyContext& context) { throw ( TransitionUndefinedException( context.getState().getName(), context.getTransition())); } void MonkeyMap_STOP::Entry(MonkeyContext& context) { Monkey& ctxt = context.getOwner(); ctxt.stop(); } void MonkeyMap_STOP::Exit(MonkeyContext& context) { Monkey& ctxt = context.getOwner(); ctxt.exit(); } void MonkeyMap_STOP::walk(MonkeyContext& context) { context.getState().Exit(context); context.setState(MonkeyMap::WALK); context.getState().Entry(context); } void MonkeyMap_WALK::Entry(MonkeyContext& context) { Monkey& ctxt = context.getOwner(); ctxt.walk(); } void MonkeyMap_WALK::Exit(MonkeyContext& context) { Monkey& ctxt = context.getOwner(); ctxt.exit(); } void MonkeyMap_WALK::stop(MonkeyContext& context) { context.getState().Exit(context); context.setState(MonkeyMap::STOP); context.getState().Entry(context); } void MonkeyMap_WALK::turn(MonkeyContext& context) { context.getState().Exit(context); context.setState(MonkeyMap::TURN); context.getState().Entry(context); } void MonkeyMap_TURN::Entry(MonkeyContext& context) { Monkey& ctxt = context.getOwner(); ctxt.turn(); } void MonkeyMap_TURN::Exit(MonkeyContext& context) { Monkey& ctxt = context.getOwner(); ctxt.exit(); } void MonkeyMap_TURN::walk(MonkeyContext& context) { context.getState().Exit(context); context.setState(MonkeyMap::WALK); context.getState().Entry(context); } // // Local variables: // buffer-read-only: t // End: //
4 总结
FSM是一种固定的范式,因此采用工具帮我们实现可以减少犯错误的机会。输入的文件就是:实体.sm。我们把重点放在业务逻辑上,所以与状态有关的代码smc都帮我们生成好了。对比一下我们手工创建和smc框架工具自动生成的类:
在cocos2d-x中使用很简单:
bool HelloWorld::init() { ////////////////////////////// // 1. super init first if ( !Layer::init() ) { return false; } auto rootNode = CSLoader::createNode("MainScene.csb"); addChild(rootNode); auto closeItem = static_cast<ui::Button*>(rootNode->getChildByName("Button_1")); closeItem->addTouchEventListener(CC_CALLBACK_1(HelloWorld::menuCloseCallback, this)); /////////////////// test /////////////////////// Monkey * mk = Monkey::create(); addChild(mk); return true; }
就这样了!不明白的地方请仔细阅读:
Cocos2d-x游戏开发之旅(钟迪龙)
cocos2d-x 游戏开发之有限状态机(FSM) (四)的更多相关文章
- cocos2d-x 游戏开发之有限状态机(FSM) (三)
cocos2d-x 游戏开发之有限状态机(FSM) (三) 有限状态机简称FSM,现在我们创建一个专门的FSM类,负责管理对象(Monkey)的状态.然后Monkey类就实现了行为与状态分离.Monk ...
- cocos2d-x 游戏开发之有限状态机(FSM) (一)
cocos2d-x 游戏开发之有限状态机(FSM) (一) 参考:http://blog.csdn.net/mgphuang/article/details/5845252<Cocos2d-x游 ...
- cocos2d-x 游戏开发之有限状态机(FSM) (二)
cocos2d-x 游戏开发之有限状态机(FSM) (二) 1 状态模式
- 《C++游戏开发》笔记十四 平滑过渡的战争迷雾(二) 实现:真正的迷雾来了
本系列文章由七十一雾央编写,转载请注明出处. http://blog.csdn.net/u011371356/article/details/9712321 作者:七十一雾央 新浪微博:http:/ ...
- iOS cocos2d 2游戏开发实战(第3版)书评
2013是游戏爆发的一年,手游用户也是飞速暴增.虽然自己不做游戏,但也是时刻了解手机应用开发的新动向.看到CSDN的"写书评得技术图书赢下载分"活动,就申请了一本<iOS c ...
- java游戏开发杂谈 - 有限状态机
在不同的阶段,游戏所运行的逻辑.所显示的界面,都是不同的. 以五子棋举例,游戏开始.游戏中.胜负已分,对应的界面和逻辑都不同. 在游戏中,又分为:自己下棋.对方下棋.游戏暂停.悔棋等多个状态. 再比如 ...
- Unity3D游戏开发从零单排(四) - 制作一个iOS游戏
提要 此篇是一个国外教程的翻译,尽管有点老,可是适合新手入门. 自己去写代码.debug,布置场景,能够收获到非常多.游戏邦上已经有前面两部分的译文,这里翻译的是游戏的最后一个部分. 欢迎回来 在第一 ...
- DirectX12 3D 游戏开发与实战第四章内容(上)
Direct3D的初始化(上) 学习目标 了解Direct3D在3D编程中相对于硬件所扮演的角色 理解组件对象模型COM在Direct3D中的作用 掌握基础的图像学概念,例如2D图像的存储方式,页面翻 ...
- DirectX12 3D 游戏开发与实战第四章内容(下)
Direct3D的初始化(下) 学习目标 了解Direct3D在3D编程中相对于硬件所扮演的角色 理解组件对象模型COM在Direct3D中的作用 掌握基础的图像学概念,例如2D图像的存储方式,页面翻 ...
随机推荐
- [系统运维]Supervisord安装和启动程序
supervisord 是client/server 系统 把不是守护进程的进程变成守护进程 监控它自己启动的进程,类似于看门狗 可以作为开机启动的一种封装 可以精确控制进程的状态,而不是pidfil ...
- 网站开发进阶(四十四)input type="submit" 和"button"的区别
网站开发进阶(四十四)input type="submit" 和"button"的区别 在一个页面上画一个按钮,有四种办法: 这就是一个按钮.如果你不写ja ...
- Findbugs异常总汇
FindBugs是基于Bug Patterns概念,查找javabytecode(.class文件)中的潜在bug,主要检查bytecode中的bug patterns,如NullPoint空指针检查 ...
- velocity中加载模板文件的方式
velocity有多中种方式供我们去加载我们自定义的模板文件,下面详细的介绍使用的方法. 1.1.1. 加载classpath目录下的模板文件 使用classpath方式加载,是我们经常用到的一种方式 ...
- java中的interface接口
接口:java接口是一些方法表征的集合,但是却不会在接口里实现具体的方法. java接口的特点如下: 1.java接口不能被实例化 2.java接口中声明的成员自动被设置为public,所以不存在pr ...
- 学习TensorFlow,生成tensorflow输入输出的图像格式
TensorFLow能够识别的图像文件,可以通过numpy,使用tf.Variable或者tf.placeholder加载进tensorflow:也可以通过自带函数(tf.read)读取,当图像文件过 ...
- (一〇七)iPad开发之modal的切换方式与展示方式
在iPad上modal有四种切换方式,分别是竖直进入(由下到上,默认方式).水平翻转.淡入淡出. 属性要设置在将要modal出来的控制器上: /* typedef NS_ENUM(NSInteger, ...
- Java正则表达式小记
http://blog.csdn.net/pipisorry/article/details/51059500 正则表达式的一般规则都一样,见[python正则表达式] java正则表达式中的特殊字符 ...
- Android的Notification的简介-android学习之旅(四十一)
Notification简介 Notification位于手机饿最上面,用于显示手机的各种信息,包括网络状态,电池状态,时间等. 属性方法介绍 代码示例 package peng.liu.test; ...
- Win7/Win8/Win10下安装Ubuntu14.04双系统 以及常见问题
整理自网络. 1. 制作镜像 将ubantu镜像刻录到优盘(我使用UltraISO刻录,镜像下载地址:链接: http://pan.baidu.com/s/1bndbcGv 密码: qsmb) 2. ...