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

【木头Cocos2d-x 023】状态机篇(第02章) --状态模式之我很胖但我很强!

 
阅读更多

Cocos2d-x状态机篇】第02--状态模式之我很胖但我很强!

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

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

正文:

状态机的应用当然就少不了状态模式了,因为它们都有“状态”两个字。

(旁白:总感觉这句话十分不可靠==

本章我们来简单地介绍一下状态模式,并且利用状态模式优化我们的有限状态机的实现。

1.什么是状态模式

说实话,我不知道,但我知道怎么使用...

用一句话来概括,也许是这样:同一件事情,让不同的人去做,每个人都会用自己的方式来做事,但,同一时间,只会让一个人在做这件事情。由老板来决定某一刻由谁来做事。

(旁白:又你可爱的妹纸的,这明明是两句话,两个句话有没有==

用程序来说,也许是这样:同一个动作,让不同的类去执行,每个类都有自己独特的处理方式,至于由哪个类来执行这个动作,要根据当前的状态来判断。并且,不同的类执行完动作之后,会切换状态。

(旁白:还是没听懂。。。)

OK,我相信大家都明白了什么是状态模式了,接下来我们把第01章的例子稍微改改,很简单的~

(旁白:等等,我压根儿就没搞懂什么是状态模式啊!)

好吧,我画了一个图,帮助大家理解:

图中有一个对象(蓝色方块),有三个状态处理类。

比如我有一件事情要做,那就是,起床之后要做的事情,但是由哪个类来执行这件事情呢?这要根据我的当前状态来决定(我有三种状态:肚子饿,想看电影,想吐槽),默认的状态是肚子饿。

然后,今天起床,我的状态是“肚子饿”。那我就会自动调用小木厨师来执行事件,执行完之后,状态会切换到“想看电影”。

那么,第二天,我起床的状态就是“想看电影”。那我就会自动调用电影院来执行事件,执行完之后,状态会切换到“想吐槽”。

第三天,我起床的状态就是“想吐槽”。那我就会自动调用吐槽旁白来执行事件,执行完之后,状态会切换到“肚子饿”。

第四天,我起床之后的状态就是“肚子饿”。那我就会自动调用小木厨师来执行事件,执行完之后,状态切换到“想看电影”。

第五天,我起床...旁白:停~!!!你想说到天亮吗?!)

其实这个例子有点不准确,甚至是十分不准确,但是对于理解状态模式还是有点帮助了。

现在,大家在接着往下看之前,请确认你已经大概了解状态模式(起码去百度过,看过一篇正式介绍状态模式的文章)。因为接下来的代码和上面那张图的差别还是不小的,如果对状态模式不理解,就会产生混乱。

那么,用什么方法实现自动调用哪个类呢?当然是多态了,小木厨师、电影院、吐槽旁白这三个类都继承了同一个父类,它们就是我们要说的状态类了。

2.用状态模式实现有限状态机

还记得上一章的Mutou类吗?兴趣爱好很广泛的那个~(旁白:我不想吐槽了...大家应该对它的update函数印象很深刻吧?那我就不贴出来了:

void Mutou::update( float dt ) {
    /* 判断在每一种状态下应该做什么事情 */
    switch(enCurState) {
    case enStateWriteCode:
        /* 如果累了就休息,并且切换到休息状态 */
        if(isTire()) {
            rest();
            changeState(enStateRest);
        }
        break;
    case enStateWriteArticle:
        /* 如果累了就休息,并且切换到休息状态 */
        if(isTire()) {
            rest();
            changeState(enStateRest);
        }
        break;
    case enStateRest:
        /* 一定的概率写代码,一定的概率写教程,并且切换到相应的状态 */
        if(isWantToWriteArticle()) {
            writeArticle();
            changeState(enStateWriteArticle);
        }
        else {
            writeCode();
            changeState(enStateWriteCode);
        }
        break;
    }
}


update函数会先判断木头当前所在的状态,然后再去执行相对应的逻辑。

(旁白:你刚刚说你不贴的代码的...

那个旁白什么的,你刚刚也说你不想吐槽啊~

但是啊,要是木头有好多好多状态,那这个函数也太庞大了~!是的,我们现在就要用到状态模式了,很简单的,别走神咯~

3.状态基类

首先,我们需要一个状态基类,它只有一个抽象方法execute,表示要执行一件事情。至于执行什么事情,由它的子类来决定。

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

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

class MutouT;

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

#endif


4三种状态对应的状态类

接下来,我们要实现最核心的类:状态类。

木头有3种状态:写代码、写教程、休息。

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

    创建日期:   2012.12.17
*/
#ifndef __STATE_WRITE_CODE_H__
#define __STATE_WRITE_CODE_H__

#include "I_State.h"

class MutouT;
class StateWirteCode : public I_State {
public:
    virtual void execute( MutouT* mutou );
};

#endif

/* cpp文件 */
#include "StateWirteCode.h"
#include "StateRest.h"
#include "MutouT.h"

void StateWirteCode::execute( MutouT* mutou ) {
    /* 如果累了就休息,并且切换到休息状态 */
    if(mutou->isTire()) {
        mutou->rest();
        mutou->changeState(new StateRest());
    }
}


上面这个就是写代码状态类,它继承了I_State,拥有一个execute方法,它表示,在写代码状态下执行一个动作。

那么,接下来,另外两种状态的类也是一样的,唯一不同的就是execute的具体实现。

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

    创建日期:   2012.12.17
*/
#ifndef __STATE_WRITE_ARTICLE_H__
#define __STATE_WRITE_ARTICLE_H__

#include "I_State.h"

class MutouT;
class StateWriteArticle : public I_State {
public:
    virtual void execute( MutouT* mutou );
};

#endif

/* cpp文件 */
#include "StateWriteArticle.h"
#include "StateRest.h"
#include "MutouT.h"

void StateWriteArticle::execute( MutouT* mutou ) {
    /* 如果累了就休息,并且切换到休息状态 */
    if(mutou->isTire()) {
        mutou->rest();
        mutou->changeState(new StateRest());
    }
}


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

    创建日期:   2012.12.17
*/
#ifndef __STATE_REST_H__
#define __STATE_REST_H__

#include "I_State.h"

class MutouT;
class StateRest : public I_State {
public:
    virtual void execute( MutouT* mutou );
};

#endif

/* cpp文件 */
#include "StateRest.h"
#include "StateWriteArticle.h"
#include "StateWirteCode.h"
#include "MutouT.h"

void StateRest::execute( MutouT* mutou ) {
    /* 一定的概率写代码,一定的概率写教程,并且切换到相应的状态 */
    if(mutou->isWantToWriteArticle()) {
        mutou->writeArticle();
        mutou->changeState(new StateWriteArticle());
    }
    else {
        mutou->writeCode();
        mutou->changeState(new StateWirteCode());
    }
}


5.新的木头类

为了使用状态模式,Mutou类要修改,先看看头文件:

#ifndef __MUTOU_T_H__
#define __MUTOU_T_H__

#include "cocos2d.h"
USING_NS_CC;

class I_State;
class MutouT : public CCNode {
public:
    CREATE_FUNC(MutouT);
    virtual bool init();

    bool isTire();                      /* 判断是否写代码写累了 */
    bool isWantToWriteArticle();        /* 是否想写教程 */
    void writeCode();                   /* 写代码 */
    void writeArticle();                /* 写教程 */
    void rest();                        /* 休息 */
    void changeState(I_State* state);   /* 切换状态 */

    virtual void update(float dt);

private:
    /* 存放当前状态类 */
    I_State* mCurState;
};
#endif


和以前的代码区别并不大,有两处修改:

1.changeState的参数变了,变成了I_State,同时,表示状态的枚举类也删掉了,因为已经有了状态类。

2.多了一个I_State成员变量,表示当前的状态,同时,表示状态的枚举变量删掉了,因为已经有了状态类。

那么,我们之前的逻辑几乎没有什么变化,依旧是在木头的update函数里做文章,只不过,这次的update函数可就简单多了~~~

(旁白:真的吗?倒是有点兴趣~

#include "MutouT.h"
#include "I_State.h"

bool MutouT::init() {
    mCurState = NULL;
    this->scheduleUpdate();
    return true;
}

bool MutouT::isTire() {
    /* 每次问木头累不累,他都会说:累~ */
    return true;
}

bool MutouT::isWantToWriteArticle() {
    /* 有10%的概率想写教程(好懒~!) */
    float ran = CCRANDOM_0_1();
    if(ran < 0.1f) {
        return true;
    }

    return false;
}

void MutouT::writeCode() {
    CCLOG("mutou is wirting Code.");
}

void MutouT::writeArticle() {
    CCLOG("mutou is writing article.");
}

void MutouT::rest() {
    CCLOG("mutou is resting.");
}

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

    mCurState = state;
}

void MutouT::update( float dt ) {
    mCurState->execute(this);
}


前面几乎没有变,看看最后那个:

voidMutouT::update(floatdt){

mCurState->execute(this);

}

这个就是新的update函数的处理了,是不是很简单?

先看看效果再解释,修改HelloWorldinit函数:

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

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

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

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

    return bRet;
}


和以前差不多,只是把Mutou类换成新的MutouT类。

(旁白:MutouT?就是MutouTow的意思?也就是Mutou2?噗,是的,木头是挺2~

然后用调试模式运行项目,将看到以下输出:

mutouiswirtingCode.

mutouisresting.

mutouiswritingarticle.

mutouisresting.

mutouiswritingarticle.

mutouisresting.

mutouiswirtingCode.

mutouisresting.

mutouiswirtingCode.

mutouisresting.

mutouiswirtingCode.

OK,和以前一样的效果。

稍微解释一下,其实这就是简单地利用了多态。神奇的地方就在execute这个函数,这个函数里面会根据不同的情况切换MutouT的当前状态,比如写教程状态的execute函数:

void StateWriteArticle::execute( MutouT* mutou ) {
    /* 如果累了就休息,并且切换到休息状态 */
    if(mutou->isTire()) {
        mutou->rest();
        mutou->changeState(new StateRest());
    }
}


如果累了,就切换到休息状态,那么,执行完这个函数之后,MutouT的状态其实就已经改变了,变成了休息状态,那么下一次的update函数就会调用休息状态的execute函数。以此类推。

看一个图,会不会好理解一些?

好吧,我承认我解释得有点糟糕,希望大家能看明白...

(旁白:我已经晕了。。。)

分享到:
评论

相关推荐

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

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

    cocos2d-x-2.1.5

    cocos2d-x-2.1.5

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

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

    cocos2d-x事件类

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

    Cocos2d-x高级开发教程

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

    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-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 状态机篇】第03章源码

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

    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 3.0

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

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

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

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

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

    Cocos2d-x-3.x游戏开发之旅

    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 3.x游戏开发实战pdf含目录

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

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

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

Global site tag (gtag.js) - Google Analytics