Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.

节点与场景实例

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

获取节点

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

例如,如果有这样的场景树,你希望获得对 Sprite2D 和 Camera2D 节点的引用,以便在脚本中访问这些节点。

../../_images/nodes_and_scene_instances_player_scene_example.webp

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

var sprite2d
var camera2d

func _ready():
    sprite2d = get_node("Sprite2D")
    camera2d = get_node("Camera2D")

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

../../_images/nodes_and_scene_instances_sprite_node.webp

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

../../_images/nodes_and_scene_instances_sprite_node_renamed.webp

节点路径

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

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

../../_images/nodes_and_scene_instances_ui_scene_example.webp

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

var animation_player

func _ready():
    animation_player = get_node("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.

要删除节点、将其从内存中释放,可以调用其 queue_free() 方法。这样,该节点的删除操作就会被排进一个队列当中,在当前帧处理完成之后就会执行队列中的节点删除操作。删除时,引擎会把该节点从场景(树)中删除,然后释放内存中相应的对象。

sprite2d.queue_free()

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

../../_images/nodes_and_scene_instances_remote_tree_with_sprite.webp

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

../../_images/nodes_and_scene_instances_remote_tree_no_sprite.webp

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

释放节点时也会释放其所有子节点。得益于此,只需删除最顶端的父节点,就可以在场景树中删除该节点及其所有子孙节点了。

实例化场景

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

  1. 从本地硬盘中加载场景。

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

var scene = load("res://my_scene.tscn")

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

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

此时的 scene 是个打包场景资源,并非节点。要创建实际的节点,你还需要调用 PackedScene.instantiate()来创建节点,该方法会返回该打包场景的节点树的根节点。你可以将其添加为当前节点的子节点。

var instance = scene.instantiate()
add_child(instance)

这两步过程的优点在于:打包的场景可以保持加载状态,且可以随时使用。例如,你可以对大量敌人或子弹快速进行实例化。