节点与场景实例

本指南将介绍如何获取节点、创建节点、将节点添加为子项、使用代码实例化场景。

获取节点

你可以通过调用 Node.get_node() 方法来获取节点的引用。子节点必须在场景树中才能成功。在父节点的 _ready() 函数中获取就可以保证这一点。

打个比方,如果你的场景树是这样的,并且你想要在脚本中获取 Sprite 和 Camera2D 节点的引用。

../../_images/nodes_and_scene_instances_player_scene_example.png

那么,你可以使用如下代码。

GDScriptC#

  1. var sprite
  2. var camera2d
  3. func _ready():
  4. sprite = get_node("Sprite")
  5. camera2d = get_node("Camera2D")
  1. private Sprite _sprite;
  2. private Camera2D _camera2d;
  3. public override void _Ready()
  4. {
  5. base._Ready();
  6. _sprite = GetNode<Sprite>("Sprite");
  7. _camera2d = GetNode<Camera2D>("Camera2D");
  8. }

请注意,你是通过名称来获取节点的,和类型无关。上面的“Sprite”和“Camera2D”都是这些节点在场景中的名字。

../../_images/nodes_and_scene_instances_sprite_node.png

如果你在“场景”面板中将 Sprite 节点重命名为“Skin”,那就必须在脚本里把获得节点的那一行语句写成 get_node("Skin")

../../_images/nodes_and_scene_instances_sprite_node_renamed.png

节点路径

获取节点的引用时,并不仅限于直接子节点。get_node() 函数支持使用路径,有点类似文件浏览器里的路径。可以用斜杠分隔节点。

在下面这个实例场景中,脚本是附加在 UserInterface 节点上的。

../../_images/nodes_and_scene_instances_ui_scene_example.png

要获取 Tween 节点,你可以使用如下代码。

GDScriptC#

  1. var tween
  2. func _ready():
  3. tween = get_node("ShieldBar/Tween")
  1. private Tween _tween;
  2. public override void _Ready()
  3. {
  4. base._Ready();
  5. _tween = GetNode<Tween>("ShieldBar/Tween");
  6. }

备注

和文件路径一样,你可以使用“..”来获取父节点。最佳实践是不要这么做,避免破坏封装。你还可以让路径以斜杠开头,这样的路径是绝对路径,最上层的节点就是“/root”,即程序的预定义根视口。

语法糖

GDScript 中有两个速写法可以用来缩短代码。首先是在成员变量之前写 onready 关键字,这样这个变量就会刚好在 _ready() 回调之前初始化。

  1. onready var sprite = get_node("Sprite")

还有一个 get_node() 的速记法:美元符号“$”。可以把它放在想要获取的名称或者节点路径之前。

  1. onready var sprite = $Sprite
  2. onready var tween = $ShieldBar/Tween

创建节点

要通过代码创建节点,请像其他任何基于类的数据类型一样,调用其 new() 方法。

你可以将新创建的节点的引用保存在一个变量中,然后调用 add_child() 将其添加为脚本所在节点的子项。

GDScriptC#

  1. var sprite
  2. func _ready():
  3. var sprite = Sprite.new() # Create a new Sprite.
  4. add_child(sprite) # Add it as a child of this node.
  1. private Sprite _sprite;
  2. public override void _Ready()
  3. {
  4. base._Ready();
  5. _sprite = new Sprite(); // Create a new Sprite.
  6. AddChild(_sprite); // Add it as a child of this node.
  7. }

要删除节点、将其从内容中释放,你可以调用其 queue_free() 方法。这样该节点的删除任务就会被添加到队列中,在当前帧完成处理后就会执行。删除时,引擎会把该节点从场景中删除,然后释放对象内存中的对象。

GDScriptC#

  1. sprite.queue_free()
  1. _sprite.QueueFree();

在调用 sprite.queue_free() 之前,远程场景树是类似这样的。

../../_images/nodes_and_scene_instances_remote_tree_with_sprite.png

在引擎释放节点后,远程场景树就不会再现实这个精灵了。

../../_images/nodes_and_scene_instances_remote_tree_no_sprite.png

你也可以调用 free() 来立即删除该节点。调用时需要小心,因为所有对它的引用都会立即变成 null。除非你知道自己在干什么,否则我们建议使用 queue_free()

释放节点时也会释放它的所有子项。多亏如此,只需删除最顶端的父节点,就可以删除整个场景树分支了。

实例化场景

场景就是模板,你可以用来来创建任意数量的复制品。这样的操作叫作实例化(instancing),在代码中进行实例化总共分两步:

  1. 从硬盘加载场景。

  2. 创建加载到的 PackedScene 资源的实例。

GDScriptC#

  1. var scene = load("res://MyScene.tscn")
  1. var scene = GD.Load<PackedScene>("res://MyScene.tscn");

预加载场景可以提升用户体验,因为加载操作发生在编译器读取脚本时,而非运行时。这个特性是 GDScript 独有的。

GDScript

  1. var scene = preload("res://MyScene.tscn")

此时的 scene 是个打包场景资源,不是节点。要创建实际的节点,你需要调用 PackedScene.instance()。它返回的是一棵节点树,可以添加为当前节点的子节点。

GDScriptC#

  1. var instance = scene.instance()
  2. add_child(instance)
  1. var instance = scene.Instance();
  2. AddChild(instance);

此两步过程的优点在于,打包的场景可以保持加载状态并可以随时使用。例如,你就可以快速地对许多敌人或子弹进行实例化。