关于本手册
本手册讲解加载画面与对象池的设置方法。它们用于减少由读取和生成引起的卡顿,例如「切换场景时会瞬间卡住」「黑屏散开之后对象才零零散散地冒出来」「一次性发射大量子弹时那一下会掉帧」等问题。
两者都是以错开读取时机为目的的功能,而且加载画面的进度中也包含了对象池的预先生成。一起设置的话效果会更明显。
ACTION GAME MAKER 中读取机制的概要
与读取相关的机制有 3 种,所需的设置量各不相同。
| 机制 | 做什么 | 所需设置 |
|---|---|---|
| 场景的异步加载 | 在后台推进场景的读取,不让主处理停下来 | 无(始终有效,没有开关设置) |
| 加载画面 | 在读取过程中显示你自己做的画面 | 做一个场景,然后在设置中选中它 |
| 对象池 | 事先创建好对象的实体,消失时不丢弃而是循环利用 | 只需放置 1 个节点 |
异步加载本身不需要任何设置就在工作。 正因为读取在后台推进,加载画面才能在读取期间持续播放动画,并且要等目标场景真正进入可绘制的状态后画面才会切换。
对象池的目标是削减实例生成所花费的时间,而不是节省内存或防止碎片化。
加载画面
设置方法
- 点击地图一览的「+ 新建」,在类型中选择「加载场景」并命名。它会被创建在
res://loadingscenes中,可直接编辑。 - 打开「设置」菜单 →「基本设置」。
- 打开「加载场景」的「启用」。
- 从「场景路径」的下拉菜单中选择第 1 步创建的场景。
就这些。不需要设置撤除的时机,也不需要按转换种类分别设置。
下拉菜单中会列出项目内的加载场景(会自动拾取根节点为 LoadingScene 的场景)。改回「未指定」就恢复为未设置状态。
把「启用」关掉,就会回到不使用加载画面的传统行为。
想更改存放位置时,可以在同一个「基本设置」的素材文件夹栏中更改「加载场景」的文件夹。
编辑方法
创建好的加载场景,与地图和菜单一样在地图制作视图中编辑。把地图一览的种类下拉菜单切换到「加载场景」就会出现在一览中。
图层停靠面板的根行是「加载设置」,从它的「+」可以放置以下 6 种。由于加载期间游戏并未运行,所以只限定为显示物和动画。
精灵 / 纯色矩形 / 动画 / 标签 / 富文本 / 九宫格矩形
与地图不同,这里没有图层,各元素直接排列在根节点下。
根节点的类型
什么类型都可以。 CanvasLayer / Control / Node2D 等都能直接使用。显示在最前面的处理由引擎完成,因此也不需要调整 layer。从地图一览创建时会是 LoadingScene。
唯独以 GameScene / MenuScene / CoreScene 为根的场景不能指定。这些是由游戏侧管理的场景,无法同时存在两个。指定后日志会报错,加载画面也不会显示。
如果想接收读取的开始・完成通知,或者想放置由可视化脚本驱动的角色,就要使用专用的 LoadingScene(后述)。
只要放上去就能生效的东西
只要放进加载画面的场景中,引擎就会代为处理,不需要做任何连接设置。
| 放置的东西 | 会怎样 |
|---|---|
简易计量条 / 图像计量条 / ProgressBar |
引擎会更新其数值。它直接使用节点的最小值/最大值,所以保持默认的 0〜100 即可工作 |
AnimationPlayer 中名为 hide 的动画 |
撤除时会播放,并在播放结束之后画面才消失(名为 loading_out 也是一样的) |
AnimationPlayer 的自动播放 |
在整个读取期间持续运行。即使游戏处于暂停状态也不会停 |
进度条只要是继承自 Range 的节点都可以使用。ACTG 的简易计量条/图像计量条也包含在内,直接放上去就会显示进度(未指定显示变量的计量条,会原样显示外部写入的数值)。
不过,只有按树顺序最先找到的那 1 个会被更新。如果想把进度分发给多个进度条,请在根节点的脚本中接收 set_progress(参数为 0.0〜1.0)。一旦实现了 set_progress,计量条的自动更新就会停止(为了避免重复操作)。
如果没有放置 hide 动画,引擎会用自带的黑幕自动淡出。
请关闭 hide 的循环。 如果处于循环状态,动画永远不会结束,完成通知也就不会到来。引擎检测到循环设置时会在日志中提示,并改用黑幕来撤除。
只有想在加载画面中放置由可视化脚本驱动的角色时,才需要把根节点的类型设为 LoadingScene(参见下一节)。
把根节点设为 LoadingScene 后会增加什么
加载画面的根节点可以指定为 LoadingScene。指定是可选的,它本身没有设置项。指定之后可以做到以下 2 件事。
| 能做到的事 | 内容 |
|---|---|
| 接收读取的通知 | 可以通过 signal 接收进度变化、读取开始、读取完成 |
| 让可视化脚本的角色动起来 | 作为子节点放置的、带有可视化脚本的对象,会像在普通场景中一样运作 |
第 2 项是由 LoadingScene 遍历子孙节点并进行注册实现的,因此根节点仍是 CanvasLayer 之类时不会生效。 想放置在加载期间四处走动的角色时,请设为 LoadingScene。
反过来说,上一节列举的那些(进度条的自动更新、hide 的撤除演出、自动播放、黑幕、消失时机、set_progress)与根节点的类型无关,都会生效。如果只是「想自己加工进度的显示方式」,实现 set_progress 就够了,不需要 LoadingScene。
消失的时机
以下条件全部满足后才会消失。没有设置项。
- 目标场景的读取已完成
- 目标场景已被放入画面并开始运行
- 放入后已绘制了 2 帧
- 对象池的预先生成已完成
- 已经过最短显示时间(0.3 秒)
第 3 项是针对「黑屏散开之后对象才冒出来」这一现象的对策。在加载画面的背后把场景预热好再消失,所以画面一消失就能立刻游玩。
目标场景在确定撤除之前不会开始运行。 这是为了避免在预先生成的等待时间里,游戏在看不见的地方擅自推进。它开始运行的时点是撤除演出(黑幕闭合,或 hide)开始的那一刻,因此首帧是在画面被遮住的期间跑的,等到能看见时显示内容已经确定了。
在游戏场景中设置的 BGM 也会等到撤除时。 不会出现画面还在、BGM 却先响起来的情况。
第 5 项是为了防止读取瞬间完成时加载画面一闪而过。这 0.3 秒是从黑幕完全打开后开始计时的,所以不会出现「刚出现就消失」的情况。
出现与消失的淡入淡出(黑幕)
如果加载画面突然出现又突然消失会显得很闪,因此引擎会开合黑幕,让切换的瞬间画面必定为纯色。
| 动作 | 时长 | 颜色 |
|---|---|---|
| 加载画面出现 | 0.2 秒 | 与转换前演出的颜色一致 |
| 加载画面消失 | 0.2 秒 | 与转换后演出的颜色一致 |
| 目标场景出现 | 转换后演出的时长(最短 0.2 秒) | 与转换后演出的颜色一致 |
最后一项「目标场景出现」会直接使用在场景转换编辑器中设置的转换后演出(亮转)的时长。即使把转换后演出设为「无」,为了不变成硬切,也至少会淡入 0.2 秒。
如果把转换后演出设为滑动类(SLIDE_UP 等),则会变成黑幕朝指定方向退去的擦除效果。原本那种「新场景的画面滑动进来」的滑动无法再现(因为场景中带有摄像机,即使移动场景,摄像机也会一起移动,画面上看不出变化)。方向会按设置得到尊重。
如果你自行放置了 hide 动画,消失之后的演出就交给作者自己负责(不使用黑幕),这是为了避免重复淡出。
关于进度条的运动
进度条表示的是距离完成的预估,并不是严格的百分比。
| 区间 | 内容 |
|---|---|
| 0〜60% | 资源读取中 |
| 60〜85% | 从读取完成到场景放置 |
| 100% | 撤除已确定 |
它会朝着各区间的上限平滑地增长,阶段推进时会加速。不会倒退,也不会在 100% 停住干等。
存在对象池的预先生成时,从那里到 99% 由预先生成的进度填充。 这一段是实际创建数量的比例,因此不是随时间平滑增长,而是直接反映生成的推进情况。预先生成开始的位置最低为 30%,不会比这更早开始(为了避免读取期间进度条不动看起来像卡住了)。
无法做到严格数值是引擎侧的限制。既取不到读取的中途进度,而且目标场景往往已作为邻近场景被预读,读取本身一瞬就结束了。
启动时的流程
启动后,首先会显示 1 秒 ACTION GAME MAKER 的启动画面,之后进入加载画面。
显示启动画面期间读取也在后台推进,所以不会因为显示启动画面而拖慢启动。 如果第一个场景很轻,在启动画面显示期间读取就完成了,那就不显示加载画面直接进入游戏(防闪烁)。
启动画面会直接反映「应用程序」→「Boot Splash」的设置(图像/背景色/缩放/过滤)。未指定时则使用 ACTION GAME MAKER 内置的启动画面。
不过,如果启动时还有对象池的预先生成未完成,则会优先显示加载画面而非防闪烁(因为需要有一个等待预先生成完成的对象)。
连接滑动转换时不显示
在场景转换编辑器中指定了连接滑动(SLIDE_UP_CONNECT 等)的转换,不会显示加载画面。连接滑动是为了「无缝移动到隔壁房间」的演出,若插入加载画面,连接滑动和玩家的移动演出就全被遮住了。
只要转换前演出・转换后演出中任意一方指定了连接滑动,就不会显示。
对象池
什么情况下有效
新建对象的处理是要花时间的。一个一个创建时感觉不出来,但在同时发射 20 发子弹这类批量创建的场合,那一瞬间就会掉帧。
对象池的机制是:在读取期间先把这些实体创建好,消失时也不丢弃而是让它休眠,下次生成时循环利用。由于没有了生成的等待时间,批量出现也不会卡顿。
设置方法
- 在地图制作视图的图层停靠面板中点击「地图设置」的「+」,选择「游戏对象池」。
- 向「池的一览」中添加元素。
- 在「对象路径」中指定想要纳入池管理的对象场景。
- 根据需要调整「预先生成的数量」等项目。
就这些。设置只需在这一个节点上完成。 对象侧、子弹数据侧、可视化脚本侧都没有需要设置的项目。登记到一览中的对象,无论从哪条生成路径都会经由对象池。
添加位置是地图设置的正下方(因为预先生成的计划是在场景读取时点进行的,所以不放在图层里面)。
游戏对象池的设置项
「池的一览」的每 1 个元素都有以下项目。
| 项目 | 含义 | 默认值 |
|---|---|---|
| 池的显示名 | 用于在一览中区分的名称。不用于引用 | 空 |
| 对象路径 | 作为生成来源的对象场景。该场景即为池的标识符 | 空 |
| 仅进行预读取 | 只提前完成读取,不持有池 | 关 |
| 预先生成的数量 | 事先创建好的数量 | 8 |
| 不足时自动追加生成 | 库存耗尽时是否追加创建 | 开 |
把「不足时自动追加生成」关闭后,库存耗尽的部分会照常新建,而且这些个体不会归还到池中。这不会报错,只是变回使用池之前的行为而已。
预先生成是跨帧一点点进行的(对每帧所占的时间设有上限),不会因为一口气创建完而卡住帧。进度会传递给加载画面,在预先生成结束前加载画面不会消失。
节点的放置位置
休眠中的库存由这个节点持有,因此节点一旦被销毁,库存也会随之丢失。放置位置不同,行为也不同。
| 放置位置 | 行为 |
|---|---|
| 不会被场景转换销毁的位置 | 库存会跨场景保留。转换时出借中的部分也会被归还,所以转换后可以直接继续使用 |
| 游戏场景之中 | 转换时节点连同库存一起被销毁。目标场景中的节点会重新进行预先生成 |
想让库存跨场景循环利用时,请让它常驻。 简便的做法是使用自动加载:创建一个只放了 1 个游戏对象池的场景,在项目设置的「全局」→「自动加载」中注册。注册的节点会在游戏开始时创建一次,且不会被场景转换销毁。
也可以从别的地图复制过来。 粘贴位置会在地图设置的正下方。
不要把它放在运行时生成的游戏对象的子级下。 因为预先生成的计划必须在场景读取时点就已完成,请作为专用节点直接放置到场景中。
仅进行预读取
打开「仅进行预读取」后,只会提前完成读取,不持有库存。
- 在生成来源场景读取完成后只创建 1 个并立即丢弃。场景及其素材会保持已读取状态,因此实际生成时的卡顿会减轻
- 该场景的对象会照常生成(不从池中分发,消失时也就直接消失)
- 不限制根节点的类型。 非游戏对象的场景、UI 的场景也能登记
- 由于不使用「预先生成的数量」「不足时自动追加生成」,这两项会变为不可编辑
它适合那些「不想循环利用,但想消除出现瞬间卡顿」的对象。
特效和粒子适用这个设置。 EffectObject / ParticleObject 不在循环利用的对象范围内(后述),但「仅进行预读取」不限制类型,所以可以登记。由于能提前完成首次显示时的读取,首次出现时的卡顿会减轻。 建议把打算使用的特效统一登记进来。
适用的对象
能够循环利用的只有游戏对象和轻量对象。 因为归还处理是从这两种类型的消失流程中执行的。
由物理运算驱动的对象(PhysicsGameObject)、特效(EffectObject)、粒子(ParticleObject)以及其他节点都不在范围内,因为它们没有在消失时归还到池中的路径。「对象路径」虽然可以指定任意 .tscn,但根节点不属于适用种类的场景不会创建池(运行时会在日志中提示)。
「仅进行预读取」的情况不受此限制。 特效和粒子请用它来登记,提前完成首次读取即可。
生效的生成路径
只要登记到一览中,以下全部都会自动经由池。 没有按路径分别设置的项目。
| 路径 | 示例 |
|---|---|
| 子弹的发射 | 发射子弹的动作 |
| 对象的生成 | 「生成对象」动作 |
| 连接对象 | 作为子级生成的对象 |
| 对象的变更 | 「变更对象」动作 |
| 复活 | 已消失对象的复活、从摄像机外的返回、场景切换后的复原 |
归还的时机
对象消失时,会代替删除而返回到池中。 消失动作、对象的变更、场景转换,任意一种情况下都会归还。
返回的个体会在归还的时点被还原为场景中保存的状态。运行时变更过的属性会回到保存值,运行时追加的节点会被删除,可视化脚本会回到从初始动作重新运行的状态。由于是在归还时而非取出时整理,所以批量取出的场合不会增加开销。
场景中一开始就配置好的对象不在归还范围内(因为它们不是从池中分发的,会直接被删除)。
编辑器的警告与日志
设置上的遗漏可以通过场景树上的警告察觉。
- 「对象路径」未设置
- 「对象路径」重复(同一场景登记多次时,靠后的不会被使用)
运行时会在日志中提示预先生成的失败(路径未设置/路径无法解析/重复/读取失败/根节点不属于适用种类)。
「没有池」这件事本身不会输出到日志。 未登记到一览中的对象只是照常生成,并非设置失误。
仅仅是库存耗尽的情况(「不足时自动追加生成」为关闭),也只是转为普通生成,不会输出任何内容。
实用示例
同时发射 20 发子弹时那一下会卡顿
- 放置游戏对象池,在「对象路径」中指定子弹的场景。
- 把「预先生成的数量」设为比同时出现在画面上的最大数量略多一些(20 发的话设为 24 等)。
- 子弹数据侧什么都不用改,直接运行。
想知道库存是否足够,可以把「不足时自动追加生成」关掉试试。如果不够,中途的子弹就会卡顿,据此增加「预先生成的数量」即可。确认完成后再把它打开,这样在意料之外的场合也不至于打不出子弹。
在加载画面上显示「Now Loading」
- 用地图一览的「+ 新建」→ 类型「加载场景」创建。
此时它已经带有黑色背景、计量条、「NOW LOADING …」的标签,以及让点号增减的动画。之后只要在「设置」→「基本设置」中启用,并从下拉菜单中选择这个场景就能运作。
想改变外观时,可以从图层停靠面板的「+」添加元素,调整标签的文字和计量条的颜色。若要自己制作消失时的演出,请在 AnimationPlayer 中添加名为 hide 的动画(请关闭循环)。
只把 Boss 的场景提前读取好
适用于不想循环利用、但想消除出现时卡顿的情况。
- 向游戏对象池的「池的一览」中添加元素。
- 在「对象路径」中指定 Boss 的场景。
- 打开「仅进行预读取」。
Boss 会照常生成,但场景和素材已读取完毕,所以出现时会更轻快。
让库存跨关卡循环利用
- 创建一个只放了 1 个游戏对象池的场景,保存到
res://。 - 在项目设置的「全局」→「自动加载」中注册该场景。
- 池的设置就排列在那个场景的节点上。
预先生成只会在游戏开始时跑一次,之后的场景转换不会重新创建。与按关卡分别放置的区别仅在于库存是否保留,设置项是相同的。
注意事项
- 异步加载没有开关设置。 场景的读取始终在后台推进。需要设置的只有加载画面和对象池。
- 加载场景的设置位于「设置」→「基本设置」。 如果要从项目设置直接修改,它在「高级设置」一侧(
application/loading_scene/*),请打开开关或在搜索栏中输入loading。 - 加载画面的根节点不能指定
GameScene/MenuScene/CoreScene。 指定后日志会报错且不会显示。 - 若要在加载画面中放置可视化脚本的角色,请把根节点设为
LoadingScene。 其他类型不会被注册,放上去也不会动。既不报错也不警告,是很难察觉的地方。 - 请关闭
hide动画的循环。 处于循环状态时完成通知不会到来。检测到时会在日志中提示,并改用黑幕撤除。 - 若迟迟无法满足撤除条件,会强制撤除。 到底在等什么会输出到日志。预先生成的数量极多时也会在此被中断,但剩余部分会在取出时创建,不影响运作。
- 只有 1 个进度条会被自动更新。 想显示多个时,请在根节点的脚本中接收
set_progress。一旦实现,自动更新就会停止。 - 连接滑动转换时不显示加载画面。 只要转换前・转换后中任意一方指定了连接滑动就不显示(这是规格)。
- 游戏对象池的节点被销毁时,库存也会丢失。 想跨场景循环利用时,请用自动加载等方式让它常驻。
- 请勿把同一场景登记到多个池中。 由于生成来源场景就是池的标识符,靠后的那个不会被使用。可以通过场景树的警告察觉。
- 池的适用对象只有游戏对象和轻量对象。 在「对象路径」中指定其他类型也不会创建池(使用「仅进行预读取」时则不限类型)。
- 库存耗尽时的行为是静默的。 「不足时自动追加生成」为关闭时只是转为普通生成,也不会输出日志。想确认库存是否足够时,请故意关掉它试试。
- 循环利用只是「不重新创建消失后返回的个体」而已。 对象的状态会在归还时还原为场景的保存值,不会从上次的中途继续。反过来说,也无法实现「让运行时添加的子节点下次也保留」这类用法。
- 请勿把游戏对象池放在运行时生成的对象的子级下。 预先生成的计划必须在场景读取时就已完成。
- 对象池的目标是削减生成时间。内存使用量会按预先生成的数量相应增加。
- 预先生成的数量会直接变成加载时间。 每个个体的生成时间取决于对象的规模,但带可视化脚本的子弹要花上数毫秒。设成数百个的话就会拉长到数秒级别,请根据实际同时出现的数量来决定。