这里是Evankaka的博客,欢迎大家前来讨论与交流~~~~~~
转载请注明出处http://blog.csdn.net/evankaka
本文主要详细讲解了设计模式中的观察都模式及其在Coco2d-x的简单应用,最后,结合游戏中的技能冷却类放大招进行了一个使用。
cocos2d-x版本:2.2.5
工程环境:windows7+VS2010
打开方式:将工程放在cocos2d-x安装目录下的project文件夹下用VS打开
本文效果:
观察者模式(有时又被称为发布/订阅模式)是软件设计模式的一种。在此种模式中,一个目标对象管理所有相依于它的观察者对象,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实作事件处理系统。我的另一篇博文,24天学会设计模式------观察者模式,这里有更加详细的介绍,有兴趣可以看这里。
在开发游戏的时候我们经常需要在层与层之间、场景与场景之间传递数据和消息,Cocos2dx框架应用观察者模式为我们封装了一CCNotificationCenter类,也叫消息通知中心,它也是一个单例类。
从观察者模式来讲,CCNotificationCenter类是观察者模式中的目标对象(主题),而CCNotificationObserver则是观察者。一个目标对象可以注册多个观察者,当目标对象的状态改变的时候,可以通知观察者对象作出相应的反应,这是标准的观察者模式的实现。但是CCNotificationCenter稍微有些许差别,CCNotificationCenter不是通过自身状态改变来通知观察者,而是通过显式地发送观察者感兴趣的消息来通知它们,消息名称则是用来标识观察者是否感兴趣。每次消息传递给CCNotificationCenter,CCNotificationCenter就会遍历所有的观察者,找到注册了该消息标识符的观察者,然后将消息发送给它们。
实现步骤:
在任何地方,只要你你对某个消息感兴趣(和pureMVC中的listNotification一样),你就可以在那里监听该消息。
void addObserver(CCObject* target,SEL_CallFuncO callBack,const char* name, CCObject* data);
参数1为事件监听的目标,参数2为回调函数(即接收到消息后执行的函数),参数3为消息名,参数4为消息体。
具体实现如下:
CCNotificationCenter::sharedNotificationCenter()->addObserver(this,
callfunco_selector(GameLayer::callBack),
name,
NULL
void postNotification(const char* name); void postNotification(const char* name,CCObject* data);
name是消息名,是消息唯一标识,在整个游戏过程中是唯一的,因此,我们一般把所有的消息名放在一个头文件中,纺织消息名重复,data是消息体,即发送的数据。
发送通知如下:
CCNotificationCenter::sharedNotificationCenter()->postNotification(name);
释放消息观察者是很重要的,不释放的话,会产生内存泄露。我们需要在析构函数方法里面,释放消息观察者。
CCNotificationCenter::sharedNotificationCenter()->removeObserver(
this,
name //消息名
}
首先,新建一个HelloWord的工程,然后HelloWorldScene.h添加
<span style="font-size:18px;"> //添加目标通知观察者之后调用的事件 void ObserverFunction(CCObject * object);</span>打开HelloWorldScene.cpp,在init()函数添加
//注册Message,如果接收到了,执行ObserverFunction CCNotificationCenter::sharedNotificationCenter()->addObserver(this,callfuncO_selector(HelloWorld::ObserverFunction),"Message",NULL);然后HelloWorld::~HelloWorld()添加:
//注意最后一定要释放消息,否则内容泄露 CCNotificationCenter::sharedNotificationCenter()->removeObserver(this,"Message");再实现函数:
//添加目标通知观察者之后调用的事件 void HelloWorld::ObserverFunction(CCObject * object) { CCLOG("You Click menuButton"); }
//第一个参数是消息的名字,第二个参数是CCObject * 类型的消息值,也就是你要发送的东西 CCNotificationCenter::sharedNotificationCenter()->postNotification("Message",NULL);
观察都模式不仅可以通知更新,也可以传递数据 ,下面来看看第2个例子
(1)首先,还是新建一个HelloWord工程,然后新建一个层类 SecondLayer
头文件 SecondLayer.h
<span style="font-size:18px;">#ifndef __SecondLayer_SCENE_H__ #define __SecondLayer_SCENE_H__ #include "cocos2d.h" class SecondLayer : public cocos2d::CCLayer { public: virtual bool init(); ~SecondLayer(); static cocos2d::CCScene* scene(); CREATE_FUNC(SecondLayer); //添加目标通知观察者之后调用的事件 void ObserverFunction(CCObject * object); }; #endif // __SecondLayer_SCENE_H__</span>
实现文件SecondLayer.cpp
#include "SecondLayer.h" USING_NS_CC; CCScene* SecondLayer::scene() { CCScene *scene = CCScene::create(); SecondLayer *layer = SecondLayer::create(); scene->addChild(layer); return scene; } SecondLayer::~SecondLayer() { //注意最后一定要释放消息,否则内容泄露 CCNotificationCenter::sharedNotificationCenter()->removeObserver(this,"Message"); } bool SecondLayer::init() { if ( !CCLayer::init() ) { return false; } //注册Message,如果接收到了,执行ObserverFunction CCNotificationCenter::sharedNotificationCenter()->addObserver(this,callfuncO_selector(SecondLayer::ObserverFunction),"Message",NULL); return true; } //添加目标通知观察者之后调用的事件 void SecondLayer::ObserverFunction(CCObject * object) { CCLOG("SecondLayer Receive num=%d",(int)object); }
HelloWorldScene.cpp中添加头文件 #include "SecondLayer.h"
init()函数中添加:
SecondLayer* second=SecondLayer::create(); this->addChild(second,0);改写项目menu按钮的事件:
void HelloWorld::menuCloseCallback(CCObject* pSender) { int num=CCRANDOM_0_1()*1000;//0-1000的随机数 CCNotificationCenter::sharedNotificationCenter()->postNotification("Message",(CCObject *)num);//向观察者传递数据 }(3)效果:
看到了吧,SenondLayer类可以收到目标发给它的消息了啦!
前面我们设计了一个技能冷却的类,不懂看这里Cocos2d-x技能冷却还要等多久?---之游戏开发《赵云要格斗》(9),它是一个层类,现在我们要实现我们一旦按下特殊技能的按钮,赵云就要释放特殊技能(放大招),这里我的思路是在英雄类(即赵云)中设置成一个观察者,一旦观察到按下了特殊技能的按钮,而且技能不在冷却,那么赵云就停下所有动作,直接放大招。
英雄类头文件 Hero.h中添加
//技能特效 void SkillAmiation(CCObject * object);//注意要有参数,因为是观察者模式的调用函数,要不会提示“类型转换”: 无法从“void (__thiscall Hero::* )(void)”转换为“cocos2d::SEL_CallFuncO”英雄类实现文件 Hero.cpp中初始化英雄InitHeroSprite(char *hero_name)函数中添加
//注册MessageSkill,如果接收到了,执行SkillAmiation CCNotificationCenter::sharedNotificationCenter()->addObserver(this,callfuncO_selector(Hero::SkillAmiation),"MessageSkill",NULL);
一旦观察到技能按钮按下,调用函数:
//技能特效 void Hero::SkillAmiation(CCObject * object) { m_HeroSprite->stopAllActions();//当前精灵停止所有动画 CCAnimation* animation = CCAnimation::create(); for( int i=1;i<=6;i++) { char szName[100] = {0}; sprintf(szName,"skill_%d.png",i); animation->addSpriteFrameWithFileName(szName); //加载动画的帧 } animation->setDelayPerUnit(0.1f); animation->setRestoreOriginalFrame(true); animation->setLoops(5); //动画循环 //将动画包装成一个动作 CCAnimate* act=CCAnimate::create(animation); //创建回调动作,攻击结束后调用AttackEnd() CCCallFunc* callFunc=CCCallFunc::create(this,callfunc_selector(Hero::AttackEnd)); //创建连续动作 CCActionInterval* skillattack=CCSequence::create(act,callFunc,NULL); IsAttack=true; m_HeroSprite->runAction(skillattack); }最后记得要释放:
Hero::~Hero(void) { //注意最后一定要释放消息,否则内容泄露 CCNotificationCenter::sharedNotificationCenter()->removeObserver(this,"MessageSkill"); }然后在 SkillButton.cpp的BeginSkill()函数中,这个类可以看 Cocos2d-x技能冷却还要等多久?---之游戏开发《赵云要格斗》(9)
//第一个参数是消息的名字,第二个参数是CCObject * 类型的消息值,也就是你要发送的东西 CCNotificationCenter::sharedNotificationCenter()->postNotification("MessageSkill",NULL);
实现了目标对象和观察者之间的抽象耦合,在本例中,则是实现了消息与观察者的抽象耦合。可以定义一种消息与消息处理对象的一对多的关系,而不用担心彼此的实现细节。
如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
(1)一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
(2)一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
(3)一个对象必须通知其他对象,而并不知道这些对象是谁。需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
更多的观察都模式讲解可以看24天学会设计模式------观察者模式
后记:
这几天好冷,写个好的博文不容易啊。这文章都趟我草稿箱好久了啊,又写代码又写文字的又截图,GIF动画还不能超2M,我都不知重新截图了多少张了!CSDN你这个功能要好好的改改下,游戏中加了声音了,可惜没法传上来听。。。。看了两晚的电影,这个游戏都没更新了,推荐大家去看《亲爱的》(很感人的,要记得自带纸巾。。),《星球崛起》(1,2,拍得很好),《太极》(1,2,打得很舒服),美剧《摩登家庭》(很搞笑.)啦啦啦。我又废话了,好久没这么多废话了,这个游戏要完结了,我也要转到3.3了,下个游戏出打飞机!