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和onExitTransitionDidStart
virtual void onEnter() override;
virtual void onExit() override;
virtual void cleanup() override;
protected:
virtual void sceneOrder();
void setNewScene(float dt); // 替换Director的场景切换逻辑
Scene *_inScene; // 要切换进来的scene
Scene *_outScene; // 被切换的scene,即是runningScene
float _duration; // 切换动画持续时间
bool _isInSceneOnTop; // inScene的画面是不是在outScene上面,后绘制的画面会覆盖到前面的画面,有在上面的感觉。
bool _isSendCleanupToScene; // outScene别切换出去要不要执行cleanup
......
};
// 分别绘制inScene和outScene
void 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为inScene
this->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 onEnter
void 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 onExit
void 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: runningScene
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
- TransitionScene::setNewScene
**
抽出只与三个Scene相关的过程调用:
- transitionScene::onEnter()
- runningScene::onExitTransitionDidStart
- newScene::onEnter()
- transitionScene::onEnterTransitionDidFinish
- transitionScene::onExitTransitionDidStart
- transitionScene::onExit
- runningScene::onExit
- newScene::onEnterTransitionDidFinish
- transitionScene::cleanup