1. 简介

过渡场景TransitionScene直接继承了场景Scene。能够在场景切换过程中实现“过渡”效果,而不是让窗口在下一帧突然展示另一个场景。

2. create

构造函数:

TransitionScene::TransitionScene()
: _inScene(nullptr)
, _outScene(nullptr)
, _duration(0.0f)
, _isInSceneOnTop(false)
, _isSendCleanupToScene(false)
{
}

create方法有2个参数:float t, Scene *scene。调用了initWithDuration,传入这两个参数。

参数scene不能为空。

1. 成员变量时间_duration设为参数t,成员变量_inScene设为参数scene。过渡场景引用了参数场景,参数场景要retain。

2. 导演执行getRunningScene,把当前正执行的场景作为_outScene。

3. 如果当前正执行的场景为空,则调用Scene::create()创建一个空场景,空场景作为_outScene。

4. _outScene指向的场景被引用了,_outScene执行retain。

5. 执行sceneOrder(),决定inScene和outScene的绘制顺序。该方法可以被子类重写,只是设置了成员变量_isInSceneOnTop的值为true/false。

3. onEnter

场景的onEnter方法在该场景每次进入窗口时执行。

TransitionScene的子类会重写onEnter方法,重写后的方法体内会先调用父类TransitionScene的onEnter方法。

1. 首先调用父类Scene onEnter方法,实际上执行的是Scene的父类Node的onEnter方法。

2. 导演的事件分发器执行setEnabled(false),停止事件分发器的工作。

3. _outScene->onExitTransitionDidStart() 要退出的场景执行onExitTransitionDidStart。

4. _inScene->onEnter() 要出现的场景执行onEnter。

简而言之,过渡的开始时,要退出的准备退出,要出现的开始出现。

4. onExit()

场景的onExit方法在该场景每次退出窗口时执行。

1. 首先调用父类Scene onExit方法,实际上执行的是Scene的父类Node的onExit方法。

2. 导演的事件分发器执行setEnabled(true),事件分发器的可以分发事件了。

3. _outScene->onExit(); 要退出的场景执行onExit,完全退出。

4. _inScene->onEnterTransitionDidFinish() 要出现的场景执行onEnterTransitionDidFinish。

简而言之,过渡的结束时,要退出的完成退出,要进入的完成进入。此时,窗口只有我们设置的要进入的场景了。

5. draw(...)

过渡场景绘制方法的源码:

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);
}
}

可以看出,绘制根据_isInSceneOnTop决定inScene和outScene的绘制顺序。

构造函数默认false。在create方法会调用sceneOrder()设置。子类可以重写sceneOrder方法,实现TransitionScene不同子类的要进入场景和要退出场景绘制顺序的不同。

6. 子类 TransitionJumpZoom

TransitionScene是所有过渡场景的父类,每种子类都是一种有不同特效的过渡场景。

这次用子类TransitionJumpZoom为例进行学习。

该过渡场景除去构造和析构仅2个方法。该过渡场景重写了onEnter方法,说明在该方法包含了过渡特效的逻辑。

- TransitionJumpZoom::onEnter()

1. 首先执行父类TransitionScene::onEnter()。让outScene(runningScene)准备退出。

2. 设置inScene和outScene在过渡开始时的初始属性。

    _inScene->setScale(0.5f);
_inScene->setPosition(s.width, );
_inScene->setAnchorPoint(Vec2(0.5f, 0.5f));
_outScene->setAnchorPoint(Vec2(0.5f, 0.5f));

3.创建3个动作。有跳跃和缩放

    ActionInterval *jump = JumpBy::create(_duration/, Vec2(-s.width,), s.width/, );
ActionInterval *scaleIn = ScaleTo::create(_duration/, 1.0f);
ActionInterval *scaleOut = ScaleTo::create(_duration/, 0.5f);

4. 创建两个动作队列。

    auto jumpZoomOut = Sequence::create(scaleOut, jump, nullptr);
auto jumpZoomIn = Sequence::create(jump, scaleIn, nullptr);

5. 两个场景runAction。

    ActionInterval *delay = DelayTime::create(_duration/);

    _outScene->runAction(jumpZoomOut);
_inScene->runAction
(
Sequence::create
(
delay,
jumpZoomIn,
CallFunc::create(CC_CALLBACK_0(TransitionScene::finish,this)),
nullptr
)
);

在要进入的场景的动作序列结束前,执行父类的TransitionScene::finish。

- TransitionScene finish

简要的说,是设置inScene和outScene的属性。从而对屏幕的窗口而言,过渡的特效执行完成,inScene可见,outScene不可见,两场景的位置、旋转、缩放都设为了默认值。

在属性设置之后,执行:

    this->schedule(CC_SCHEDULE_SELECTOR(TransitionScene::setNewScene), );

该语句建了一个Timer,时间间隔为0,说明下一帧就要执行参数回调函数setNewScene。

- TransitionScene setNewScene

1. 首先unschedule了上一帧的Timer。

2. 把导演属性_sendCleanupToScene赋给TransitionScene属性_isSendCleanupToScene。

3. 执行导演的replaceScene(_inScene),将inScene作为了导演的nextScene。

4. 最后outScene可见。

7. 场景切换的运行过程

这里用replaceScene为例。

例子中,我们点击菜单项,触发replaceScene函数,参数是某个过渡场景。

过渡场景的开始 到onEnter()

1. 本帧的GLView接收到触摸事件(鼠标点击菜单项),触发了回调函数,回调函数中新建了一个新场景和过渡场景,过渡场景的inScene为执行导演的replaceScene,参数为新场景。

2. replaceScene中:导演_sendCleanupToScene置true,新场景入栈,新场景成为nextScene。nextScene原本为NULL。

3. 本帧结束。进入下一帧。

4. 下一帧在scheduler update之后判断到存在nextScene,执行setNextScene方法。

5. setNextScene:

void Director::setNextScene()
{
_eventDispatcher->dispatchEvent(_beforeSetNextScene); bool runningIsTransition = dynamic_cast<TransitionScene*>(_runningScene) != nullptr; // false
bool newIsTransition = dynamic_cast<TransitionScene*>(_nextScene) != nullptr; // true if (! newIsTransition) //新场景是过渡场景,不执行
{
if (_runningScene)
{
_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; //nextScene至此成为了runningScene if ((! runningIsTransition) && _runningScene) //曾经的runningscene为false,执行
{
_runningScene->onEnter(); //过渡场景的onEnter
_runningScene->onEnterTransitionDidFinish();
} _eventDispatcher->dispatchEvent(_afterSetNextScene);
}

6. 执行过渡场景的onEnter:

  1. 执行Node onEnter。

  2. 执行TransitionScene onEnter,导演的eventDispatcher停用,要退出的场景onExitTransitionDidStart,要进入的场景onEnter。

  3. 为inScene和outScene分别创建动作序列。分别runAction。

过渡场景的结束 到onExit()

1. 过渡场景中,新场景动作序列最后一个动作是回调动作CallFunc,调用TransitionScene finish(),在设置属性之后,在下一帧通过Scheduler调用setNewScene方法。

2. setNewScene中:replaceScene(inScene),实现了nextScene指向新场景。此时,runningScene是过渡场景。

3. 在下下一帧会执行导演setNextScene,因为runningScene和nextScene都存在,runningScene过渡场景执行onExitTransitionDidStart onExit。

bool runningIsTransition = dynamic_cast<TransitionScene*>(_runningScene) != nullptr; // true
bool newIsTransition = dynamic_cast<TransitionScene*>(_nextScene) != nullptr; //false if (! newIsTransition) //执行
{
if (_runningScene) //过渡场景onExit
{
_runningScene->onExitTransitionDidStart();
_runningScene->onExit();
} if (_sendCleanupToScene && _runningScene)
{
_runningScene->cleanup();
}
}
//......

过渡场景的onExit中,要退出的场景执行onExit,完全退出,要出现的场景执行onEnterTransitionDidFinish,完全进入。同时,导演的eventDispatcher启用。

cleanup()

继续setNextScene,_sendCleanupToScene在replaceScene时置true,过渡场景执行TransitionScene cleanup()。

    Scene::cleanup();
if( _isSendCleanupToScene )
_outScene->cleanup();

调用了Node cleanup()。_isSendCleanupToScene由导演_sendCleanupToScene设置,为true时,对要退出的场景cleanup()。

_sendCleanupToScene

在导演replaceScene时置true,导演pushScene置false,导演popScene置true。

为true时,在导演setNextScene时,对导演的runningScene cleanup(),然后runningScene被nextScene替代。

如果此时runnningScene是过渡场景,过渡场景的cleanup()除了对自己cleanup,还要通过_isSendCleanupToScene判断是否对要退出的场景cleanup。

_isSendCleanupToScene是在过渡场景动作结束时,导演_sendCleanupToScene直接赋值。

所以,导演用过渡场景作为replaceScene参数时,过渡场景和要退出的场景都会被cleanup。

‎Cocos2d-x 学习笔记(3.2) TransitionScene 过渡场景和场景切换的过程的更多相关文章

  1. Android(java)学习笔记160:Framework运行环境之 Android进程产生过程

    1.前面Android(java)学习笔记159提到Dalvik虚拟机启动初始化过程,就下来就是启动zygote进程: zygote进程是所有APK应用进程的父进程:每当执行一个Android应用程序 ...

  2. Android(java)学习笔记205:网易新闻RSS客户端应用编写逻辑过程

    1.我们的项目需求是编写一个新闻RSS浏览器,RSS(Really Simple Syndication)是一种描述和同步网站内容的格式,是使用最广泛的XML应用.RSS目前广泛用于网上新闻频道,bl ...

  3. 【Unity 3D】学习笔记三十五:游戏实例——摄像机切换镜头

    摄像机切换镜头 在游戏中常常会切换摄像机来观察某一个游戏对象,能够说.在3D游戏开发中,摄像头的切换是不可或缺的. 这次我们学习总结下摄像机怎么切换镜头. 代码: private var Camera ...

  4. Android(java)学习笔记103:Framework运行环境之 Android进程产生过程

    1. 前面Android(java)学习笔记159提到Dalvik虚拟机启动初始化过程,就下来就是启动zygote进程: zygote进程是所有APK应用进程的父进程:每当执行一个Android应用程 ...

  5. Android(java)学习笔记148:网易新闻RSS客户端应用编写逻辑过程

    1.我们的项目需求是编写一个新闻RSS浏览器,RSS(Really Simple Syndication)是一种描述和同步网站内容的格式,是使用最广泛的XML应用.RSS目前广泛用于网上新闻频道,bl ...

  6. .NET 5学习笔记(10)——Entity Framework Core之切换SQLServer和SQLite

    上一篇我们梳理了CodeFist的一般流程,本篇我们讨论如何在一套代码中,支持SQL Server和SQLite的切换.同时从本篇开始,我们从.NET Core 3.1 迁移到.NET 5.相信.NE ...

  7. CSS学习笔记2-2d变换和过渡属性

    前言:今天又是一个周末,心情不错,趁着闲暇之余,把剩下来的CSS3学习的内容全部整理出来,练习用的源码也稍微整理了一下. 2D转换 transform:translate||rotate||scale ...

  8. redis学习笔记——expire、pexpire、expireat、pexpireat的执行过程

    这里主要讲的Redis是怎么样设置过期键的,可以算作后续"Redis过期键的删除策略"的前篇或者说预备知识. 在了解过期键问题前我们首先需要对redis的数据库和数据库键空间有一定 ...

  9. Unity学习笔记(一)——基本概念之场景(Scene)

    场景,顾名思义就是我们在游戏中所看到的物品.建筑.人物.背景.声音.特效等,基本上和我们玩游戏时所看到的游戏“场景”是同一个概念. Unity 3D中,“场景”是一个视图,我们通过“场景”这个视图,来 ...

随机推荐

  1. 用Python玩数据-笔记整理-第二章-练习与测试

    课间练习:  经典问题的Python编程 按公式:C= 5/9×(F-32) ,将华氏温度转换成摄氏温度,并产生一张华氏0-300度与对应的摄氏温度之间的对照表(每隔20度输出一次) 验证命题:如果一 ...

  2. ItemsControl绑定的数据模板显示不同样式:模板选择器

    总所周知,wpf提供了数据模板,列表控件可以绑定数据实现批量显示同类型数据.不过同个数据模板显示不同的样式怎么办?这时我们可以用模板选择器. 首先我们可以将数据绑定到首先定义资源样式 <Data ...

  3. Linux 系统的基本操作及工具的使用

    基本操作命令如:useradd.userdel.passwd.su 添加用户.删除用户.修改密码.切换用户 ls.ll.cd.cp.mv.chmod ps.kil.man mkdir.touch.ta ...

  4. 使用SVG symbols建立图标系统完整指南

    从最开始的使用img图片,到后来的使用css sprite来减少服务器请求,再到流行的图形字体化图标Iconfont.现在,一种全新的图标使用方式开始流行了起来--SVG symbols图标. 工作原 ...

  5. [剑指offer] 40. 数组中只出现一次的数字

    题目描述 一个整型数组里除了两个数字之外,其他的数字都出现了偶数次.请写程序找出这两个只出现一次的数字. 思路: 解法一: 哈希表 class Solution { public: void Find ...

  6. 《VR入门系列教程》之7---DK2和Crescent Bay

    The DK2     于2014年春,Oculus发布了第二代开发版头显设备,代号为DK2.与DK1相比,Oculus Rift DK2的外观有很大改进,并且轻了许多,体积仍然比较大,可以罩住大部分 ...

  7. 第二篇:"空空如也"的博客应用

    文中涉及的示例代码,已同步更新到 HelloGitHub-Team 仓库 建立博客应用 我们已经建立了 django 博客的项目工程,并且成功地运行了它.不过到目前为止这一切都还只是 django 为 ...

  8. spring 事务隔离级别导致的bug

    事情是这样的,ios进货单有一个数量加一手,减一手的功能,每加减一次就会异步调用后台的接口,后台判断sku如果不存在就插入,存在就更新.   问题描述: 当ios发了多次请求后, 在第二次请求的时候, ...

  9. JNDI资源(一)

    JNDI:Java命名与目录接口 是一个应用程序设计的API,为开发人员提供了查找和访问各种命名和目录的通用.统一的服务. 使用JNDA的步骤: 1.配置资源. //Tomcat跟目录/conf/co ...

  10. rabbitMQ_topic(五)

    主题转发器 发送到主题转发器的消息不能有任意的 routing_key - 它必须是由点分隔的单词列表.这些单词可以是任何东西,但通常它们指定与消息相关联的一些功能.几个有效的routeKey示例:“ ...