[历史归档]本文原发布于 cstriker1407.info 个人博客,内容为历史存档,仅供参考。
发布时间:2014-01-27| 标题:cocos2dx学习笔记:CCNode回调流程|分类:编程 / C && C++ / cocos2dx |标签:cocos2dx
cocos2dx学习笔记:CCNode回调流程
- CCNode的回调流程
- CCNode的几个回调函数:
- cocos2dx程序框架流程:
- 首先进入main.cpp函数,可以发现程序调用了Appliaction的run方法:
- 然后查看run方法,是一个while(1)循环,调用了Director的mainloop方法:
- 查看mainLoop方法,发现其调用了drawScene方法:
- 查看drawScene方法:
- 没有场景切换动画时的回调流程:
- void CCDirector::setNextScene(void):
- 有场景切换动画时的回调流程:
- 从场景A切换到场景X:
- CCTransitionScene和CCTransitionShrinkGrow片段:
- 由场景X切换场景B:
最近项目中用到了cocos2dx来开发一个基于陀螺仪的demo,忙了半个星期,一边学习一边开发,总算搞了出来。这里备份下开发中学习到的一些cocos2dx知识。
CCNode的回调流程
在使用CCLayer,CCScene的时候往往想在layer或者场景切换的时候处理一些资源类的东西,于是就在CCNode里发现了一些回调函数,但是这些回调函数如何调用,作者在网上也没有搜索,自己翻了下源码,发现比较简单。在这里备份下。
CCNode的几个回调函数:
classCC_DLLCCNode:publicCCObject{public:。。。。。 。。。。。virtualboolinit();staticCCNode*create(void);/** * Event callback that is invoked every time when CCNode enters the 'stage'. * If the CCNode enters the 'stage' with a transition, this event is called when the transition starts. * During onEnter you can't access a "sister/brother" node. * If you override onEnter, you shall call its parent's one, e.g., CCNode::onEnter(). * @js NA * @lua NA */virtualvoidonEnter();/** Event callback that is invoked when the CCNode enters in the 'stage'. * If the CCNode enters the 'stage' with a transition, this event is called when the transition finishes. * If you override onEnterTransitionDidFinish, you shall call its parent's one, e.g. CCNode::onEnterTransitionDidFinish() * @js NA * @lua NA */virtualvoidonEnterTransitionDidFinish();/** * Event callback that is invoked every time the CCNode leaves the 'stage'. * If the CCNode leaves the 'stage' with a transition, this event is called when the transition finishes. * During onExit you can't access a sibling node. * If you override onExit, you shall call its parent's one, e.g., CCNode::onExit(). * @js NA * @lua NA */virtualvoidonExit();/** * Event callback that is called every time the CCNode leaves the 'stage'. * If the CCNode leaves the 'stage' with a transition, this callback is called when the transition starts. * @js NA * @lua NA */virtualvoidonExitTransitionDidStart();/// @} end of event callbacks./** * Stops all running actions and schedulers */virtualvoidcleanup(void);。。。。。 。。。。。};cocos2dx程序框架流程:
首先进入main.cpp函数,可以发现程序调用了Appliaction的run方法:
intAPIENTRY_tWinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPTSTR lpCmdLine,intnCmdShow){。。。。。 。。。。。returnCCApplication::sharedApplication()->run();}然后查看run方法,是一个while(1)循环,调用了Director的mainloop方法:
intCCApplication::run(){。。。。。 。。。。。while(1){。。。。。CCDirector::sharedDirector()->mainLoop();。。。。。}。。。。。}查看mainLoop方法,发现其调用了drawScene方法:
voidCCDisplayLinkDirector::mainLoop(void){。。。。。 。。。。。drawScene();。。。。。}查看drawScene方法:
voidCCDirector::drawScene(void){。。。。。 。。。。。if(m_pNextScene){setNextScene();}。。。。。if(m_pRunningScene){m_pRunningScene->visit();}。。。。。}查看代码到这里,就可以大致的知道cocos2dx程序的运行流程,通过不停的循环,来执行当前scene的visit方法绘制界面,如果有场景切换,就切换场景后继续visiit。这里作者不关心visit和draw方法,只关注于CCNode的回调流程。
没有场景切换动画时的回调流程:
void CCDirector::setNextScene(void):
voidCCDirector::setNextScene(void){boolrunningIsTransition=dynamic_cast<CCTransitionScene*>(m_pRunningScene)!=NULL;boolnewIsTransition=dynamic_cast<CCTransitionScene*>(m_pNextScene)!=NULL;// If it is not a transition, call onExit/cleanupif(!newIsTransition){if(m_pRunningScene){m_pRunningScene->onExitTransitionDidStart();m_pRunningScene->onExit();}// issue #709. the root node (scene) should receive the cleanup message too// otherwise it might be leaked.if(m_bSendCleanupToScene&&m_pRunningScene){m_pRunningScene->cleanup();}}if(m_pRunningScene){m_pRunningScene->release();}m_pRunningScene=m_pNextScene;m_pNextScene->retain();m_pNextScene=NULL;if((!runningIsTransition)&&m_pRunningScene){m_pRunningScene->onEnter();m_pRunningScene->onEnterTransitionDidFinish();}}通过查看setNextScene的方法,就可以发现在没有场景切换动画时,各个回调函数的调用流程很清楚了。
首先调用要退出场景的 onExitTransitionDidStart 方法和 onExit 方法,如果当前是切换场景不是push场景,就继续调用cleanup方法,最后在执行新场景的 onEnter 和 onEnterTransitionDidFinish 方法。
我们查看下这4个onXXXX方法,CCScene和CCLayer均继承自CCNode,这4个方法也是CCNode的方法。我们以onEnter为例,
voidCCNode::onEnter(){arrayMakeObjectsPerformSelector(m_pChildren,onEnter,CCNode*);。。。。。}可以发现里面有个宏,这个就是关键所在~~,宏的定义如下:
#definearrayMakeObjectsPerformSelector(pArray,func,elementType)\do{\if(pArray&&pArray->count()>0)\{\CCObject*child;\CCARRAY_FOREACH(pArray,child)\{\elementType pNode=(elementType)child;\if(pNode)\{\pNode->func();\}\}\}\}\while(false)由此就很清晰了,当场景切换时,新老场景的各个回调方法会被回调,这些回调方法又会递归调用其children的相同的回调方法,由于CCScene,CCLayer,CCSprite等在cocos2dx的场景构建时为父子关系,那么场景中的各种元素都会回调一次。
有场景切换动画时的回调流程:
有场景动画时回调比较复杂一点,首先需要明确的是场景切换动画也是场景。这样当场景A通过动画X切换到场景B时,实际上是有3个场景:A,X,B。为了便于分析,我们假设场景切换动画X为 CCTransitionShrinkGrow 类的动画。
从场景A切换到场景X:
回到函数 void CCDirector::setNextScene(void) 中,当从A切到X时,m_pRunningScene 是场景A,m_pNextScene 是场景X,A不是场景切换动画类CCTransitionShrinkGrow(CCTransitionScene),X是场景切换动画类。那么根据函数逻辑,函数流程会直接走向场景X的onEnter和onEnterXXX函数,但不会走向场景A的onExitXXX和onExit函数。
CCTransitionScene和CCTransitionShrinkGrow片段:
代码较多,但是根据代码的走向就很清晰了。
classCC_DLLCCTransitionScene:publicCCScene{protected:CCScene*m_pInScene;CCScene*m_pOutScene;。。。。。 。。。。。}classCC_DLLCCTransitionShrinkGrow:publicCCTransitionScene,publicCCTransitionEaseScene{。。。。。 。。。。。}CCTransitionScene*CCTransitionScene::create(floatt,CCScene*scene){CCTransitionScene*pScene=newCCTransitionScene();if(pScene&&pScene->initWithDuration(t,scene)){pScene->autorelease();returnpScene;}CC_SAFE_DELETE(pScene);returnNULL;}boolCCTransitionScene::initWithDuration(floatt,CCScene*scene){CCAssert(scene!=NULL,"Argument scene must be non-nil");if(CCScene::init()){m_fDuration=t;// retainm_pInScene=scene;m_pInScene->retain();m_pOutScene=CCDirector::sharedDirector()->getRunningScene();if(m_pOutScene==NULL){m_pOutScene=CCScene::create();m_pOutScene->init();}m_pOutScene->retain();CCAssert(m_pInScene!=m_pOutScene,"Incoming scene must be different from the outgoing scene");sceneOrder();returntrue;}else{returnfalse;}}voidCCTransitionScene::finish(){。。。。。 。。。。。this->schedule(schedule_selector(CCTransitionScene::setNewScene),0);}voidCCTransitionScene::setNewScene(floatdt){。。。。。 CCDirector*director=CCDirector::sharedDirector();。。。。。 director->replaceScene(m_pInScene);。。。。。}// custom onEntervoidCCTransitionScene::onEnter(){CCScene::onEnter();// disable events while transitionsCCDirector::sharedDirector()->getTouchDispatcher()->setDispatchEvents(false);// outScene should not receive the onEnter callback// only the onExitTransitionDidStartm_pOutScene->onExitTransitionDidStart();m_pInScene->onEnter();}// custom onExitvoidCCTransitionScene::onExit(){CCScene::onExit();// enable events while transitionsCCDirector::sharedDirector()->getTouchDispatcher()->setDispatchEvents(true);m_pOutScene->onExit();// m_pInScene should not receive the onEnter callback// only the onEnterTransitionDidFinishm_pInScene->onEnterTransitionDidFinish();}voidCCTransitionShrinkGrow::onEnter(){CCTransitionScene::onEnter();。。。。。 。。。。。 m_pInScene->runAction(this->easeActionWithAction(scaleIn));m_pOutScene->runAction(CCSequence::create(this->easeActionWithAction(scaleOut),CCCallFunc::create(this,callfunc_selector(CCTransitionScene::finish)),NULL));}首先,当走向场景X的onEnter时,CCTransitionShrinkGrow类的onEnter函数首先调用了父类CCTransitionScene的onEnter函数,然后调用了CCTransitionScene的finish函数。
根据CCTransitionScene的initXX函数,可以知道在CCTransitionScene中,m_pInScene是场景B,m_pOutScene是场景A,那么根据CCTransitionScene的onEnter函数的逻辑,知道此时场景A的onExitXXX被调用,场景B的onEnter被调用。
再来分析CCTransitionScene的finish函数,该函数又调用了CCTransitionScene的setNewScene函数,在该函数中,我们发现程序调用了我们熟悉的ReplaceScene函数,前面已经知道了m_pInScene是场景B,即程序现在开始执行由场景X切换场景B。
由场景X切换场景B:
和上面一样,我们分析void CCDirector::setNextScene(void) 中,当从X切到B时,m_pRunningScene 是场景X,m_pNextScene 是场景B,B不是场景切换动画类CCTransitionShrinkGrow(CCTransitionScene),X是场景切换动画类。那么根据函数逻辑,函数流程会走向场景X的onExitXXX和onExit函数,但是不会执行场景B的onEnter和onEnterXXX函数。
场景X类是CCTransitionShrinkGrow类,执行场景X的onExitXXX函数和onExit函数时,会进入CCTransitionScene函数的onExit函数,由于cocos2dx是类实例(指针)传递,因此这里的实例和上面由A切到X的类实例是一个,那么m_pInScene依然是场景B,m_pOutScene依然是场景A,在CCTransitionScene函数的onExit函数里,场景A的onExit被调用,场景B的onEnterXXX被调用。
此时,整个场景切换过程已经完成。
综上可得:
没有切换动画时,调用流程:
A: onExitTransitionDidStart
A: onExit
B: onEnter
B: onEnterTransitionDidFinish
有切换动画时,调用流程:
A: onExitTransitionDidStart
B: onEnter
A: onExit
B: onEnterTransitionDidFinish