自动加载与内部节点

其他引擎可能会鼓励使用创建“管理器”类,将大量功能组织成一个全局可访问的实体。Godot通过支持各种方法来减少这些物体的尺寸而繁荣起来。相反,它尽可能将内容转移到各个节点。

例如,如果一个开发者正在构建一个平台,并且他们想要收集能够产生声音效果的硬币,那该怎么办?好吧,有一个节点可以实现这一点: AudioStreamPlayer . 但是他们在测试过程中注意到,如果他们在音频流播放器已经在播放声音时“播放”,那么新的声音会中断第一个声音,在它可以播放之前将其终止。

用户倾向于认为最好的解决方案是让SoundManager自动加载节点使整个系统更智能。它生成了一个音频流播放器池,当声音效果的每个新请求出现时,这些播放器循环通过。然后,他们将此SoundManager设置为自动加载,这样他们就可以从任何位置使用 SFX.play("coin_pickup.ogg") . 他们不知道,他们在代码中引入了许多复杂的内容。

  • 全局状态 :一个对象现在负责所有对象的数据。如果SFX有错误或没有可用的音频流播放器,一切都将中断。

  • 全局访问 :现在任何对象都可以调用 SFX.play(sound_path) 从任何地方,都不再有一个简单的方法来跟踪SFX错误的开始。

  • 全球资源分配 :如果所有对象的数据和处理从一开始就集中在一起,那么必须有一个对象…

    1. 可能导致错误行为的资源分配风险。

      • 例如:对象池中的音频流播放器太少。声音无法播放或中断另一个声音。

    2. 过度分配资源并使用比需要更多的内存/处理。

      • 例:有任意数量的音频流播放器,其中许多都是闲置的,什么都不做。

    3. 让每个需要音频流播放器的对象准确地记录它需要多少音频流播放器,以及它的声音。但是,这违背了使用第三方的目的;它现在耦合到每个对象,就像子节点一样。有人在等式中加入了不必要的中间人。

与每个场景相比,每个场景在自身中保留尽可能多的audiostreamplayer节点,所有这些问题都会消失。

  • 每个场景管理自己的状态信息。如果数据有问题,它只会在那个场景中引起问题。

  • 每个场景只访问自己的节点。现在,如果存在一个bug,那么跟踪哪个节点(可能是场景的根节点)以及代码中进行有问题调用的位置(定位代码引用给定节点的位置)将变得容易得多。

  • 每个场景都清楚地知道执行任务需要多少资源。不因缺乏信息而浪费内存或处理。

自动加载的典型理由包括:“我有涉及多个场景中许多节点的公共XS,我希望每个场景都有X。”

如果x是一个函数,那么解决方案是创建一个新类型的 Node 它处理为单个场景或节点子树提供该功能。

如果x是数据,那么解决方案是1)创建新类型的 Resource 共享数据,或2)将数据存储在每个节点都可以访问的对象中(场景中的节点可以使用 get_owner() 例如获取场景根)。

所以什么时候 应该 一个使用自动加载?

  • 静态数据 :如果您需要静态数据,即应该与类关联的数据(因此只有一个数据副本),那么自动加载就是实现这一点的好机会。静态数据不存在于Godot的脚本API中,所以自动加载单例是下一个最好的方法。如果创建一个类作为自动加载,并且从未在场景中创建该类的另一个副本,那么它将代替正式的singleton API工作。

  • 方便 :自动加载的节点在gdscript中为其名称生成了一个全局变量。这对于定义应该始终存在但需要对象实例信息的对象非常方便。另一种方法是创建一个名称空间脚本:一个脚本的目的只是加载和创建常量以访问其他脚本或packedScene资源,从而产生类似 MyAutoload.MyNode.new() .

    • 注意,在Godot3.1中引入脚本类会质疑这个原因的有效性。使用它们,可以使用gdscript中的显式名称访问脚本。不需要使用自动加载来获取命名空间脚本,例如 MyScriptClass.MyPreloadedScript.new() .

如果singleton正在管理自己的信息,而不是侵入其他对象的数据,那么它是创建一个处理广泛范围任务的“系统”类的好方法。例如,目标系统、任务系统或对话系统将是单例实现的重要用例。