cocos2dx中,一个场景是一个以Scene对象为根节点的UI树。任意时刻最多只有一个scene在运行,渲染就是从Scene节点开始的。
Scene栈
cocos使用栈来保存scene,栈顶的scene为当前在绘制的scene(runningScene)。如果想栈顶压栈一个scene,那么runningScene将从之前的栈顶scene切换到新的栈顶scene。
pushScene
压栈scene,不会立刻切换scene,而是等到下一帧执行切换。
void Director::pushScene(Scene *scene)
{
CCASSERT(scene, "the scene should not null");
_sendCleanupToScene = false; // 场景切换的时候,当前running scene要不要cleanup。
// 即stopAllAction、unScheduleAllCallback
......
_scenesStack.pushBack(scene); // 压栈
_nextScene = scene; // 在下一帧执行切换
}
void Director::drawScene() // 每帧调用
{
......
_eventDispatcher->dispatchEvent(_eventBeforeDraw); // 可以看到是在下一帧绘制前执行场景切换
if (_nextScene) {
setNextScene();
}
......
}
void Director::setNextScene() { // 执行场景切换
// 这里还发布了before、after事件
_eventDispatcher->dispatchEvent(_beforeSetNextScene);
bool runningIsTransition = dynamic_cast<TransitionScene*>(_runningScene) != nullptr;
bool newIsTransition = dynamic_cast<TransitionScene*>(_nextScene) != nullptr;
// If it is not a transition, call onExit/cleanup
if (! newIsTransition) // transitionScene自己内部完成了切换逻辑,个人觉得这个设计让逻辑复杂了,不合理。
if (_runningScene) // 先执行running scene的onExit
{
_runningScene->onExitTransitionDidStart();
_runningScene->onExit();
}
// issue #709. the root node (scene) should receive the cleanup message too
// otherwise it might be leaked.
if (_sendCleanupToScene && _runningScene)
{
_runningScene->cleanup();
}
}
if (_runningScene)
{
_runningScene->release();
}
_runningScene = _nextScene;
_nextScene->retain();
_nextScene = nullptr;
if ((! runningIsTransition) && _runningScene) // 然后执行next scene的onEnter
{
_runningScene->onEnter();
_runningScene->onEnterTransitionDidFinish();
}
_eventDispatcher->dispatchEvent(_afterSetNextScene);
}
runWithScene
特殊版的pushScene,调用时,scene栈必须是空的,也即是没有running scene。
使用场景:运行程序第一个scene。
void Director::runWithScene(Scene *scene)
{
CCASSERT(scene != nullptr, "This command can only be used to start the Director. There is already a scene present.");
CCASSERT(_runningScene == nullptr, "_runningScene should be null");
pushScene(scene);
startAnimation();
}
popScene
弹栈,running scene切换到栈顶-1的scene,不会立即执行,同样是等到下一帧执行。
void Director::popScene(void)
{
// 调用时,栈不能为空。
CCASSERT(_runningScene != nullptr, "running scene should not null");
......
_scenesStack.popBack(); // 弹栈栈顶scene
ssize_t c = _scenesStack.size();
if (c == 0) // 栈空,退出程序
{
end();
}
else // 切换到栈顶-1的scene
{
_sendCleanupToScene = true;
_nextScene = _scenesStack.at(c - 1);
}
}
popToSceneStackLevel
弹栈到指定scene。
// popToSceneStackLevel(0); // 弹栈到栈内没有scene,最终退出程序。
// popToSceneStackLevel(1); // 弹栈到栈内只剩最底的scene。
void Director::popToSceneStackLevel(int level)
{
CCASSERT(_runningScene != nullptr, "A running Scene is needed");
ssize_t c = _scenesStack.size();
// level 0? -> end
if (level == 0)
{
end();
return;
}
// current level or lower -> nothing
if (level >= c)
return;
auto firstOnStackScene = _scenesStack.back();
if (firstOnStackScene == _runningScene)
{
......
_scenesStack.popBack();
--c;
}
// pop stack until reaching desired level
while (c > level)
{
auto current = _scenesStack.back();
if (current->isRunning())
{
current->onExit();
}
current->cleanup();
......
_scenesStack.popBack();
--c;
}
_nextScene = _scenesStack.back();
// cleanup running scene
_sendCleanupToScene = true;
}
popToRootScene
弹栈到只剩栈底的scene。
// 弹栈直到只剩一个scene
void Director::popToRootScene(void)
{
popToSceneStackLevel(1);
}
replaceScene
替换栈顶的scene,即替换runningScene。
void Director::replaceScene(Scene *scene)
{
//CCASSERT(_runningScene, "Use runWithScene: instead to start the director");
CCASSERT(scene != nullptr, "the scene should not be null");
if (_runningScene == nullptr) { // 如果scene栈空,相当于执行runWithScene
runWithScene(scene);
return;
}
if (scene == _nextScene)
return;
if (_nextScene) // 如果正打算切换到_nextScene,立刻让这个scene退出
{
if (_nextScene->isRunning())
{
_nextScene->onExit();
}
_nextScene->cleanup();
_nextScene = nullptr;
}
ssize_t index = _scenesStack.size() - 1; // 栈顶scene索引
_sendCleanupToScene = true;
......
_scenesStack.replace(index, scene); // 替换栈顶scene
_nextScene = scene; // 下一帧执行切换
}