最近项目中用到了cocos2dx来开发一个基于陀螺仪的demo,忙了半个星期,一边学习一边开发,总算搞了出来。这里备份下开发中学习到的一些cocos2dx知识。
CONTENTS
CCNode的回调流程
在使用CCLayer,CCScene的时候往往想在layer或者场景切换的时候处理一些资源类的东西,于是就在CCNode里发现了一些回调函数,但是这些回调函数如何调用,作者在网上也没有搜索,自己翻了下源码,发现比较简单。在这里备份下。
CCNode的几个回调函数:
class CC_DLL CCNode : public CCObject { public: 。。。。。 。。。。。 virtual bool init(); static CCNode * 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 */ virtual void onEnter(); /** 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 */ virtual void onEnterTransitionDidFinish(); /** * 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 */ virtual void onExit(); /** * 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 */ virtual void onExitTransitionDidStart(); /// @} end of event callbacks. /** * Stops all running actions and schedulers */ virtual void cleanup(void); 。。。。。 。。。。。 };
cocos2dx程序框架流程:
首先进入main.cpp函数,可以发现程序调用了Appliaction的run方法:
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { 。。。。。 。。。。。 return CCApplication::sharedApplication()->run(); }
然后查看run方法,是一个while(1)循环,调用了Director的mainloop方法:
int CCApplication::run() { 。。。。。 。。。。。 while (1) { 。。。。。 CCDirector::sharedDirector()->mainLoop(); 。。。。。 } 。。。。。 }
查看mainLoop方法,发现其调用了drawScene方法:
void CCDisplayLinkDirector::mainLoop(void) { 。。。。。 。。。。。 drawScene(); 。。。。。 }
查看drawScene方法:
void CCDirector::drawScene(void) { 。。。。。 。。。。。 if (m_pNextScene) { setNextScene(); } 。。。。。 if (m_pRunningScene) { m_pRunningScene->visit(); } 。。。。。 }
查看代码到这里,就可以大致的知道cocos2dx程序的运行流程,通过不停的循环,来执行当前scene的visit方法绘制界面,如果有场景切换,就切换场景后继续visiit。这里作者不关心visit和draw方法,只关注于CCNode的回调流程。
没有场景切换动画时的回调流程:
void CCDirector::setNextScene(void):
void CCDirector::setNextScene(void) { bool runningIsTransition = dynamic_cast<CCTransitionScene*>(m_pRunningScene) != NULL; bool newIsTransition = dynamic_cast<CCTransitionScene*>(m_pNextScene) != NULL; // If it is not a transition, call onExit/cleanup if (! 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为例,
void CCNode::onEnter() { arrayMakeObjectsPerformSelector(m_pChildren, onEnter, CCNode*); 。。。。。 }
可以发现里面有个宏,这个就是关键所在~~,宏的定义如下:
#define arrayMakeObjectsPerformSelector(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片段:
代码较多,但是根据代码的走向就很清晰了。
class CC_DLL CCTransitionScene : public CCScene { protected: CCScene * m_pInScene; CCScene * m_pOutScene; 。。。。。 。。。。。 } class CC_DLL CCTransitionShrinkGrow : public CCTransitionScene , public CCTransitionEaseScene { 。。。。。 。。。。。 } CCTransitionScene * CCTransitionScene::create(float t, CCScene *scene) { CCTransitionScene * pScene = new CCTransitionScene(); if(pScene && pScene->initWithDuration(t,scene)) { pScene->autorelease(); return pScene; } CC_SAFE_DELETE(pScene); return NULL; } bool CCTransitionScene::initWithDuration(float t, CCScene *scene) { CCAssert( scene != NULL, "Argument scene must be non-nil"); if (CCScene::init()) { m_fDuration = t; // retain m_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(); return true; } else { return false; } } void CCTransitionScene::finish() { 。。。。。 。。。。。 this->schedule(schedule_selector(CCTransitionScene::setNewScene), 0); } void CCTransitionScene::setNewScene(float dt) { 。。。。。 CCDirector *director = CCDirector::sharedDirector(); 。。。。。 director->replaceScene(m_pInScene); 。。。。。 } // custom onEnter void CCTransitionScene::onEnter() { CCScene::onEnter(); // disable events while transitions CCDirector::sharedDirector()->getTouchDispatcher()->setDispatchEvents(false); // outScene should not receive the onEnter callback // only the onExitTransitionDidStart m_pOutScene->onExitTransitionDidStart(); m_pInScene->onEnter(); } // custom onExit void CCTransitionScene::onExit() { CCScene::onExit(); // enable events while transitions CCDirector::sharedDirector()->getTouchDispatcher()->setDispatchEvents(true); m_pOutScene->onExit(); // m_pInScene should not receive the onEnter callback // only the onEnterTransitionDidFinish m_pInScene->onEnterTransitionDidFinish(); } void CCTransitionShrinkGrow::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
发表评论