如何在层间或者场景间进行消息传递,大概是每个初学者都要面对的问题吧。这里介绍cocos2d-x的一种消息/数据传递方式,内置的观察者模式,也称消息通知中心,CCNotificationCenter。
CCNotificationCenter
CCNotificationCenter是cocos2d-x提供的一个消息中心,类似于观察者模式,是一个单例类,用于辅助控制消息传递。大概工作原理如下图。
这里以A,B通讯为例,A,B可以是层间或者场景间。假如A要向B发送带数据的消息。那么
(1)B向小心中心注册一个bMsg的消息,告知消息中心,如果消息中心接收到bMsg,必须通知B,以便B采取相应的措施。
(2)A向消息中心发送bMsg消息。
(3)消息中心接收到bMsg,查询有谁在它这里注册了这个消息,发现是B,就通知B有人发送了bMsg。
(4)当然这个消息带有A要传递的数据,而B也不知道到底是谁给它发了bMsg这条消息,除非在传递的数据中指出。
相关API
CCNotificationCenter API1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| static CCNotificationCenter *sharedNotificationCenter(void);
static void purgeNotificationCenter(void);
void addObserver(CCObject *target, SEL_CallFuncO selector, const char *name, CCObject *obj); void removeObserver(CCObject *target,const char *name); int removeAllObservers(CCObject *target); void postNotification(const char *name); void postNotification(const char *name, CCObject *object);
|
观察者的意思即是等待获取消息的对象,比如上图的B。
原理
CCNotificationCenter的实现比较简单,它的内部维护了一个CCArray数组,数组的元素是CCNotificationObserver类型的对象,CCNotificationObserver封装了回调的执行者,回调函数,消息名称以及传递的数据。每次注册,也就是添加观察者,其实就是把这个CCNotificationObserver对象加入通知中心的CCArray数组,而每次的消息发送就是将消息传递给通知中心,由通知中心遍历这个数组,找到一致的消息,调用这些回调函数。其实这样看起来,就像是A在调用B的函数。具体的就自己看下源码吧。
示例
这里做了一个场景,包含了2个层,目的是层A向层B发送带数据的消息,层B收到后打印出这条数据。
CCNotificationCenter的示例1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| bool ALayer::init() { bool bRet=false; do { CC_BREAK_IF(!CCLayer::init()); CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize(); CCMenuItemImage *pCloseItem = CCMenuItemImage::create( "CloseNormal.png", "CloseSelected.png", this, menu_selector(ALayer::PostMessage)); pCloseItem->setAnchorPoint(ccp(0.5,0.5)); pCloseItem->setPosition(ccp(visibleSize.width/2,visibleSize.height/2)); CCMenu* pMenu = CCMenu::create(pCloseItem, NULL); pMenu->setPosition(CCPointZero); this->addChild(pMenu, 1); bRet=true; } while (0); return bRet; } void ALayer::PostMessage(CCObject* pSender) { CCString* str=CCString::create("Hello BLayer!"); CCNotificationCenter::sharedNotificationCenter()->postNotification("BMessage",str); }
bool BLayer::init() { bool bRet=false; do { CC_BREAK_IF(!CCLayer::init()); CCNotificationCenter::sharedNotificationCenter()->addObserver(this,callfuncO_selector(BLayer::getMessage),"BMessage",NULL); bRet=true; } while (0); return bRet; } void BLayer::getMessage(CCObject* obj) { CCString* str=static_cast<CCString*>(obj); CCLog(str->getCString()); } BLayer::~BLayer(void) { CCNotificationCenter::sharedNotificationCenter()->purgeNotificationCenter(); }
|
注意
使用CCNotificationCenter需要注意以下几点:
(1)一个对象可以注册多个消息,一个消息也可以由多个消息注册。
(2)传递参数,A可以向B传递参数,而B在注册的时候也可以带一个参数,如果这两个数据不是指向同一对象的话,消息不会传递。也就是说要么A传递NULL对象,要么B注册时带NULL对象,要么都不是NULL但必须是同一对象,消息传递才会成功。以下是发送消息执行的判断:
消息发送的判断1
| if (!strcmp(name,observer->getName()) && (observer->getObject() == object || observer->getObject() == NULL || object == NULL))
|
(3)局部变量的传递,注意到上例,传递的是CCString的一个局部变量(但还是要autorelease),从CCNotificationCenter的实现上来看,这是没有问题的,因为数据是在postNotification被调用的,也就是整个函数体并没结束,数据不会被销毁。
效果图
源码下载
下载地址