何时使用场景VS脚本

我们已经介绍了场景和脚本的不同之处.脚本使用命令性代码定义引擎类扩展,而场景使用声明性代码.

结果,每个系统的功能都不同.场景可以定义扩展类的初始化方式,但不能定义其实际行为.场景通常与脚本结合使用,以便场景充当脚本声明性代码的扩展.

匿名类型

单独使用脚本 可以 完全定义场景的内容.从本质上讲,Godot编辑器所做的,仅在其对象的C++构造函数中.

但是,选择哪个来使用,可能是一个两难问题.创建脚本实例与创建引擎类相同,而处理场景需要更改API:

GDScript

C#

  1. const MyNode = preload("my_node.gd")
  2. const MyScene = preload("my_scene.tscn")
  3. var node = Node.new()
  4. var my_node = MyNode.new() # Same method call
  5. var my_scene = MyScene.instance() # Different method call
  6. var my_inherited_scene = MyScene.instance(PackedScene.GEN_EDIT_STATE_MAIN) # Create scene inheriting from MyScene
  1. using System;
  2. using Godot;
  3. public class Game : Node
  4. {
  5. public readonly Script MyNodeScr = (Script)ResourceLoader.Load("MyNode.cs");
  6. public readonly PackedScene MySceneScn = (PackedScene)ResourceLoader.Load("MyScene.tscn");
  7. public Node ANode;
  8. public Node MyNode;
  9. public Node MyScene;
  10. public Node MyInheritedScene;
  11. public Game()
  12. {
  13. ANode = new Node();
  14. MyNode = new MyNode(); // Same syntax
  15. MyScene = MySceneScn.Instance(); // Different. Instantiated from a PackedScene
  16. MyInheritedScene = MySceneScn.Instance(PackedScene.GenEditState.Main); // Create scene inheriting from MyScene
  17. }
  18. }

此外,由于引擎和脚本代码之间的速度差异,脚本的运行速度将比场景慢一些.节点越大和越复杂,将它构建为场景的理由就越多.

命名的类型

在某些情况下,用户可以在编辑器内,将脚本注册为新类型.在节点或资源创建对话框中,将其显示为新类型,并带有可选图标.在这些情况下,用户使用脚本的能力更加流畅.而不必…

  1. 了解他们想要使用的脚本的基本类型.

  2. 创建一个该基本类型的实例.

  3. 将脚本添加到节点.

    1. (拖放方法)

      1. 在文件系统停靠面板中找到脚本.

      2. 将脚本拖放到场景停靠面板中的节点上.

    2. (属性方法)

      1. 向下滚动到属性检查器的底部,找到 脚本 属性并选择它.

      2. 从下拉列表中选择 加载.

      3. 从文件对话框中选择脚本.

使用一个注册的脚本,脚本类型将像系统中的其他节点和资源一样成为创建选项.不需要做任何上述工作.创建对话框甚至还有一个搜索栏,用于按名称查找类型.

有两种用于注册类型的系统…

  • 自定义类型

    • 仅限编辑器.类型名称在运行时中不可访问.

    • 不支持继承的自定义类型.

    • 一个初始化工具.使用脚本创建节点.仅此而已.

    • 编辑器没有对该脚本的类型感知,或其与其他引擎类型或脚本的关系.

    • 允许用户定义一个图标.

    • 适用于所有脚本语言,因为它抽象处理脚本资源.

    • 设置使用 EditorPlugin.add_custom_type.

  • 脚本类

    • 编辑器和运行时均可访问.

    • 显示全部继承关系.

    • 使用脚本创建节点,但也可以从编辑器更改或扩展类型.

    • 编辑器知道脚本、脚本类和引擎c++类之间的继承关系.

    • 允许用户定义一个图标.

    • 引擎开发人员必须手动添加对语言的支持(名称公开和运行时可访问性两者).

    • 仅适用于Godot 3.1+版本.

    • 编辑器扫描项目文件夹,并为所有脚本语言注册任何公开的名称.为公开此信息,每种脚本语言都必须实现自己的支持.

这两种方法都向创建对话框添加名称,特别是脚本类,还允许用户在不加载脚本资源的情况下访问类别名称.在任何地方都可以创建实例,和访问常量或静态方法.

有了这些功能,由于它赋予用户易用性,人们可能希望它们的类型是没有场景的脚本.那些正在开发的插件或创建供设计人员使用的内部工具,将以这种方式使事情变得更轻松.

不足之处在于,这也意味着很大程度上必须使用命令式编程.

Script[脚本]与PackedScene[场景包]的性能对比

在选择场景和脚本时,最后一个需要考虑的方面是执行速度.

随着对象内容的增加,脚本创建和初始化所需的内容也会大大增加.创建节点层次结构就说明了这一点.每个Node的逻辑可能有几百行代码.

下面的代码示例创建一个新的 Node,更改名称,分配脚本,将其未来的父级设置为其所有者,以便保存到磁盘中,最后将其添加为”主”节点的子级:

GDScript

C#

  1. # Main.gd
  2. extends Node
  3. func _init():
  4. var child = Node.new()
  5. child.name = "Child"
  6. child.script = preload("Child.gd")
  7. child.owner = self
  8. add_child(child)
  1. using System;
  2. using Godot;
  3. public class Main : Resource
  4. {
  5. public Node Child { get; set; }
  6. public Main()
  7. {
  8. Child = new Node();
  9. Child.Name = "Child";
  10. Child.Script = ResourceLoader.Load<Script>("child.gd");
  11. Child.Owner = this;
  12. AddChild(Child);
  13. }
  14. }

这样的脚本代码比引擎端的C++代码要慢很多.每条指令都要调用脚本API,导致后端要进行多次 “查找”,以找到要执行的逻辑.

场景有助于避免这个性能问题. PackedScene [场景包],场景继承的基础类型,定义了使用序列化数据创建对象的资源.引擎可以在后端批量处理场景,并提供比脚本好得多的性能.

总结

最后,最好的方法是考虑以下几点:

  • 如果希望创建一个基本工具,它将在几个不同的项目中重用,以及可能提供给不同技能水平的人使用.(包括那些不认为自己是个程序员的用户),它很可能是一个脚本,有一个自定义名称/图标.

  • 如果有人想创造一个特定于他们的游戏的概念,那么它应该是一个场景.场景比脚本更容易跟踪/编辑,并提供更多的安全性.

  • 如果有人想命名一个场景,他们仍然可以在3.1版本中,通过声明一个脚本类并给它一个场景作为常量来实现这一点.实际上,该脚本变成了一个命名空间:

    GDScript

    1. # game.gd
    2. extends Reference
    3. class_name Game # extends Reference, so it won't show up in the node creation dialog
    4. const MyScene = preload("my_scene.tscn")
    5. # main.gd
    6. extends Node
    7. func _ready():
    8. add_child(Game.MyScene.instance())