Scene是UI树的根节点,正常情况下,是不会同时绘制两个Scene,因此也就无法做到Scene间的延时切换(有动画的切换),因为这必须要同时绘制两个Scene。cocos引擎设计了TransitionScene来支持同时绘制两个Scene。
它的设计思路就是,自定义一种特殊的Scene,叫TransitionScene,它包含两个普通Scene:

  • inScene:要切换成的新场景
  • outScene:被切换的当前场景

绘制TransitionScene其实就是绘制它的inScene和outScene。至于切换动画,这个和普通节点的Action动画一样执行。

UML

TransitionScene模块的UML如下:
点击查看【processon】

使用示例

  1. // 需求:设置NewScene的入场动画
  2. auto newScene = NewScene::create();
  3. auto time = 1.5f; // 入场动画时间
  4. auto transitionScene = TransitionScene::create(time, newScene); // inScene: newScene
  5. // outScene: runningScene
  6. // 下面这一步,将runningscene设置为transitionScene,引擎将渲染transitionScene,
  7. // 而transitionScene的渲染逻辑是渲染inScene和outScene。
  8. // 这里并不会触发runningScene的onExit和nextScene的onEnter,而是改由transitionScene内部控制。
  9. Director::getInstance()->replaceScene(transitionScene);

切换效果

JumpZoom

TransitionJumpZoom。
GIF.gif

Progress效果

TransitionProgressRadialCW、TransitionProgressRadialCCW:
image.png

TransitionProgressHorizontal、TransitionProgressVertical:
image.png

TransitionProgressInOut、TransitionProgressOutIn:
image.png

淡化效果

TransitionCrossFade:
image.png

TransitionFade、FadeWhiteTransition:

  • outScene fade out,屏幕黑色,然后fade in inScene
  • FadeWhiteTransition,fade out之后,屏幕白色

TransitionFadeTR、TransitionFadeBL:
image.png

TransitionFadeUp、TransitionFadeDown:
image.png

翻书效果

PageTransitionForward、PageTransitionBackward

瓦片效果

TransitionTurnOffTiles:
image.png

Split分裂效果

TransitionSplitRows、TransitionSplitCols:
image.png

Flip翻转效果

FlipXLeftOver
FlipXRightOver
FlipYUpOver
FlipYDownOver
image.png

FlipAngularLeftOver
FlipAngularRightOver
image.png

ZoomFlipXLeftOver
ZoomFlipXRightOver
ZoomFlipYUpOver
ZoomFlipYDownOver
image.png

ZoomFlipAngularLeftOver
ZoomFlipAngularRightOver
image.png

TransitionShrinkGrow

image.png

TransitionRotoZoom

image.png

滑动效果

TransitionMoveInL
TransitionMoveInR
TransitionMoveInT
TransitionMoveInB
image.png

TransitionSlideInL
TransitionSlideInR
TransitionSlideInT
TransitionSlideInB
image.png

源码分析

  1. class CC_DLL TransitionScene : public Scene
  2. {
  3. public:
  4. // 场景切换结束回调
  5. void finish(void);
  6. // 隐藏outScene、显示inScene,有用子类的动画有这个需求
  7. void hideOutShowIn(void);
  8. // 这是TransitionScene的实现基础,在这里实现对inScene和outScene的绘制
  9. // 这样才能做到同时绘制两个Scene。
  10. virtual void draw(Renderer *renderer, const Mat4 &transform, uint32_t flags) override;
  11. // 在这里相应调用inScene的onEnter和onEnterTransitionDidFinish、outScene的onExit和onExitTransitionDidStart
  12. virtual void onEnter() override;
  13. virtual void onExit() override;
  14. virtual void cleanup() override;
  15. protected:
  16. virtual void sceneOrder();
  17. void setNewScene(float dt); // 替换Director的场景切换逻辑
  18. Scene *_inScene; // 要切换进来的scene
  19. Scene *_outScene; // 被切换的scene,即是runningScene
  20. float _duration; // 切换动画持续时间
  21. bool _isInSceneOnTop; // inScene的画面是不是在outScene上面,后绘制的画面会覆盖到前面的画面,有在上面的感觉。
  22. bool _isSendCleanupToScene; // outScene别切换出去要不要执行cleanup
  23. ......
  24. };
  25. // 分别绘制inScene和outScene
  26. void TransitionScene::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
  27. {
  28. Scene::draw(renderer, transform, flags);
  29. if( _isInSceneOnTop ) {
  30. _outScene->visit(renderer, transform, flags);
  31. _inScene->visit(renderer, transform, flags);
  32. } else {
  33. _inScene->visit(renderer, transform, flags);
  34. _outScene->visit(renderer, transform, flags);
  35. }
  36. }
  37. void TransitionScene::finish() // 切换动画结束,做一些首尾工作
  38. {
  39. // clean up
  40. _inScene->setVisible(true);
  41. _inScene->setPosition(0,0);
  42. _inScene->setScale(1.0f);
  43. _inScene->setRotation(0.0f);
  44. _inScene->setAdditionalTransform(nullptr);
  45. _outScene->setVisible(false);
  46. _outScene->setPosition(0,0);
  47. _outScene->setScale(1.0f);
  48. _outScene->setRotation(0.0f);
  49. _outScene->setAdditionalTransform(nullptr);
  50. // 动画执行完毕之后,在下一帧切换runningScene为inScene
  51. this->schedule(CC_SCHEDULE_SELECTOR(TransitionScene::setNewScene), 0);
  52. }
  53. void TransitionScene::setNewScene(float /*dt*/)
  54. {
  55. this->unschedule(CC_SCHEDULE_SELECTOR(TransitionScene::setNewScene));
  56. // Before replacing, save the "send cleanup to scene"
  57. Director *director = Director::getInstance();
  58. _isSendCleanupToScene = director->isSendCleanupToScene();
  59. director->replaceScene(_inScene); // runningScene正式切换为inScene
  60. ......
  61. // issue #267
  62. _outScene->setVisible(true);
  63. }
  64. // custom onEnter
  65. void TransitionScene::onEnter()
  66. {
  67. ......
  68. Scene::onEnter();
  69. // disable events while transitions
  70. _eventDispatcher->setEnabled(false);
  71. // outScene should not receive the onEnter callback
  72. // only the onExitTransitionDidStart
  73. _outScene->onExitTransitionDidStart();
  74. _inScene->onEnter();
  75. }
  76. // custom onExit
  77. void TransitionScene::onExit()
  78. {
  79. ......
  80. Scene::onExit();
  81. // enable events while transitions
  82. _eventDispatcher->setEnabled(true);
  83. _outScene->onExit();
  84. // _inScene should not receive the onEnter callback
  85. // only the onEnterTransitionDidFinish
  86. _inScene->onEnterTransitionDidFinish();
  87. ......
  88. }

总结一下下面代码的场景切换过程:

  1. // 假设runningScene不为空,runningScene不是TransitionScene子类对象
  2. auto runningScene = Director::getInstance()->getRunningScene();
  3. auto newScene = NewScene::create();
  4. auto transitionScene = TransitionScene::create(1.5f, newScene); // inScene: newScene
  5. // outScene: runningScene
  6. Director::getInstance()->replaceScene(transitionScene);
  • 下一帧
    • Director::setNewScene,runningScene切换为transitionScene,下面的runningScene都中上面代码中的runningScene
    • transitionScene::onEnter()
      • runningScene::onExitTransitionDidStart
      • newScene::onEnter()
      • 执行newScene的入场动画、runningScene的离场动画
    • transitionScene::onEnterTransitionDidFinish()
    • 若干帧之后
      • 动画结束,执行transitionScene::finish,表示动画结束回调
      • 下一帧
        • TransitionScene::setNewScene
          • director->replaceScene(newScene)
          • 下一帧
            • 执行director::setNextScene(),runningScene切换为newScene
            • transitionScene::onExitTransitionDidStart
            • transitionScene::onExit
              • runningScene::onExit
              • newScene::onEnterTransitionDidFinish
            • transitionScene::cleanup

**
抽出只与三个Scene相关的过程调用:

  • transitionScene::onEnter()
    • runningScene::onExitTransitionDidStart
    • newScene::onEnter()
  • transitionScene::onEnterTransitionDidFinish
  • transitionScene::onExitTransitionDidStart
  • transitionScene::onExit
    • runningScene::onExit
    • newScene::onEnterTransitionDidFinish
  • transitionScene::cleanup