【在Cocos2d-x中面向接口编程01】向她表白之实现函数回调
首先,我要郑重地声明一件事情:我现在很尿急,但是厕所有人~!
嗯,然后,我本来是一名Java程序员,由于Cocos2d-x太吸引人,我就硬着头皮学,可是C++和Java那看似差不多,实际又确实差很多的纠结,让我变得...很尿急~!
(旁白:可以少说一些废话么?)
最近我在试着写一个小游戏,叫做《粒子格斗》。
(旁白:你已经说过了。。。)
然后,我自己写了一个消息派发的功能,因为当时我并不知道Cocos2d-x已经集成了这个功能。我写Java习惯了,所以我按照Java的思维习惯实现了这个消息派发。如果对消息派发不熟悉或者完全不懂的朋友,那个,还是百度一下会好一些~
(旁白:等等~!你不是应该介绍实现函数回调吗?)
哦,对,我应该介绍实现函数回调的,但是如果你和我一样,是Java出身的,并且Java功底也不是很深厚,对于掌握新语言不太迅速的话,那么,请继续往下看。
如果你对C++已经是比较熟悉了,那,可以忽略这篇文章。
笨木头花心贡献,啥?花心?不呢,是用心~
转载请注明,原文地址:http://blog.csdn.net/musicvs/article/details/8261147
正文:
1.从Java移植过来的消息派发
按照Java的思路,实现如下:
a.首先要实现消息派发的核心类,用于订阅、删除以及发布消息,这个类只需用实现3个函数:
/* 订阅消息 */
addListener(I_MessageDispath msgListener, const char* sMsgType);
/* 删除订阅的消息 */
delListener(I_MessageDispath msgListener, const char* sMsgType);
/* 发布消息 */
dispatchMsg(const char* sMsgType, CCObject* pData);
b.然后就要实现一个接口,一个代表消息订阅者的接口(这很美妙,我太喜欢接口了,它让我的代码之间的耦合度减小了很多很多~!),我们命名为I_MessageDispatch。
class I_MessageDispatch : public CCObject {
public:
void onRecv(CCObject* pData);
};
c.详细的就不多解释了,我们再用一个CCDictionary保存不同类型的订阅者对象,比较完整的头文件内容如下:
class MessageDispatch : public CCObject {
public:
static MessageDispatch* sharedMessageDispatch();
/* 订阅消息 */
addListener(I_MessageDispath msgListener, const char* sMsgType);
/* 删除订阅的消息 */
delListener(I_MessageDispath msgListener, const char* sMsgType);
/* 发布消息 */
dispatchMsg(const char* sMsgType, CCObject* pData);
private:
/* CCDictionary<CCArray<I_MessageDispatch*>, const char*> */
CCDictionary* mListenerDict;
};
d.然后就是一些细节的处理,好,不多说。就是字典里保存了消息类型和消息订阅者列表的对应关系。添加订阅的时候,就把实现了I_MessageDispatch接口的对象添加到对应的列表里(删除同理),然后发布消息的时候,取出要发布的消息类型对应的订阅者列表,对列表中的每个对象都调用一次onRecv函数,就可以了。
(旁白:你不是说不多说么?==我感觉你说了很多)
2.看起来它工作得很好
看起来,这个消息派发类工作得不错,所有功能都能正常运行。可是,问题来了。
C++中是没有接口的说法的,它只有多继承,我太怕多继承了,我可给它整晕了好多次。首先它有一个弱点,二义性,额,我就不解释了,专业解释请百度一下(旁白:你压根儿就解释不了吧...)。比如我有这样一个类:
class ClientFight : public CCLayer {
public:
bool initWithLayer(CCLayer* mLayer);
static ClientFight* createWithScene(CCLayer* mLayer);
#endif
嗯,看起来没有什么问题,但是,如果我这个类想订阅消息,怎么办?很简单啊,让它继承I_Messagedispatch接口,然后实现onRecv函数,然后订阅消息,就OK了,这是我在写Java时最容易想到的方法。
class ClientFight : public CCLayer, public I_MessageDispatch {
public:
bool initWithLayer(CCLayer* mLayer);
static ClientFight* createWithScene(CCLayer* mLayer);
virtual void onRecv(CCObject* pData);
#endif
3.二义性,烦死你
来看看这样一个情景,某个类创建了ClientFight对象,并且保持了它的引用,当我要释放引用的时候,很自然要调用release函数。于是,编译器会告诉你,ClientFight的release函数调用不明确。
原因呢?ClientFight继承了CCLayer,CCLayer继承了CCObject,I_MessageDispatch也继承了CCObject。
于是,它们两个都有release函数,而ClientFight作为它们的子类,不知道该继承谁的release才好,所以编译器晕了,我也晕了。
要解决这个问题很简单,本来是这样调用的:mClientFight->release();
现在改为:mClientFight->CCLayer::release();
OK,这样编译器就不会晕了。但我还是晕啊,这太麻烦了~!这只是其中一个小问题,以后还会遇到更多问题,对于Java出身的人来说,我暂时无法接受这么麻烦的编码。
4.发现使用接口的新方式
我写的那个消息派发还不止这点故障,还有更多的故障,我就不说了。
偶然的机会,我百度了一下:Cocos2d-x观察者。
然后被我发现了子龙山人的博客,他介绍了Cocos2d-x的观察者模式,正好就是说的消息派发,木了个头的,原来已经有这个功能了,我还干嘛写得这么辛苦?
(旁白:你废话多,所以总是干些多余的事情==)
这个消息派发的功能大家可以看看这个类:CCNotificationCenter。
或者看看大牛的博客:
http://www.zilongshanren.com/cocos2d-x-design-pattern-6-observer/
http://www.xinze.me/cocos2d-x-ccnotificationcenter/
我稍微拜读了一下Cocos2d-x的源码,发了一个很奇妙的地方,是一种“另类”的接口使用方式。
CCNotificationCenter在添加订阅者的时候,不需要订阅者实现某个接口,直接添加就可以了。
CCNotificationCenter会自动新建一个对象,这个对象是CCNotificationObserver,其实这就可以当做是一个接口,只不过订阅者不是实现这个接口,而是直接绑定到这个接口身上。按照我朋友的一个说法,这有点像适配器模式,也许就是适配器模式。
void CCNotificationCenter::addObserver(CCObject *target,
SEL_CallFuncO selector,
const char *name,
CCObject *obj)
{
if (this->observerExisted(target, name))
return;
CCNotificationObserver *observer = new CCNotificationObserver(target, selector, name, obj);
if (!observer)
return;
observer->autorelease();
m_observers->addObject(observer);
}
怎么样?这样我们就可以避免一个对象为了订阅消息而不得不使用多继承了~
5.实例:向她表白
我们先来练练手,熟悉一下C++的函数参数的传递,再次声明,已经熟悉C++的朋友,直接忽略我。
(旁白:我们早就忽略你了。。。)
我们来实现一个经典的事情:我不敢向喜欢的女生表达,所以我想让我的朋友帮我这个忙。
这个类很简单:
#ifndef __TELL_LIKE_HER_H__
#define __TELL_LIKE_HER_H__
#include "cocos2d.h"
USING_NS_CC;
typedef void (CCObject::*SEL_CallFuncL) (CCObject*);
#define callfuncL_selector(_SELECTOR) (SEL_CallFuncL)(&_SELECTOR)
/* 这是一个不太像接口的接口 */
class I_LikeHer : public CCObject {
public:
I_LikeHer(CCObject* targer, SEL_CallFuncL selector);
void performSelector();
private:
SEL_CallFuncL mSelector;
CCObject* mTargert;
};
/* 这是我的朋友,他会帮我表达我的心意 */
class TellLikeHer : public CCObject {
public:
static TellLikeHer* sharedTellLikeHer();
/* 设置要表白的主角~ */
void setTheActor(CCObject* targer, SEL_CallFuncL selector);
/* 她就在这里,TellLikeHer将代替主角传达心意 */
void sheIsHere();
private:
static TellLikeHer* mTellLikeHer;
I_LikeHer* mLikerHer;
};
#endif
要实现函数回调,我们需要两样东西:
typedefvoid(CCObject::*SEL_CallFuncL)(CCObject*);
#definecallfuncL_selector(_SELECTOR)(SEL_CallFuncL)(&_SELECTOR)
定义一个SEL_CallFuncL类型,以及一个传递函数参数的宏,我不太善于解释这个,我是依葫芦画瓢仿照源码写的,所以我选择不解释。
我们看到有一个I_LikeHer类,这个就是相当于一个接口,TellLikeHer类只保存这种接口的类,所以,我们依旧是面向接口编程,减少了耦合度。
我们来看看setTheActor函数(函数名取得不好,不要灭了我。。。):
void TellLikeHer::setTheActor( CCObject* targer, SEL_CallFuncL selector ) {
mLikerHer = new I_LikeHer(targer, selector);
}
很简单,外部传进来的可以是任意的对象类型(当然,必须是继承了CCObject的),TellLikeHer类根据就不需要关心谁传进来了,因为它会创建一个I_LikeHer对象,把传进来的对象包装起来。
噗,听起来又有点像装饰者模式?是蛮像的。
再来看看sheIsHere函数:
void TellLikeHer::sheIsHere() {
mLikerHer->performSelector();
}
这确实是在面向接口编程,因为我们只需要调用I_LikeHer接口的performSelector函数,调用了这个函数之后,监听者就会被回调,是的,我们帮他传达了心意了,并且告诉了他结果。
void I_LikeHer::performSelector() {
(mTargert->*mSelector)(NULL);
}
这个是什么东西?它就是我们的主题:函数的回调。
这有点混乱,我明白。(旁白:你真心觉得只有一点点混乱?)我们来画图理解:
这很好地解决了二义性的问题。
也许是我的经验太少,对接口的使用了解不深入,所以总以为面向接口编程,就是定义一个接口,让其他类来实现。
在Java里,我暂时没有遇到什么大问题。
不过在Cocos2d-x中刚好暴露了这种方式的缺陷。
好吧,对我来说,这是一次不错的收获。因为二义性的问题,让我认识了Cocos2d-x的消息派发(CCNotificationCenter)以及一种很不错接口使用方式。
又不过,这种接口的使用方式也不见得是万能的,如果一个接口要实现多个函数,那,又该怎么做呢?于是,我得拿出一本C++的教程,研究一下C++关于接口方面的知识了。
(旁白:噗,说了等于没说。)
相关推荐
资源名称:Cocos2d-x实战:JS卷——Cocos2d-JS开发内容简介:本书是介绍Cocos2d-x游戏编程和开发技术书籍,介绍了使用Cocos2d-JS中核心类、瓦片地图、物理引擎、音乐音效、数据持久化、网络通信、性能优化、多平台...
cocos2d-x-2.1.5
在使用cocos2d-x开发游戏的过程中,为了实现逻辑和显示相分离。 在下通宵了一个晚上,写出了该事件类。 谨记,该事件只能用于cocos2d-x中。 事件发送者需要继承EventDispatcher类 事件接收者需要继承EventHandle类...
资源名称:大富翁手机游戏开发实战基于Cocos2d-x3.2引擎内容简介:李德国编著的《大富翁手机游戏开发实战(基于 Cocos2d-x3.2引擎)》使用Cocos2d-x游戏引擎技术,带领读者一步一步从零开始进行大富翁移动游戏的开发...
Cocos2d-x是移动跨平台开发最流行的游戏引擎,而本书是一本很全面的、比较‘接地气’的游戏开发教程。书中汇聚了热门手机游戏《捕鱼达人》开发的实战经验,作者从最基础的内容开始,逐步深入地介绍了Cocos2d-x的相关...
Cocos2d-x游戏编程——C++篇(电子工业出版社,徐飞 著)书本配套的光盘代码,
cocos2d-x-3.2下载,不多说。或者可以下载另一个资源 cocos引擎老版本集合(cocos2d-x-2.2.1 - 3.5) http://download.csdn.net/download/crazymagicdc/9982656
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是一款支持多平台的 2D手机游戏引擎,支持iOS、Android、BlackBerry等众多平台。当前,很多移动平台流行的游戏,都是基于Cocos2D-X开发的。 《Cocos2D-X...
Cocos2d-x实战
这是我重新弄的cocos2d-x-3.0的类图.之前别人兄台弄的,有些不全面,有些地方错误.我这个可以说是最新的了.每个类添加了中文的详细注解,同时也添加了中文的类名称翻译.这样对cocos2d-x-3.0的框架比较好上手. 有兴趣的...
Cocos2d-x高级开发教程:制作自己的《捕鱼达人》 图书简介: 《Cocos2d-x高级开发教程:制作自己的《捕鱼达人》》是国内第一本全面深入讲解Cocos2d-x进阶内容的图书,Cocos2d-x创始人王哲作序推荐,《捕鱼达人》开发...
《Cocos2D-X游戏开发技术精解》详细介绍如何使用Cocos2D-X引擎开发自己的移动平台游戏。全书共15章,主要内容包括:Cocos2D-X引擎简介;如何建立跨平台的开发环境;引擎的核心模块——渲染框架;如何实现动态画面和...
因为最近在学cocos2d-x,找了半天在网上也找不到一个离线的文档,于是自己抽空做了一个,全部内容提取自cocos2d-x中文官网的文档页http://cn.cocos2d-x.org/article 目前只提取了cocos2d-x部分内容。因为内容比较多...
精通COCOS2D-X游戏开发 精通COCOS2D-X游戏开发 精通COCOS2D-X游戏开发 精通COCOS2D-X游戏开发 精通COCOS2D-X游戏开发
经典版本 方便下载 源码 旧版本 3.8 官网找不到了 cocos2d-x-3.8.zip
Cocos2d-x之Lua核心编程 配套代码,没有对应的pdf的书,下载前请看清楚啊!
资源名称:Cocos2d-X案例开发大全内容简介:《Cocos2d-X案例开发大全》包括大量基于Cocos2d-X开发的游戏案例,详细讲解了基于Cocos2d-X游戏引擎在Android及iOS平台下进行游戏开发的思路,能够快速帮助读者提升利用...
Cocos2d-x 3.x游戏开发实战pdf含目录,内容详细,强烈推荐给大家。
Cocos2d-x-3.x游戏开发之旅-钟迪龙著 全新pdf版和附书代码(代码为工程文件,可复制) 附带目录标签