首先,调用切换场景的函数是:CCDirector下的void replaceScene(CCScene *pScene);
void CCDirector::replaceScene(CCScene *pScene) { CCAssert(m_pRunningScene, "Use runWithScene: instead to start the director"); CCAssert(pScene != NULL, "the scene should not be null"); unsigned int index = m_pobScenesStack->count(); m_bSendCleanupToScene = true; m_pobScenesStack->replaceObjectAtIndex(index - 1, pScene); m_pNextScene = pScene; }
查看一下其在其他地方的调用,发现其主要的作用是用在pushScene以及popScene方法中,这两个方法是另一种切换场景的方式,本文暂不做研究。
2. m_bSendCleanupToScene = true;
查看一下其的调用情况,发现void CCDirector::setNextScene(void)中有这么一段:
// 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(); }
setNextScene函数从字面意思来理解是,设置下一场景的意思,这个方法等下研究
从上面可以看出,m_bSendCleanupToScene 的作用是设置下一场景的时候,判断需不需要将当前运行的场景cleanup
3.m_pNextScene = pScene;设置下一个场景
查看其调用情况,发现在void CCDirector::drawScene(void)方法下面
if (m_pNextScene) { setNextScene(); }
上面可以看出,主要的切换场景的方法是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;//m_pNextScene 指针置空 if ((! runningIsTransition) && m_pRunningScene)//当前运行场景不在过渡状态 { m_pRunningScene->onEnter(); m_pRunningScene->onEnterTransitionDidFinish(); } }
1)如果不使用过渡特效,则调用当前场景的onExitTransitionDidStart(),以及onExit()两个方法,然后cleanup()方法,再release()。然后把m_pRunningScene指向到m_pNextScene,再把m_pNextScene retain(),然后把指针m_pNextScene置空,并调用新的当前场景m_pRunningScene的onEnter() 以及onEnterTransitionDidFinish()方法
2)如果使用过渡特效,则调用m_pRunningScene的release()方法,然后把m_pRunningScene指向到m_pNextScene,再把m_pNextScene retain(),然后把指针m_pNextScene置空,并调用新的当前场景m_pRunningScene的onEnter() 以及onEnterTransitionDidFinish()方法
在此,总结一下,如果不使用过渡特效切换场景的话,流程如下:
1)创建新的Scene newScene
2)调用CCDirector的void replaceScene(newScene);方法
3)主线程会因为2)中设置了m_pNextScene,而调用CCDirector的void CCDirector::setNextScene(void)方法
4)在setNextScene方法中的调用流程是:
oldScene : onExitTransitionDidStart()
onExit()
cleanup()
release()
newScene: retain()
onEnter()
onEnterTransitionDidFinish()
由此可以总结下,不过切换场景,场景方法调用的流程:
newScene: init()
oldScene : onExitTransitionDidStart()
onExit()
cleanup()
release()
newScene: retain()
onEnter()
onEnterTransitionDidFinish()
oldScene ::~oldScene (void)
下面来研究下,使用过渡特效切换场景
最初的调用方法:
CCDirector::sharedDirector()->replaceScene(cocos2d::CCTransition特效::create(time, newScene));
查看下特效的创建方法CCTransition特效::create(time, newScene):(以一个特效为例)
CCTransitionFadeUp* CCTransitionFadeUp::create(float t, CCScene* scene) { CCTransitionFadeUp* pScene = new CCTransitionFadeUp(); if(pScene && pScene->initWithDuration(t, scene)) { pScene->autorelease(); return pScene; } CC_SAFE_DELETE(pScene); return NULL; }可以看出,它return的是一个新new出来的CCTransition特效 对象,这个对象是继承自CCTransitionScene的
再来看看pScene->initWithDuration(t, scene)方法里做了什么
bool CCTransitionScene::initWithDuration(float t, CCScene *scene) { CCAssert( scene != NULL, "Argument scene must be non-nil"); if (CCScene::init())//判断新场景有没有init,没有就init { m_fDuration = t;//过渡时间 // retain m_pInScene = scene;//m_pInScene 记录过渡后场景 m_pInScene->retain();//retain过渡后场景 m_pOutScene = CCDirector::sharedDirector()->getRunningScene();//m_pOutScene 记录过渡前场景 if (m_pOutScene == NULL)//如果过渡前场景为空,则创建个,并init { m_pOutScene = CCScene::create(); m_pOutScene->init(); } m_pOutScene->retain();//retain过渡前场景 AAA CCAssert( m_pInScene != m_pOutScene, "Incoming scene must be different from the outgoing scene" ); sceneOrder();//场景排序 return true; } else { return false; } }sceneOrder(); 这个方法是干嘛的呢?看下源码
void CCTransitionScene::sceneOrder() { m_bIsInSceneOnTop = true;//字面意思上,理解为过渡后场景在上 }原来是设置m_bIsInSceneOnTop为true,
那这个m_bIsInSceneOnTop具体有什么作用呢?
查看其调用,发现其主要作用是
void CCTransitionScene::draw() { CCScene::draw(); if( m_bIsInSceneOnTop ) { m_pOutScene->visit();//过渡前场景 m_pInScene->visit();//过渡后场景 } else { m_pInScene->visit();//过渡后场景 m_pOutScene->visit();//过渡前场景 } }过渡场景draw的时候,用m_bIsInSceneOnTop来控制两个场景的访问顺序。
再看replaceScene(cocos2d::CCTransition特效::create(time, newScene));这个方法,其他跟之前一样
不过 m_pNextScene = pScene;这一句使得m_pNextScene 指向为一个继承自CCTransitionScene的对象
然后因为m_pNextScene 不为空,主线程会调用CCDirector的void CCDirector::setNextScene(void)方法,
再逐句分析下 CCDirector::setNextScene(void)方法,会有以下调用结果:
void CCDirector::setNextScene(void) { bool runningIsTransition = dynamic_cast<CCTransitionScene*>(m_pRunningScene) != NULL;//该值为false bool newIsTransition = dynamic_cast<CCTransitionScene*>(m_pNextScene) != NULL;//因为之前m_pNextScene被赋值,此处newIsTransition 为true // 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)//过渡前场景release,注:这个场景不会被释放掉,因为在AAA ,该场景被retain { m_pRunningScene->release(); } m_pRunningScene = m_pNextScene;//m_pRunningScene 指向m_pNextScene,即那个过渡场景 m_pNextScene->retain();//过渡场景retain m_pNextScene = NULL;//m_pNextScene 指针置空,其实就是把主线程里调用该方法的开关关了 if ((! runningIsTransition) && m_pRunningScene)//条件成立 { m_pRunningScene->onEnter();//调用过渡场景onEnter方法 m_pRunningScene->onEnterTransitionDidFinish();//调用过渡场景onEnterTransitionDidFinish方法 } }
void CCTransitionFadeTR::onEnter() { CCTransitionScene::onEnter(); CCSize s = CCDirector::sharedDirector()->getWinSize(); float aspect = s.width / s.height; int x = (int)(12 * aspect); int y = 12; CCActionInterval* action = actionWithSize(CCSizeMake(x,y)); m_pOutScene->runAction ( CCSequence::create ( easeActionWithAction(action), CCCallFunc::create(this, callfunc_selector(CCTransitionScene::finish)), CCStopGrid::create(), NULL ) ); }本人查看了大部分的特效OnEnter,基本实现特效的一些设置,但是都有一个共同点,就是都有这一句
CCCallFunc::create(this, callfunc_selector(CCTransitionScene::finish))
意思是在,执行完过渡之后调用CCTransitionScene::finish方法。
当然还有调用了父类的CCTransitionScene的onEnter 方法
// custom onEnter void CCTransitionScene::onEnter() { CCScene::onEnter();//父类OnEnter // disable events while transitions CCDirector::sharedDirector()->getTouchDispatcher()->setDispatchEvents(false);//过渡期间关闭触摸事件响应 // outScene should not receive the onEnter callback // only the onExitTransitionDidStart m_pOutScene->onExitTransitionDidStart();//调用过度前场景的onExitTransitionDidStart方法 m_pInScene->onEnter();//调用过渡后场景的onEnter方法 }
再来看看CCTransitionScene::finish方法
void CCTransitionScene::finish() { // clean up m_pInScene->setVisible(true); m_pInScene->setPosition(ccp(0,0)); m_pInScene->setScale(1.0f); m_pInScene->setRotation(0.0f); m_pInScene->getCamera()->restore(); m_pOutScene->setVisible(false); m_pOutScene->setPosition(ccp(0,0)); m_pOutScene->setScale(1.0f); m_pOutScene->setRotation(0.0f); m_pOutScene->getCamera()->restore(); //[self schedule:@selector(setNewScene:) interval:0]; this->schedule(schedule_selector(CCTransitionScene::setNewScene), 0); }
来看看
void CCTransitionScene::setNewScene(float dt) { CC_UNUSED_PARAM(dt); this->unschedule(schedule_selector(CCTransitionScene::setNewScene)); // Before replacing, save the "send cleanup to scene" CCDirector *director = CCDirector::sharedDirector(); m_bIsSendCleanupToScene = director->isSendCleanupToScene(); director->replaceScene(m_pInScene);//切换场景到过渡后场景 // issue #267 m_pOutScene->setVisible(true); }该方法也主要是调用了CCDirector的replaceScene方法来将过渡后场景替换为新场景
replaceScene就不研究了,主要是把 m_pNextScene指向到m_pInScene过渡后场景
m_pNextScene不为空,然后在主线程当中,又会调用CCDirector的setNextScene()方法
void CCDirector::setNextScene(void) { bool runningIsTransition = dynamic_cast<CCTransitionScene*>(m_pRunningScene) != NULL;//这个为true,过渡场景即为当前场景 bool newIsTransition = dynamic_cast<CCTransitionScene*>(m_pNextScene) != NULL;//false,新场景不是过渡场景 // If it is not a transition, call onExit/cleanup if (! newIsTransition)//true { if (m_pRunningScene) { m_pRunningScene->onExitTransitionDidStart();//过渡场景onExitTransitionDidStart m_pRunningScene->onExit();//过渡场景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();//过渡场景cleanup } } if (m_pRunningScene) { m_pRunningScene->release();//释放过渡场景 } m_pRunningScene = m_pNextScene;//当前场景设置为过渡后场景 m_pNextScene->retain();//过渡后场景retain = NULL;//m_pNextScene 指针置空,其实就是把主线程里调用该方法的开关关了 if ((! runningIsTransition) && m_pRunningScene)//这个为false,不进入 { m_pRunningScene->onEnter(); m_pRunningScene->onEnterTransitionDidFinish(); } }来看下过渡场景的onExit()方法
// custom onExit void CCTransitionScene::onExit() { CCScene::onExit();//父类onExit方法 // enable events while transitions CCDirector::sharedDirector()->getTouchDispatcher()->setDispatchEvents(true);//开启触摸事件 m_pOutScene->onExit();//过渡前场景onExit // m_pInScene should not receive the onEnter callback // only the onEnterTransitionDidFinish m_pInScene->onEnterTransitionDidFinish();//过渡后场景onEnterTransitionDidFinish }
好了,来总结下流程吧:
newScene: init()
TransitionScene: create()
initWithDuration(float t, CCScene *scene)
newScene: retain()
oldScene: retain()
oldScene : release()
TransitionScene : retain()
onEnter()
oldScene: onExitTransitionDidStart()
newScene: onEnter()
TransitionScene : onEnterTransitionDidFinish()
过渡特效...
TransitionScene : onExitTransitionDidStart()
onExit()
oldScene: onExit()
newScene: onEnterTransitionDidFinish()
TransitionScene : cleanup()
release()
oldScene: release()
newScene: release()
retain()
大致是这样的流程,有些可能有错误,尤其最后,release什么的,因为是auto release,所以应该是下一帧释放
能够理解上面的流程,其实很多问题就都能够解决了~
说下我遇到的问题,我想将一个layer加到scene上去,我一般是这么做的,就是直接将layer加到当前scene上去,就是m_pRunningScene,这样做的好处是,我不需要每次指定哪个scene~直接添加就好,坏处也在这里,我想将layer加到新场景里,在新场景init的时候,我将layer加上去,但是这时候,m_pRunningScene指向的是旧scene
解决办法嘛,将layer在新场景onEnter的时候加到m_pRunningScene上去,这样可不可以呢?
在没有切换特效的时候OK
那么有切换特效的时候呢,找到newScene: onEnter() 这个时候的m_pRunningScene指向谁?可以看到这个时候指向过渡场景,所以,也是不行的,有没有什么办法解决呢?
我的想法是在onEnter里面加入,然后先判断m_pRunningScene是不是过渡场景,如果不是,则将layer加入到m_pRunningScene下;如果是,则将layer加入到过渡场景m_pRunningScene的下一个场景m_pInScene。但是m_pInScene是protected类型,无法访问。
所以还是只能直接指定layer确切的需要加入到哪个scene上去