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. [CSDN_Markdown]使用LaTeX基本数学公式

    简介 以前我很头疼在博文里写公式,一直期盼CSDN的博文编辑器能支持LaTeX 公式输入,今天终于可以使用这个功能了!此文主要讨论如何在CSDN的Markdown编辑器中写 LaTeX 公式! 使用L ...

  2. Matplotlib Toolkits:地图绘制工具

    Matplotlib Toolkits:地图绘制工具 有没有一种可以直接在详细地图(如谷歌地图)上绘制上百万坐标点的工具???谷歌地图坐标点多了也不能绘制了. Basemap (Not distrib ...

  3. CentOS6.7下安装MySQL

    第一步:到MySQL官网上下载linux版本的MySQL rpm 安装包. 第二步: 将该压塑包解压后,有如下文件: 第三步:安装文件,我们需要安装的文件有 MySQL-server-5.6.26-1 ...

  4. OpenCV+python 人脸识别

    首先给大家推荐一本书:机器学习算法原理与编程实践 本文内容全部转载于书中,相当于一个读书笔记了吧 绪论 1992年麻省理工学院通过实验对比了基于结构特征的方法与基于模版匹配的方法,发现模版匹配的方法要 ...

  5. Centos6.5 mysql安装

    cenos中安装软件使用yum进行安装,小米加步枪不如ak47. 第1步.yum安装mysql   yum -y install mysql-server 第2步.设置开机启动   chkconfig ...

  6. SQL 数据库语言分析总结(二)

    介绍sql语言 我们接着一的顺序继续介绍这个语言 数据类型 整形: TINYINT(8位) SMALLINT(16位) MEDIUMINT(24位) INT(32位) BIGINT(64位) 实数: ...

  7. 04_关于元数据,ResultSetMetaData对象以及API方法介绍

     ResultSetMetaData对象 元数据,可以理解为数据的数据 Jdbc中的元数据是指数据库.表.列的定义信息. ResultSetMetaData对象表示结果集ResultSet对象的元 ...

  8. 【java线程系列】java线程系列之线程间的交互wait()/notify()/notifyAll()及生产者与消费者模型

    关于线程,博主写过java线程详解基本上把java线程的基础知识都讲解到位了,但是那还远远不够,多线程的存在就是为了让多个线程去协作来完成某一具体任务,比如生产者与消费者模型,因此了解线程间的协作是非 ...

  9. Android开发学习之路--Activity之初体验

    环境也搭建好了,android系统也基本了解了,那么接下来就可以开始学习android开发了,相信这么学下去肯定可以把android开发学习好的,再加上时而再温故下linux下的知识,看看androi ...

  10. Swift基础之:新的访问控制fileprivate和open

    (转载的,暂时没有研究过这类语句,有空看看) 在swift 3中新增加了两种访问控制权限 fileprivate和 open.下面将对这两种新增访问控制做详细介绍. fileprivate 在原有的s ...