`
mmdev
  • 浏览: 12915807 次
  • 性别: Icon_minigender_1
  • 来自: 大连
文章分类
社区版块
存档分类
最新评论

【木头Cocos2d-x 025】状态机篇(第04章) --事件驱动,你想象不到的强大

 
阅读更多

Cocos2d-x状态机篇】第04章--事件驱动,你想象不到的强大

笨木头花心贡献,啥?花心?不呢,是用心~

转载请注明,原文地址
http://blog.csdn.net/musicvs/article/details/8349314

正文:

到现在为止,我们已经有能力实现简单的有限状态机了,但是,大家有没有发现一个问题?

(旁白:貌似我不止发现一个问题==

那就是,我们必须主动地在update函数里检测状态的变化,但是,并不是每时每刻对象的状态都会改变的。这种主动检测状态改变的方式,其实十分不妥当,这会造成CUP的压力比较大。

(旁白:是CPU吧?拜托~!)

那么,现在我要向大家介绍一种十分美妙的技术,这无论是在应用开发还是游戏开发里,都是十分有用的。那就是,事件驱动。

很幸运,在Cocos2d-x里,已经集成了实现事件驱动的功能了,那就是CCNotificationCenter类,我更喜欢称之为“消息派发”。我建议对这方面不熟悉的朋友,先百度一下“观察者模式”、“订阅者”等关键词,以及了解关于CCNotificationCenter的使用。

那么,现在,我们要把我们第02章里的程序改为消息驱动的形式了~

(旁白:废话终于结束了。。。)

1.彻底抛弃update函数,新的状态机

/*
    文件名:    MutouTFSM.h
    描 述:    木头对象的状态机,用来管理状态
    创建人:    笨木头 (CSDN博客:http://blog.csdn.net/musicvs)

  创建日期:   2012.12.19
    修改日期:   2012.12.20
*/
#ifndef __MUTOUT_FSM_H__
#define __MUTOUT_FSM_H__

#include "cocos2d.h"
USING_NS_CC;

class I_State;
class MutouT;

class MutouTFSM : public CCNode {
public:
    ~MutouTFSM();
        
    static MutouTFSM* createWithMutouT(MutouT* mutou);
    bool initWithMutouT(MutouT* mutou);
    
    void changeState(I_State* state);   /* 切换状态 */

private:
    void onRecvWantToRest(CCObject* obj);
    void onRecvWantToWriteCode(CCObject* obj);
    void onRecvWantToWriteArticle(CCObject* obj);

    /* 存放当前状态类 */
    I_State* mCurState;

    /* 木头对象 */
    MutouT* mMutou;
};

#endif

这是新的状态机类,发现什么不一样了吗?

(旁白:不就是update函数删掉了,然后多了一个析构函数么==很特别吗?)

这其实不是重点(可恶的旁白竟然答对了),重点看看cpp文件:

#include "MutouTFSM.h"
#include "MutouT.h"
#include "I_State.h"
#include "EnumMsgType.h"

#define NOTIFY CCNotificationCenter::sharedNotificationCenter()

MutouTFSM::~MutouTFSM() {
    NOTIFY->removeObserver(this, "wantToRest");
    NOTIFY->removeObserver(this, "wantToWriteCode");
    NOTIFY->removeObserver(this, "wantToWriteArticle");
}

MutouTFSM* MutouTFSM::createWithMutouT( MutouT* mutou ) {
    MutouTFSM* fsm = new MutouTFSM();

    if(fsm && fsm->initWithMutouT(mutou)) {
        fsm->autorelease();
    }
    else {
        CC_SAFE_DELETE(fsm);
        fsm = NULL;
    }

    return fsm;
}

bool MutouTFSM::initWithMutouT( MutouT* mutou ) {
    this->mCurState = NULL;
    this->mMutou = mutou;
    mMutou->retain();

    /* 订阅消息 */
    NOTIFY->addObserver(this,callfuncO_selector(MutouTFSM::onRecvWantToRest) , "wantToRest", NULL);
    NOTIFY->addObserver(this,callfuncO_selector(MutouTFSM::onRecvWantToWriteCode) , "wantToWriteCode", NULL);
    NOTIFY->addObserver(this,callfuncO_selector(MutouTFSM::onRecvWantToWriteArticle) , "wantToWriteArticle", NULL);
    return true;
}

void MutouTFSM::changeState( I_State* state ) {
    CC_SAFE_DELETE(mCurState);

    this->mCurState = state;
    
}
void MutouTFSM::onRecvWantToRest( CCObject* obj ) {
    this->mCurState->execute(mMutou, en_Msg_WantToRest);
}

void MutouTFSM::onRecvWantToWriteCode( CCObject* obj ) {
    this->mCurState->execute(mMutou, en_Msg_WantToWriteCode);
}

void MutouTFSM::onRecvWantToWriteArticle( CCObject* obj ) {
    this->mCurState->execute(mMutou, en_Msg_WantToWriteArticle);
}

首先,为了避免代码过长,我做了一个宏定义:

#defineNOTIFYCCNotificationCenter::sharedNotificationCenter()

我们来看看,状态机总共订阅了3个消息:wangToRestwantToWriteCodewantToWriteArticle。为了偷懒,我直接把字符串硬编码了。

再来看看收到消息后,状态机是怎么处理的:

void MutouTFSM::onRecvWantToRest( CCObject* obj ) {
    this->mCurState->execute(mMutou, en_Msg_WantToRest);
}

很简单,很以前一样,调用当前状态的execute方法,但这次多了一个参数,那就是消息类型。看看消息类型的枚举:

#ifndef __ENUM_MSG_TYPE_H__
#define __ENUM_MSG_TYPE_H__

enum EnumMsgType {
    en_Msg_WantToRest,
    en_Msg_WantToWriteCode,
    en_Msg_WantToWriteArticle,
};

#endif

(旁白:感觉好麻烦==

大家没有觉得这样好麻烦吗?

(旁白:你就忽略我吧,我习惯了。。。)

其实我也觉得好麻烦,但是Cocos2d-xCCNotificationCenter只能这么用,它是使用函数指针的方式来回调进行消息发布,而不是使用接口。所以状态机每订阅一个消息就要多写一个函数来接收消息。

更可恶的是,订阅消息的时候,消息类型只能用字符串,不能用枚举值~!所以状态机在传递事件给状态类的时候,只能再建一套枚举类型了。如果大家有更好的方法记得和我分享~

2.状态类做小改动

然后,状态类的execute函数也要增加一个参数,就是消息类型:

/*
    文件名:    State.h
    描 述:    状态基类
    创建人:    笨木头 (CSDN博客:http://blog.csdn.net/musicvs)

    创建日期:   2012.12.17
    修改日期:   2012.12.20
*/
#ifndef __I_STATE_H__
#define __I_STATE_H__

#include "EnumMsgType.h"

class MutouT;

class I_State {
public:
    virtual void execute(MutouT* mutou, EnumMsgType enMsgType) = 0;
};

#endif


很简单,不解释了,其它的状态子类也要做相应的修改。

然后,我们来看看3种状态类的execute函数的新实现:

void StateRest::execute( MutouT* mutou, EnumMsgType enMsgType ) {
    switch(enMsgType) {
    case en_Msg_WantToWriteCode:
        mutou->writeCode();
        mutou->getFSM()->changeState(new StateWirteCode());
        break;
    case en_Msg_WantToWriteArticle:
        mutou->writeArticle();
        mutou->getFSM()->changeState(new StateWriteArticle());
        break;
    }
}

void StateWriteArticle::execute( MutouT* mutou, EnumMsgType enMsgType ) {
    switch(enMsgType) {
    case en_Msg_WantToRest:
        mutou->rest();
        mutou->getFSM()->changeState(new StateRest());
        break;
    }
}

void StateWirteCode::execute( MutouT* mutou, EnumMsgType enMsgType ) {
    switch(enMsgType) {
    case en_Msg_WantToRest:
        mutou->rest();
        mutou->getFSM()->changeState(new StateRest());
        break;
    }
}


看到了吗?

(旁白:看到了,看到了==你快点说吧,别这么唠叨了。。。)

每个状态类的execute函数已经不需要再主动去判断木头当前在做什么或者想做什么或者是什么状况。它只要知道当前发生了什么事件,它只关心它所关心的事件,在特定的事件下改变木头的状态即可。

3.测试新代码

是时候测试一下了,继续修改HelloWorldinit函数:

bool HelloWorld::init()
{
    bool bRet = false;
    do 
    {
        CC_BREAK_IF(! CCLayer::init());

        /* 新建木头2角色 */
        mMutou = MutouT::create();

        /* 初始化木头的状态为休息 */
        mMutou->getFSM()->changeState(new StateRest());

        /* 模拟事件的发生 */
        NOTIFY->postNotification("wantToWriteCode");
        NOTIFY->postNotification("wantToRest");
        NOTIFY->postNotification("wantToWriteArticle");
        NOTIFY->postNotification("wantToRest");
        NOTIFY->postNotification("wantToWriteArticle");
        NOTIFY->postNotification("wantToRest");
        NOTIFY->postNotification("wantToWriteCode");
        NOTIFY->postNotification("wantToRest");

        this->addChild(mMutou);
        bRet = true;
    } while (0);

    return bRet;
}


由于事件的发生是随机的,要产生这些随机的事件,可不是一两段代码就能搞定的,所以,我直接用最简单的方式,主动发送这些事件消息。

然后,用debug模式运行项目,我们将看到熟悉的日志:

mutouiswirtingCode.

mutouisresting.

mutouiswritingarticle.

mutouisresting.

mutouiswritingarticle.

mutouisresting.

mutouiswirtingCode.

mutouisresting.

4.最后的最后

好了,我想,我对有限状态机的介绍就到这里了,其实我对有限状态机的理解也不是十分深刻,我也是现学现卖,一方面是巩固自己的知识,另一方面我确实很喜欢和大家分享知识。

也许通过这四篇文章的介绍大家还是无法感受到状态机倒底有多强大,但我可以肯定地告诉大家,它真的很强大。比如,我描述这样一个场景:

一个拥有超能力的木头,它所经过的地方周围的物体都会弹开,于是,它一遍走,周围的物体一遍弹。十分美妙的场景~

(旁白:美你个木头==。。。)

这依旧可以使用有限状态机来实现,木头经过的时候可以发出“我来了,我的能力范围是方圆10米”,然后周围的物体订阅了这个消息,在接收到这个消息的时候,就由静止状态改变为弹射状态,然后物体就执行弹射动作。而这一切看起来就像是自动完成的。

好了,已经对状态机理解深刻的朋友,希望能给我一些建议,对状态机不了解的朋友,希望能帮到你们。好吧,旁白出来清场~

(旁白:清你可爱的妹纸的==

(旁白:OK,大家排好队,一个接一个地离场...额,我为毛要帮他清场啊~!)

本章项目源码:http://download.csdn.net/detail/musicvs/4912216

分享到:
评论

相关推荐

    Cocos2d-x实战:JS卷——Cocos2d-JS开发

    资源名称:Cocos2d-x实战:JS卷——Cocos2d-JS开发内容简介:本书是介绍Cocos2d-x游戏编程和开发技术书籍,介绍了使用...基础篇包括第2章~第8章,Cocos2d- 资源太大,传百度网盘了,链接在附件中,有需要的同学自取。

    cocos2d-x事件类

    在使用cocos2d-x开发游戏的过程中,为了实现逻辑和显示相分离。 在下通宵了一个晚上,写出了该事件类。 谨记,该事件只能用于cocos2d-x中。 事件发送者需要继承EventDispatcher类 事件接收者需要继承EventHandle类...

    Cocos2d-x游戏编程——C++篇 .iso

    Cocos2d-x游戏编程——C++篇(电子工业出版社,徐飞 著)书本配套的光盘代码,

    cocos2d-x-2.1.5

    cocos2d-x-2.1.5

    【Cocos2d-x 状态机篇】第04章源码

    【Cocos2d-x 状态机篇】第04章--事件驱动,你想象不到的强大 .教程源码. 教程地址: http://blog.csdn.net/musicvs/article/details/8349314

    经典版本 方便下载 源码 旧版本 3.8 官网找不到了 cocos2d-x-3.8.zip

    经典版本 方便下载 源码 旧版本 3.8 官网找不到了 cocos2d-x-3.8.zip

    Cocos2d-x高级开发教程

    Cocos2d-x是移动跨平台开发最流行的游戏引擎,而本书是一本很全面的、比较‘接地气’的游戏开发教程。书中汇聚了热门手机游戏《捕鱼达人》开发的实战经验,作者从最基础的内容开始,逐步深入地介绍了Cocos2d-x的相关...

    大富翁手机游戏开发实战基于Cocos2d-x3.2引擎

    资源名称:大富翁手机游戏开发实战基于Cocos2d-x3.2引擎内容简介:李德国编著的《大富翁手机游戏开发实战(基于 Cocos2d-x3.2引擎)》使用Cocos2d-x游戏引擎技术,带领读者一步一步从零开始进行大富翁移动游戏的开发...

    cocos2d-x-3.2旧版引擎下载

    cocos2d-x-3.2下载,不多说。或者可以下载另一个资源 cocos引擎老版本集合(cocos2d-x-2.2.1 - 3.5) http://download.csdn.net/download/crazymagicdc/9982656

    cocos2d-x实战项目

    cocos2d-x实战项目 01.cocos2d-x原理及环境配置.rar 03.cocostudio使用方法及UI控制.rar 04.XML文件读取与骨骼动画.rarcocos2d-x实战项目 01.cocos2d-x原理及环境配置.rar 03.cocostudio使用方法及UI控制.rar 04.XML...

    Cocos2D-X游戏开发技术精解

    资源名称:Cocos2D-X游戏开发技术精解内容简介:Cocos2D-X是一款支持多平台的 2D手机游戏引擎,支持iOS、Android、BlackBerry等众多平台。当前,很多移动平台流行的游戏,都是基于Cocos2D-X开发的。 《Cocos2D-X...

    cocos2d-x-3.0 类图

    这是我重新弄的cocos2d-x-3.0的类图.之前别人兄台弄的,有些不全面,有些地方错误.我这个可以说是最新的了.每个类添加了中文的详细注解,同时也添加了中文的类名称翻译.这样对cocos2d-x-3.0的框架比较好上手. 有兴趣的...

    精通COCOS2D-X游戏开发 基础卷_2016.4-P399-13961841.pdf

    精通COCOS2D-X游戏开发 精通COCOS2D-X游戏开发 精通COCOS2D-X游戏开发 精通COCOS2D-X游戏开发 精通COCOS2D-X游戏开发

    【Cocos2d-x 状态机篇】第03章源码

    【Cocos2d-x 状态机篇】第03章--真正的状态机来了~! .教程源代码 教程地址:http://blog.csdn.net/musicvs/article/details/8348353

    Cocos2D-X游戏开发技术精解.pdf

    《Cocos2D-X游戏开发技术精解》详细介绍如何使用Cocos2D-X引擎开发自己的移动平台游戏。全书共15章,主要内容包括:Cocos2D-X引擎简介;如何建立跨平台的开发环境;引擎的核心模块——渲染框架;如何实现动态画面和...

    cocos2d-x 3.0

    cocos2d-x 3.0 人物行走 . 包里有代码和 图片资源.

    Cocos2d-x 3.x游戏开发实战pdf含目录

    Cocos2d-x 3.x游戏开发实战pdf含目录,内容详细,强烈推荐给大家。

    cocos2d-x 动画工具 Flash2Cocos2d-x 1.3

    cocos2d-x 动画工具 Flash2Cocos2d-x 1.3

    cocos2d-x windows vs2010配置

    Cocos2d-x windows vs2010 配置图文详解

    Cocos2d-x实战 JS卷 Cocos2d-JS开发

    Cocos2d-x实战 JS卷 Cocos2d-JS开发 PDF 电子书完整版本

Global site tag (gtag.js) - Google Analytics