cocos2dx中,一个场景是一个以Scene对象为根节点的UI树。任意时刻最多只有一个scene在运行,渲染就是从Scene节点开始的。

Scene栈

cocos使用栈来保存scene,栈顶的scene为当前在绘制的scene(runningScene)。如果想栈顶压栈一个scene,那么runningScene将从之前的栈顶scene切换到新的栈顶scene。

pushScene

压栈scene,不会立刻切换scene,而是等到下一帧执行切换。

  1. void Director::pushScene(Scene *scene)
  2. {
  3. CCASSERT(scene, "the scene should not null");
  4. _sendCleanupToScene = false; // 场景切换的时候,当前running scene要不要cleanup。
  5. // 即stopAllAction、unScheduleAllCallback
  6. ......
  7. _scenesStack.pushBack(scene); // 压栈
  8. _nextScene = scene; // 在下一帧执行切换
  9. }
  10. void Director::drawScene() // 每帧调用
  11. {
  12. ......
  13. _eventDispatcher->dispatchEvent(_eventBeforeDraw); // 可以看到是在下一帧绘制前执行场景切换
  14. if (_nextScene) {
  15. setNextScene();
  16. }
  17. ......
  18. }
  19. void Director::setNextScene() { // 执行场景切换
  20. // 这里还发布了before、after事件
  21. _eventDispatcher->dispatchEvent(_beforeSetNextScene);
  22. bool runningIsTransition = dynamic_cast<TransitionScene*>(_runningScene) != nullptr;
  23. bool newIsTransition = dynamic_cast<TransitionScene*>(_nextScene) != nullptr;
  24. // If it is not a transition, call onExit/cleanup
  25. if (! newIsTransition) // transitionScene自己内部完成了切换逻辑,个人觉得这个设计让逻辑复杂了,不合理。
  26. if (_runningScene) // 先执行running scene的onExit
  27. {
  28. _runningScene->onExitTransitionDidStart();
  29. _runningScene->onExit();
  30. }
  31. // issue #709. the root node (scene) should receive the cleanup message too
  32. // otherwise it might be leaked.
  33. if (_sendCleanupToScene && _runningScene)
  34. {
  35. _runningScene->cleanup();
  36. }
  37. }
  38. if (_runningScene)
  39. {
  40. _runningScene->release();
  41. }
  42. _runningScene = _nextScene;
  43. _nextScene->retain();
  44. _nextScene = nullptr;
  45. if ((! runningIsTransition) && _runningScene) // 然后执行next scene的onEnter
  46. {
  47. _runningScene->onEnter();
  48. _runningScene->onEnterTransitionDidFinish();
  49. }
  50. _eventDispatcher->dispatchEvent(_afterSetNextScene);
  51. }

runWithScene

特殊版的pushScene,调用时,scene栈必须是空的,也即是没有running scene。
使用场景:运行程序第一个scene。

  1. void Director::runWithScene(Scene *scene)
  2. {
  3. CCASSERT(scene != nullptr, "This command can only be used to start the Director. There is already a scene present.");
  4. CCASSERT(_runningScene == nullptr, "_runningScene should be null");
  5. pushScene(scene);
  6. startAnimation();
  7. }

popScene

弹栈,running scene切换到栈顶-1的scene,不会立即执行,同样是等到下一帧执行。

  1. void Director::popScene(void)
  2. {
  3. // 调用时,栈不能为空。
  4. CCASSERT(_runningScene != nullptr, "running scene should not null");
  5. ......
  6. _scenesStack.popBack(); // 弹栈栈顶scene
  7. ssize_t c = _scenesStack.size();
  8. if (c == 0) // 栈空,退出程序
  9. {
  10. end();
  11. }
  12. else // 切换到栈顶-1的scene
  13. {
  14. _sendCleanupToScene = true;
  15. _nextScene = _scenesStack.at(c - 1);
  16. }
  17. }

popToSceneStackLevel

弹栈到指定scene。

  1. // popToSceneStackLevel(0); // 弹栈到栈内没有scene,最终退出程序。
  2. // popToSceneStackLevel(1); // 弹栈到栈内只剩最底的scene。
  3. void Director::popToSceneStackLevel(int level)
  4. {
  5. CCASSERT(_runningScene != nullptr, "A running Scene is needed");
  6. ssize_t c = _scenesStack.size();
  7. // level 0? -> end
  8. if (level == 0)
  9. {
  10. end();
  11. return;
  12. }
  13. // current level or lower -> nothing
  14. if (level >= c)
  15. return;
  16. auto firstOnStackScene = _scenesStack.back();
  17. if (firstOnStackScene == _runningScene)
  18. {
  19. ......
  20. _scenesStack.popBack();
  21. --c;
  22. }
  23. // pop stack until reaching desired level
  24. while (c > level)
  25. {
  26. auto current = _scenesStack.back();
  27. if (current->isRunning())
  28. {
  29. current->onExit();
  30. }
  31. current->cleanup();
  32. ......
  33. _scenesStack.popBack();
  34. --c;
  35. }
  36. _nextScene = _scenesStack.back();
  37. // cleanup running scene
  38. _sendCleanupToScene = true;
  39. }

popToRootScene

弹栈到只剩栈底的scene。

  1. // 弹栈直到只剩一个scene
  2. void Director::popToRootScene(void)
  3. {
  4. popToSceneStackLevel(1);
  5. }

replaceScene

替换栈顶的scene,即替换runningScene。

  1. void Director::replaceScene(Scene *scene)
  2. {
  3. //CCASSERT(_runningScene, "Use runWithScene: instead to start the director");
  4. CCASSERT(scene != nullptr, "the scene should not be null");
  5. if (_runningScene == nullptr) { // 如果scene栈空,相当于执行runWithScene
  6. runWithScene(scene);
  7. return;
  8. }
  9. if (scene == _nextScene)
  10. return;
  11. if (_nextScene) // 如果正打算切换到_nextScene,立刻让这个scene退出
  12. {
  13. if (_nextScene->isRunning())
  14. {
  15. _nextScene->onExit();
  16. }
  17. _nextScene->cleanup();
  18. _nextScene = nullptr;
  19. }
  20. ssize_t index = _scenesStack.size() - 1; // 栈顶scene索引
  21. _sendCleanupToScene = true;
  22. ......
  23. _scenesStack.replace(index, scene); // 替换栈顶scene
  24. _nextScene = scene; // 下一帧执行切换
  25. }