Scene是UI树的根节点,正常情况下,是不会同时绘制两个Scene,因此也就无法做到Scene间的延时切换(有动画的切换),因为这必须要同时绘制两个Scene。cocos引擎设计了TransitionScene来支持同时绘制两个Scene。
它的设计思路就是,自定义一种特殊的Scene,叫TransitionScene,它包含两个普通Scene:
- inScene:要切换成的新场景
- outScene:被切换的当前场景
绘制TransitionScene其实就是绘制它的inScene和outScene。至于切换动画,这个和普通节点的Action动画一样执行。
UML
TransitionScene模块的UML如下:
点击查看【processon】
使用示例
// 需求:设置NewScene的入场动画auto newScene = NewScene::create();auto time = 1.5f; // 入场动画时间auto transitionScene = TransitionScene::create(time, newScene); // inScene: newScene// outScene: runningScene// 下面这一步,将runningscene设置为transitionScene,引擎将渲染transitionScene,// 而transitionScene的渲染逻辑是渲染inScene和outScene。// 这里并不会触发runningScene的onExit和nextScene的onEnter,而是改由transitionScene内部控制。Director::getInstance()->replaceScene(transitionScene);
切换效果
JumpZoom
Progress效果
TransitionProgressRadialCW、TransitionProgressRadialCCW:
TransitionProgressHorizontal、TransitionProgressVertical:
TransitionProgressInOut、TransitionProgressOutIn:
淡化效果
TransitionCrossFade:
TransitionFade、FadeWhiteTransition:
- outScene fade out,屏幕黑色,然后fade in inScene
- FadeWhiteTransition,fade out之后,屏幕白色
TransitionFadeTR、TransitionFadeBL:
TransitionFadeUp、TransitionFadeDown:
翻书效果
PageTransitionForward、PageTransitionBackward
瓦片效果
Split分裂效果
TransitionSplitRows、TransitionSplitCols:
Flip翻转效果
FlipXLeftOver
FlipXRightOver
FlipYUpOver
FlipYDownOver
FlipAngularLeftOver
FlipAngularRightOver
ZoomFlipXLeftOver
ZoomFlipXRightOver
ZoomFlipYUpOver
ZoomFlipYDownOver
ZoomFlipAngularLeftOver
ZoomFlipAngularRightOver
TransitionShrinkGrow
TransitionRotoZoom
滑动效果
TransitionMoveInL
TransitionMoveInR
TransitionMoveInT
TransitionMoveInB
TransitionSlideInL
TransitionSlideInR
TransitionSlideInT
TransitionSlideInB
源码分析
class CC_DLL TransitionScene : public Scene{public:// 场景切换结束回调void finish(void);// 隐藏outScene、显示inScene,有用子类的动画有这个需求void hideOutShowIn(void);// 这是TransitionScene的实现基础,在这里实现对inScene和outScene的绘制// 这样才能做到同时绘制两个Scene。virtual void draw(Renderer *renderer, const Mat4 &transform, uint32_t flags) override;// 在这里相应调用inScene的onEnter和onEnterTransitionDidFinish、outScene的onExit和onExitTransitionDidStartvirtual void onEnter() override;virtual void onExit() override;virtual void cleanup() override;protected:virtual void sceneOrder();void setNewScene(float dt); // 替换Director的场景切换逻辑Scene *_inScene; // 要切换进来的sceneScene *_outScene; // 被切换的scene,即是runningScenefloat _duration; // 切换动画持续时间bool _isInSceneOnTop; // inScene的画面是不是在outScene上面,后绘制的画面会覆盖到前面的画面,有在上面的感觉。bool _isSendCleanupToScene; // outScene别切换出去要不要执行cleanup......};// 分别绘制inScene和outScenevoid TransitionScene::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags){Scene::draw(renderer, transform, flags);if( _isInSceneOnTop ) {_outScene->visit(renderer, transform, flags);_inScene->visit(renderer, transform, flags);} else {_inScene->visit(renderer, transform, flags);_outScene->visit(renderer, transform, flags);}}void TransitionScene::finish() // 切换动画结束,做一些首尾工作{// clean up_inScene->setVisible(true);_inScene->setPosition(0,0);_inScene->setScale(1.0f);_inScene->setRotation(0.0f);_inScene->setAdditionalTransform(nullptr);_outScene->setVisible(false);_outScene->setPosition(0,0);_outScene->setScale(1.0f);_outScene->setRotation(0.0f);_outScene->setAdditionalTransform(nullptr);// 动画执行完毕之后,在下一帧切换runningScene为inScenethis->schedule(CC_SCHEDULE_SELECTOR(TransitionScene::setNewScene), 0);}void TransitionScene::setNewScene(float /*dt*/){this->unschedule(CC_SCHEDULE_SELECTOR(TransitionScene::setNewScene));// Before replacing, save the "send cleanup to scene"Director *director = Director::getInstance();_isSendCleanupToScene = director->isSendCleanupToScene();director->replaceScene(_inScene); // runningScene正式切换为inScene......// issue #267_outScene->setVisible(true);}// custom onEntervoid TransitionScene::onEnter(){......Scene::onEnter();// disable events while transitions_eventDispatcher->setEnabled(false);// outScene should not receive the onEnter callback// only the onExitTransitionDidStart_outScene->onExitTransitionDidStart();_inScene->onEnter();}// custom onExitvoid TransitionScene::onExit(){......Scene::onExit();// enable events while transitions_eventDispatcher->setEnabled(true);_outScene->onExit();// _inScene should not receive the onEnter callback// only the onEnterTransitionDidFinish_inScene->onEnterTransitionDidFinish();......}
总结一下下面代码的场景切换过程:
// 假设runningScene不为空,runningScene不是TransitionScene子类对象auto runningScene = Director::getInstance()->getRunningScene();auto newScene = NewScene::create();auto transitionScene = TransitionScene::create(1.5f, newScene); // inScene: newScene// outScene: runningSceneDirector::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
- TransitionScene::setNewScene
**
抽出只与三个Scene相关的过程调用:
- transitionScene::onEnter()
- runningScene::onExitTransitionDidStart
- newScene::onEnter()
- transitionScene::onEnterTransitionDidFinish
- transitionScene::onExitTransitionDidStart
- transitionScene::onExit
- runningScene::onExit
- newScene::onEnterTransitionDidFinish
- transitionScene::cleanup
