Attention: Here be dragons
This is the latest
(unstable) version of this documentation, which may document features
not available in or compatible with released stable versions of Godot.
Checking the stable version of the documentation...
节点与场景实例
本指南将介绍如何获取节点、创建节点,如何将节点添加为子项,以及如何使用代码实例化场景。
参见
Check the 创建实例 tutorial to learn about Godot's approach to scene instancing.
获取节点
你可以通过调用 Node.get_node() 方法来获取对某个节点的引用,此时子节点必须在场景树中才能获取成功。在父节点的 _ready()
函数中获取就可以保证这一点。
例如,如果有这样的场景树,你希望获得对 Sprite2D 和 Camera2D 节点的引用,以便在脚本中访问这些节点。

那么,你便可以使用如下代码。
var sprite2d
var camera2d
func _ready():
sprite2d = get_node("Sprite2D")
camera2d = get_node("Camera2D")
private Sprite2D _sprite2D;
private Camera2D _camera2D;
public override void _Ready()
{
base._Ready();
_sprite2D = GetNode<Sprite2D>("Sprite2D");
_camera2D = GetNode<Camera2D>("Camera2D");
}
请注意:你是通过名称来获取节点的,而非通过节点的类型来获取的。上面的“Sprite2D”和“Camera2D”都是这些节点在场景中的名字。

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

节点路径
获取节点的引用时,并不仅限于直接子节点。get_node()
函数支持使用节点路径来获取节点。节点路径有点类似文件浏览器里的路径,可以用斜杠来分隔节点。
在下面这个实例场景中,脚本是附加在 UserInterface 节点上的。

要获取 AnimationPlayer 节点,你可以使用如下代码。
var animation_player
func _ready():
animation_player = get_node("ShieldBar/AnimationPlayer")
private AnimationPlayer _animationPlayer;
public override void _Ready()
{
base._Ready();
_animationPlayer = GetNode<AnimationPlayer>("ShieldBar/AnimationPlayer");
}
备注
和文件路径一样,你也可以使用“..”来获取父节点,最好不要这么做,以免破坏封装。你还可以让路径以斜杠开头,这样的路径叫做绝对路径,其最上层的节点就是“/root”,即程序预定义的根视口。
语法糖
GDScript 中有两个快速写法来缩短节点获取代码的长度。首先是在成员变量的前面写上 @onready
注解,这样这个变量就会刚好在 _ready()
回调之前初始化。
@onready var sprite2d = get_node("Sprite2D")
还有一个快速写法就是 get_node()
的缩写:美元符号“$”,可以把它放在想要获取的名称或者节点路径之前。
@onready var sprite2d = $Sprite2D
@onready var animation_player = $ShieldBar/AnimationPlayer
创建节点
要通过代码创建节点,请像对象类型一样,调用其 new()
方法。
你可以将新创建的节点的引用保存在一个变量中,然后调用 add_child()
方法,将其添加为脚本所在节点的子节点。
var sprite2d
func _ready():
var sprite2d = Sprite2D.new() # Create a new Sprite2D.
add_child(sprite2d) # Add it as a child of this node.
private Sprite2D _sprite2D;
public override void _Ready()
{
base._Ready();
_sprite2D = new Sprite2D(); // Create a new Sprite2D.
AddChild(_sprite2D); // Add it as a child of this node.
}
要删除节点、将其从内存中释放,可以调用其 queue_free()
方法。这样,该节点的删除操作就会被排进一个队列当中,在当前帧处理完成之后就会执行队列中的节点删除操作。删除时,引擎会把该节点从场景(树)中删除,然后释放内存中相应的对象。
sprite2d.queue_free()
_sprite2D.QueueFree();
在调用 sprite2d.queue_free()
之前,远程场景树是这样的。

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

你也可以调用 free()
来立即删除该节点。调用时需要小心:所有对它的引用都会立即变成 null
,除非你知道自己在干什么,否则建议使用 queue_free()
。
释放节点时也会释放其所有子节点。得益于此,只需删除最顶端的父节点,就可以在场景树中删除该节点及其所有子孙节点了。
实例化场景
场景就是模板,你可以用场景来创建出任意数量的复制品,这种的操作就叫作实例化(instancing)。在代码中进行实例化总共分两步:
从本地硬盘中加载场景。
创建已加载好的 PackedScene 资源的实例。
var scene = load("res://my_scene.tscn")
var scene = GD.Load<PackedScene>("res://MyScene.tscn");
预加载场景可以提升用户体验,因为加载操作是发生在编译器读取脚本的时候进行的,而非在游戏运行时进行,这个特性是 GDScript 所独有的。
var scene = preload("res://my_scene.tscn")
此时的 scene
是个打包场景资源,并非节点。要创建实际的节点,你还需要调用 PackedScene.instantiate()来创建节点,该方法会返回该打包场景的节点树的根节点。你可以将其添加为当前节点的子节点。
var instance = scene.instantiate()
add_child(instance)
var instance = scene.Instantiate();
AddChild(instance);
这两步过程的优点在于:打包的场景可以保持加载状态,且可以随时使用。例如,你可以对大量敌人或子弹快速进行实例化。