手动更改场景

有时候, 有必要更好地控制如何交换场景. 如上所述, 一个 Viewport 的子节点将呈现给它生成的图像. 即使对于 "当前" 场景之外的节点, 这也适用. Autoloads属于这一类, 但是一个实例在运行时添加到树中的场景也是如此:

var simultaneous_scene = preload("res://levels/level2.tscn").instance()

func _add_a_scene_manually():
    # This is like autoloading the scene, only
    # it happens after already loading the main scene.
    get_tree().get_root().add_child(simultaneous_scene)
public PackedScene simultaneousScene;

public MyClass()
{
    simultaneousScene = (PackedScene)ResourceLoader.Load("res://levels/level2.tscn").instance();
}

public void _AddASceneManually()
{
    // This is like autoloading the scene, only
    // it happens after already loading the main scene.
    GetTree().GetRoot().AddChild(simultaneousScene);
}

要完成循环并将旧场景替换为旧场景, 开发人员可以选择制作. 从视图中删除场景有很多策略 Viewport. 权衡涉及平衡操作速度和内存消耗以及平衡数据访问和完整性.

  1. 我们可以删除现有场景. SceneTree.change_scene()SceneTree.change_scene_to() 会立即删除当前场景. 不过开发者也可以删除主场景. 假设根节点的名字是 "Main", 可以执行 get_node("/root/Main").free() 来删除整个场景.

    • 卸载内存.

      • 好处: RAM不再拖累自重.

      • 坏处: 回到那个场景现在更加昂贵, 因为它必须再次加载回内存(需要时间和内存). 如果不久就回来是不必要的.

      • 坏处: 无法再访问该场景的数据. 如果不久就使用这些数据就不成问题了.

      • 注意: 通过将一个或多个节点重新附加到不同的场景, 甚至直接将其重新附加到 SceneTree, 可以将数据保存在即将删除的场景中.

    • 处理停止.

      • 好处: 没有节点意味着没有进程, 物理过程或输入处理. CPU可用于处理新场景的内容.

      • 坏处: 这些节点的处理和输入处理不再运行. 如果不需要使用更新的数据, 则不成问题.

  2. 我们可以隐藏现有场景. 通过更改节点的可见性或碰撞检测, 我们可以从游戏角色的角度隐藏整个节点子树.

    • 记忆仍然存在.

      • 好处: 如果需要, 仍然可以访问数据.

      • 好处: 无需再移动任何节点来保存数据.

      • 坏处: 更多数据被保存在内存中, 这将成为对内存敏感平台(如Web或移动设备)的问题.

    • 处理继续.

      • 好处: 数据继续接收处理更新, 因此场景将不断更新其中依赖于增量时间或帧数据的任何数据.

      • Pro: 节点仍然是组的成员(因为组属于 SceneTree).

      • 坏处: 现在CPU的注意力分散在两个场景之间. 负载过大可能导致帧速率降低. 应该确保测试性能, 以确保目标平台能够支持它们提供的负载.

  3. 我们可以从树上删除现有的场景. 给现有场景的根节点分配一个变量. 然后使用 Node.remove_child(Node) 来将整个场景从树上分离.

    • 记忆仍然存在(与从视图中隐藏它相似的优点/缺点).

    • 处理停止(类似于完全删除它的优点/缺点).

    • 亲: 这种 "隐藏" 的变化更容易显示/隐藏. 人们必须只调用一个方法add / remove_child方法, 而不是潜在地跟踪场景的多个变化. 它类似于在其他引擎中禁用游戏对象.

    • 坏处: 与仅从视图中隐藏它不同, 如果场景中包含的数据依赖于delta时间, 输入, 组或其他派生自 SceneTree access的数据, 则它将变为陈旧.

在有些情况下, 人们可能希望有许多场景同时存在. 也许人们在运行时添加他们自己的单例, 或者在场景变化之间保留一个场景的数据(将场景添加到根节点).

get_tree().get_root().add_child(scene)
GetTree().GetRoot().AddChild(scene);

也许他们希望使用 ViewportContainers 来同时显示多个场景。这意图是在屏幕的不同部分渲染不同内容的情况下是最理想的。迷你地图和多人分屏游戏就是很好的例子。

每个选项都有最合适的情况, 因此必须检查每个选项的效果并确定最适合其独特情况的路径.