Cocos2d-x game development-Finite State Machine (FSM) (4), cocos2d-xfsm
Cocos2d-x game development-Finite State Machine (FSM) (4)
Although we understand FSM and can write our own FSM, there are better tools to help us complete this tedious task. SMC (http://smc.sourceforge.net/) is such a tool. :
Http://sourceforge.net/projects/smc/files/latest/download
Smc. jar under bin is a command line tool used to generate status classes. Run the following command:
$ java -jar Smc.jar Monkey.sm
1. FSM in the real world
First, define a state machine plain text file: Monkey. sm. The content is as follows:
// cheungmine// 2015-01-22// entity class%class Monkey// entity class header%header Monkey.h// inital state%start MonkeyMap::STOP// entity state map%map MonkeyMap%%STOPEntry {stop();}Exit {exit();}{walk WALK {}}WALKEntry {walk();}Exit {exit();}{stop STOP {}turn TURN {}}TURNEntry {turn();}Exit {exit();}{walk WALK {}}%%
Here, % class Monkey indicates the object class name: Monkey (Monkey. h and Monkey. cpp)
% Header specifies the header file: Monkey. h
% Map indicates the status chart class, which contains all States. Here is: MonkeyMap
% Start indicates the actual status. Here is STOP, and the corresponding class is MonkeyMap_STOP.
The section between %... % defines each status. The format is as follows:
STOP // status name Entry {// execute this function to enter this status stop ();} Exit {// execute this function to exit this status Exit exit ();} {// status switching logic walk {}}
When you run the following command, the system automatically generates the files Monkey_sm.h and Monkey_sm.cpp. Add the built-in statemap. h to the project.
java -jar Smc.jar Monkey.sm
2. entity class
We still need to implement the business logic by ourselves, that is, write Monkey. h and Monkey. cpp. However, this write of the Monkey class requires certain rules. The source code is as follows:
// 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 200class 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 ???? It is the callback function that triggers the state. The business logic of the entity state change is implemented here.
// 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. State Machine
The frame code Smc has been generated for us: Monkey_sm.h and 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. Summary
FSM is a fixed paradigm, so using tools to help us reduce the chances of making mistakes. The input file is entity. sm. We focus on the business logic, so status-related code smc has been generated for us. Compare the manually created classes and automatically generated classes by the smc Framework tool:
Using in cocos2d-x is simple:
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;}
That's it! Read the following carefully:
Cocos2d-x game development tour (Zhong Dilong)