自动加载与常规节点

Godot 提供了一个在项目根节点自动加载节点的功能,允许你在全局范围内访问它们,从而完成单例作用 单例(自动加载) 。当你在代码中使用 SceneTree.change_scene_to_file 更改场景时,这些自动加载的节点不会被释放。

在本指南中, 你将学习到何时使用自动加载功能, 以及避免使用该功能的方法.

切割音频问题

其他引擎可能鼓励使用创建管理类, 单例将很多功能组织到一个全局可访问的对象中. 由于节点树和信号,Godot提供了许多避免全局状态的方法.

例如, 假设我们正在构建一个平台游戏, 并希望收集能够播放声音效果的硬币, 那么就有一个节点 AudioStreamPlayer. 如果在 AudioStreamPlayer 已经在播放声音时调用它, 新的声音就会打断第一个声音.

一种解决方案是写一个全局的、自动加载的音效管理器类。它会生成一个 AudioStreamPlayer 的节点池,每当一个新的音效请求出现时,它就会在这个节点池中找到可用的节点来播放。我们不妨就把该类命名为 Sound ,你可以通过 Sound.play("coin_pickup.ogg") 从你项目中的任何位置使用它。这在短期内解决了问题但是却造成了更多的麻烦:

  1. 全局状态 : 一个对象现在负责所有对象的数据. 如果音效有错误, 或没有一个可用的 AudioStreamPlayer , 一切都会崩溃.

  2. 全局访问 : 意味着任何对象都可以从任何地方调用 Sound.play(sound_path) , 便不容易找到错误的来源了.

  3. 全局资源分配 : 由于从一开始就存储了一个 AudioStreamPlayer 节点池, 如果数量太少会遇到bug, 而数量太多则会占用更多的内存.

备注

全局访问的问题在于,任何地方的代码都可能将错误的数据传递给我们例子中的 Sound 自动加载。因此,为了修复这个 bug,你需要检索的区域涵盖了整个项目。

当你将代码保存在场景中时, 音频可能仅涉及一个或两个脚本.

与之形成对比的是, 每个场景在其内部, 保留尽可能多的 AudioStreamPlayer 节点, 所有这些问题都会消失:

  1. 每个场景管理自己的状态信息. 如果数据有问题, 则只会在该场景中引起问题.

  2. 每个场景只访问自己的节点. 那么如果有一个bug, 很容易找到哪个节点有问题.

  3. 每个场景只分配所需数量的资源.

管理共享功能或数据

使用自动加载的另一个原因可能是你希望在许多场景中重复使用相同的方法或数据.

对于函数,可以使用 GDScript 中的 class_name 关键字创建一种新的 Node 类型,为单个场景提供该功能。

当涉及到数据时, 你可以:

  1. 创建一个新类型的 Resource 来共享数据.

  2. 将数据存储在每个节点可以访问的对象中, 例如使用 owner 属性来访问场景的根节点.

何时应使用自动加载

GDScript 支持使用 static func 创建 static (静态) 函数,与 class_name 结合使用时还可以创建辅助函数库,无需创建实例来调用这些函数。 静态函数也有一些限制:不能引用成员变量、非静态(non-static)函数或 self

从 Godot 4.1 开始,GDScript 还支持使用 static varstatic (静态)变量,意味着你现在可以在类的实例之间共享变量,而无需创建单独的自动加载节点或脚本。

尽管如此,对于那些涵盖范围广泛的系统来说,使用自动加载的节点仍然可以简化你的代码。如果自动加载的节点管理自己的信息并且不侵入其他对象的数据,那么这就是一个创建处理广泛任务的系统(例如,任务或对话系统)的好方法。

备注

自动加载 完全是一个单例。没有什么可以阻止你实例化自动加载的节点的副本。它只是一个使节点作为场景树的根的子节点自动加载的工具,而与游戏的节点结构或运行哪个场景(比如通过按 F6 键运行当前场景)无关。

因此,你可以像这样,通过调用 get_node("/root/Sound") 来获取名为 Sound 的自动加载节点。