首先,调用切换场景的函数是: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;
}
1.m_pobScenesStack是CCDirector里面场景的数组
查看一下其在其他地方的调用,发现其主要的作用是用在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();
}
有这一段,意思是设置了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方法
}
}
下面看看调用过渡场景的onEnter方法里做了什么?上面举得例子CCTransitionFadeUp没有重写OnEnter方法,其父类CCTransitionFadeTR有重写
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方法
}
那调用过渡场景的onEnterTransitionDidFinish方法里做了什么?这个方法,过渡场景类没有重写,就不看了
再来看看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);
}
没什么看的,主要进行了切换完之后两个场景的一些恢复工作,最后调用CCTransitionScene::setNewScene方法
来看看
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上去