Attention: Here be dragons
This is the latest
(unstable) version of this documentation, which may document features
not available in or compatible with released stable versions of Godot.
Checking the stable version of the documentation...
手动更改场景
有时候,你可能希望对场景的切换拥有更多的控制权。要知道,一个 Viewport'(视口)的所有子节点,都会被渲染到它生成的图像上。这个规则同样适用于那些不在 "当前" 场景里的节点。‘自动加载’(Autoloads)就属于这一类,另外,那些你在运行时通过代码实例化并添加到场景树中的场景,也属于这一类:
var simultaneous_scene = preload("res://levels/level2.tscn").instantiate()
func _add_a_scene_manually():
# This is like autoloading the scene, only
# it happens after already loading the main scene.
get_tree().root.add_child(simultaneous_scene)
public Node simultaneousScene;
public MyClass()
{
simultaneousScene = ResourceLoader.Load<PackedScene>("res://levels/level2.tscn").Instantiate();
}
public void _AddASceneManually()
{
// This is like autoloading the scene, only
// it happens after already loading the main scene.
GetTree().Root.AddChild(simultaneousScene);
}
要完成循环并将旧场景替换为新场景, 开发人员可以选择制作. 从视图 Viewport 中删除场景有很多策略. 权衡涉及平衡操作速度和内存消耗以及平衡数据访问和完整性.
删除现有场景。 SceneTree.change_scene_to_file() 和 SceneTree.change_scene_to_packed() 这两个方法会立即删除当前场景。你甚至可以直接删除主场景。假设你的根节点名字叫 "Main",那么你可以执行
get_node("/root/Main").free()来直接销毁整个场景。卸载内存.
好处: RAM不再拖累自重.
坏处: 回到那个场景现在更加昂贵, 因为它必须再次加载回内存(需要时间和内存). 如果不久就回来是不必要的.
坏处: 无法再访问该场景的数据. 如果不久就使用这些数据就不成问题了.
注意: 通过将一个或多个节点重新附加到不同的场景, 甚至直接将其重新附加到 SceneTree, 可以将数据保存在即将删除的场景中.
处理停止.
优点:没有节点,就意味着没有任何常规处理(process)、物理处理(physics processing)或输入处理(input handling)在运行。这样一来,CPU 就可以腾出全部精力,专心去处理新场景的内容了。
坏处: 这些节点的处理和输入处理不再运行. 如果不需要使用更新的数据, 则不成问题.
隐藏现有场景。 通过改变节点的可见性或者碰撞检测状态,你可以从玩家的视角将整个节点子树隐藏起来。使用 CanvasItem.hide() 可以隐藏一个场景,而使用 CanvasItem.show() 则可以再次将它显示出来。
记忆仍然存在.
优点:如果有需要,你依然可以访问到这些数据。
好处: 无需再移动任何节点来保存数据.
缺点:会有更多的数据被保留在内存中,这在网页或移动设备等对内存非常敏感的平台(或硬件)上,可能会成为一个问题。
处理继续.
优点:数据会继续接收处理更新,因此场景会保持其中所有依赖增量时间(delta time)或帧数据的更新。
Pro: 节点仍然是组的成员(因为组属于 SceneTree).
缺点:CPU 现在需要同时兼顾两个场景(的处理)。如果负载过重,可能会导致游戏掉帧。因此,你应该在开发过程中不断测试性能,以确保目标平台能够承受这种方案带来的压力。
将现有场景从树中移除。 先给现有场景的根节点指定一个变量。然后使用 Node.remove_child(Node) 将整个场景从树中分离出来。如果之后想重新挂载它,就使用 Node.add_child(Node)。
内存中的数据依然存在(其优缺点与单纯隐藏场景类似)。
处理过程会停止(其优缺点与彻底删除场景类似)。
优点:这种‘隐藏’场景的方式,在显示和隐藏之间切换起来要容易得多。你不需要费心去记录场景里各种可能发生的状态变化,只需要调用 add_child 和 remove_child 方法就行了。这和其他游戏引擎里‘禁用游戏对象(Game Objects)’的做法非常相似。
坏处:与仅从视图中隐藏它不同,如果场景中包含的数据依赖于时间增量、输入、分组或其他通过访问 SceneTree 才能得到的数据,则它将变为陈旧。
当然,也有一些情况下你可能希望多个场景同时存在。比如在运行时动态添加你自己的单例(Singleton),或者在场景切换时保留某个场景的数据(通过将该场景添加到根节点来实现)。
get_tree().root.add_child(scene)
GetTree().Root.AddChild(scene);
另一种情况是使用 SubViewportContainers 来同时显示多个场景。这对于在屏幕的不同区域渲染不同的内容来说是非常理想的选择(比如用来制作小地图,或者分屏多人游戏等)。
每种方案都有它最适用的场合,所以你必须仔细考量每种方法带来的实际影响,从而找出最适合你独特需求的那条路。