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) (四)的更多相关文章

  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. Dynamics CRM2016 Web API之更新记录的单个属性字段值

    在web api中提供了对单个属性的更新接口,这和查询中查询单个属性类似,对这个接口我个人也是比较喜欢的. var id = "{D1E50347-86EB-E511-9414-ADA183 ...

  2. Android Multimedia框架总结(七)C++中MediaPlayer的C/S架构补充及MediaService介绍

    转载请把头部出处链接和尾部二维码一起转载,本文出自逆流的鱼,文章链接: http://blog.csdn.net/hejjunlin/article/details/52465168 前面一篇主要介绍 ...

  3. Hadoop:Hadoop单机伪分布式的安装和配置

    http://blog.csdn.net/pipisorry/article/details/51623195 因为lz的linux系统已经安装好了很多开发环境,可能下面的步骤有遗漏. 之前是在doc ...

  4. Unity UGUI图文混排源码(三) -- 动态表情

    这里是根据图文混排源码(二)进一步修改的,其他链接也不贴了,就贴一个链接就好了,第一次看这文章的同学可以先去看看其他几篇文章 Unity UGUI图文混排源码(二):http://blog.csdn. ...

  5. 14 Fragment 注意点

    API 过时问题 API 23过时 public void onAttach(Activity activity)替换为public void onAttach(Context context) 注意 ...

  6. hive的strict模式;where,group by,having,order by同时使用的执行顺序

    主要限制三种情况 (1) 有partition的表查询需要加上where子句,筛选部分数据实现分区裁剪,即不允许全表全分区扫描,防止数据过大 (2) order by 执行时只产生一个reduce,必 ...

  7. Android实现横屏以及全屏的小技巧

    分享两个安卓的实用小技巧,那就是横屏和全屏的实现. 首先是横屏的实现 首先是在清单文件中实现 <activity android:name=".MainActivity" a ...

  8. HTML5中 HTML格式化/HTML样式/链表/表格-样式 韩俊强的博客

    HTML5学习从简单到复杂,循环渐进! 每日更新关注:http://weibo.com/hanjunqiang  新浪微博! 1.HTML格式化 <!DOCTYPE html> <h ...

  9. iOS视图控制器初始化问题

    最近在群里见不少人 问到用视图控制器的alloc /init方法初始化的时候,出来的是黑色的空界面.之前我也遇到过,所以在这里总结下. 我们在项目中肯定都会用到自定义的ViewController,而 ...

  10. 使用GDAL库中的RPC校正问题

    最近将GDAL库更新至1.11版本之后,发现之前写的RPC像方改正模型校正的结果偏差特别大(更新版本之前结果和PCI处理的结果一致).所以初步判断是GDAL库的bug,经过各个参数修改发现原来是指定的 ...