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/cleanupif (! 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(); // 弹栈栈顶scenessize_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? -> endif (level == 0){end();return;}// current level or lower -> nothingif (level >= c)return;auto firstOnStackScene = _scenesStack.back();if (firstOnStackScene == _runningScene){......_scenesStack.popBack();--c;}// pop stack until reaching desired levelwhile (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。
// 弹栈直到只剩一个scenevoid 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栈空,相当于执行runWithScenerunWithScene(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; // 下一帧执行切换}
