godot-nodes-scenes
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseGodot Nodes & Scenes (4.x)
Godot 节点与场景(4.x版本)
Compose games from nodes and scenes, instance them at runtime, and access the tree
without crashing on freed or missing nodes. Targets Godot 4.3+.
基于节点与场景构建游戏,在运行时实例化它们,并在访问场景树时避免因节点已释放或缺失导致崩溃。适用于**Godot 4.3+**版本。
When to use
适用场景
- Use when structuring scenes, choosing how to break a feature into nodes, instancing a
.tscn(bullets, enemies, UI), or setting up autoload singletons.PackedScene - Use when debugging /
get_node()returning$Path, or "Attempt to call on a previously freed instance".null
When not to use: GDScript language/syntax → ; signal-based
decoupling → ; physics bodies/collisions → .
godot-gdscriptgodot-signals-groupsgodot-physics- 适用于构建.tscn场景、选择如何将功能拆分为节点、实例化(如子弹、敌人、UI),或配置自动加载单例时。
PackedScene - 适用于调试/
get_node()返回$Path,或“尝试调用已释放实例”错误时。null
不适用场景:GDScript语言/语法相关问题请参考;基于信号的解耦请参考;物理体/碰撞相关问题请参考。
godot-gdscriptgodot-signals-groupsgodot-physicsCore workflow
核心工作流程
- Model with composition. A scene is a tree of nodes saved as . Build small, single-purpose scenes (Player, Bullet, Enemy) and compose larger scenes from them. Favor adding child nodes over deep inheritance.
.tscn - Make a scene reusable by giving its root a script and exposing config. Save it; it becomes a
@exportyou can instance many times.PackedScene - Instance at runtime with /
preload→load→scene.instantiate(). Set position/state after adding (or before, both work).add_child(instance) - Access nodes safely. Use for fixed children; use unique names (
@onready var x = $Path) for nodes deep in the tree; never assume a node still exists.%Name - Use autoloads for global state/services (game state, audio, scene switching) — registered in Project Settings > Globals (Autoload), accessible by name everywhere.
- Free nodes with and guard later access with
queue_free().is_instance_valid()
- 基于组合建模。场景是保存为.tscn格式的节点树。创建小型、单一用途的场景(如玩家、子弹、敌人),并通过组合这些小场景构建更大的场景。优先添加子节点,而非使用深度继承。
- 让场景可复用:为场景根节点添加脚本并暴露配置项。保存后,该场景会成为可多次实例化的
@export。PackedScene - 运行时实例化:通过/
preload加载场景 → 调用load创建实例 → 使用scene.instantiate()将实例添加到场景树。可在添加前后设置位置/状态(两种方式都可行)。add_child(instance) - 安全访问节点:对于固定子节点,使用;对于树中深层节点,使用唯一名称(
@onready var x = $Path);永远不要假设节点仍然存在。%Name - 使用自动加载管理全局状态/服务(如游戏状态、音频、场景切换)—— 在项目设置>全局(自动加载)中注册,可通过名称在任意位置访问。
- 使用释放节点,并在后续访问时通过
queue_free()验证节点有效性。is_instance_valid()
Patterns
实践模式
1. Instance a scene at runtime
1. 运行时实例化场景
gdscript
extends Node2D
const BULLET := preload("res://bullet.tscn") # preload: loaded at compile time
func shoot(at: Vector2, dir: Vector2) -> void:
var bullet := BULLET.instantiate() # create an instance of the scene
bullet.global_position = at
bullet.direction = dir # set exported/public state
add_child(bullet) # now it's in the tree and runsgdscript
extends Node2D
const BULLET := preload("res://bullet.tscn") # preload: loaded at compile time
func shoot(at: Vector2, dir: Vector2) -> void:
var bullet := BULLET.instantiate() # create an instance of the scene
bullet.global_position = at
bullet.direction = dir # set exported/public state
add_child(bullet) # now it's in the tree and runs2. Safe node access: $, get_node_or_null, and unique names
2. 安全节点访问:$、get_node_or_null与唯一名称
gdscript
@onready var label: Label = $UI/Label # $ is sugar for get_node("UI/Label")
@onready var health_bar: ProgressBar = %HealthBar # % = scene-unique name (rename-proof)
func update() -> void:
var optional := get_node_or_null("Maybe/Missing") # returns null instead of erroring
if optional:
optional.queue_free()gdscript
@onready var label: Label = $UI/Label # $ is sugar for get_node("UI/Label")
@onready var health_bar: ProgressBar = %HealthBar # % = scene-unique name (rename-proof)
func update() -> void:
var optional := get_node_or_null("Maybe/Missing") # returns null instead of erroring
if optional:
optional.queue_free()3. An autoload singleton (global game state)
3. 自动加载单例(全局游戏状态)
gdscript
undefinedgdscript
undefinedgame_state.gd — add in Project Settings > Globals > Autoload as "GameState".
game_state.gd — add in Project Settings > Globals > Autoload as "GameState".
extends Node
var score := 0
signal score_changed(value: int)
func add_score(points: int) -> void:
score += points
score_changed.emit(score) # any scene can: GameState.score_changed.connect(...)
undefinedextends Node
var score := 0
signal score_changed(value: int)
func add_score(points: int) -> void:
score += points
score_changed.emit(score) # any scene can: GameState.score_changed.connect(...)
undefined4. Change the running scene
4. 切换运行场景
gdscript
func go_to_level_2() -> void:
# Swaps the current scene for another. Frees the old scene tree.
get_tree().change_scene_to_file("res://levels/level_2.tscn")
# Or, with a preloaded PackedScene:
# get_tree().change_scene_to_packed(LEVEL_2)gdscript
func go_to_level_2() -> void:
# Swaps the current scene for another. Frees the old scene tree.
get_tree().change_scene_to_file("res://levels/level_2.tscn")
# Or, with a preloaded PackedScene:
# get_tree().change_scene_to_packed(LEVEL_2)Pitfalls
常见陷阱
- /
$Pathreturnget_node()or error when the path is wrong or the node isn't in the tree yet. Usenull, verify the path matches the scene, or use@onreadyfor optional nodes.get_node_or_null() - Renaming a node breaks . Use unique names (
$Path, set via right-click%NameAccess as Unique Name) so deep references survive renames and reparenting. - mid-frame can crash other code still using the node. Prefer
free()(deletes at end of frame) and checkqueue_free().is_instance_valid(node) - Setting child state before is fine, but
add_child()on the child only runs after it enters the tree — don't expect its_ready()vars before that.@onready - Autoload order matters: autoloads are added before the main scene, in list order. An autoload can't depend on the main scene existing yet.
- was renamed to
instance()in Godot 4.instantiate()runs at parse time (path must be constant);preloadruns at runtime (path can be a variable).load - is deferred, not immediate — Godot swaps and frees the old scene at the end of the current frame. Any code after the call still runs against the old tree, and
change_scene_to_file()isn't the new scene until next frame. Don't read the new scene's nodes on the same line; do it from the new scene'sget_tree().current_scene._ready()
- /
$Path返回get_node()或报错:当路径错误或节点尚未加入场景树时会出现此问题。使用null、验证路径是否与场景匹配,或对可选节点使用@onready。get_node_or_null() - 重命名节点会破坏:使用唯一名称(
$Path,通过右键>设为唯一名称访问),这样深层引用在节点重命名或重新父化后仍然有效。%Name - 帧中调用可能导致崩溃:如果其他代码仍在使用该节点,会引发崩溃。优先使用
free()(在帧末尾删除节点),并通过queue_free()检查节点有效性。is_instance_valid(node) - 在前设置子节点状态是可行的,但子节点的
add_child()仅在加入场景树后才会运行——在此之前不要依赖其_ready()变量。@onready - 自动加载顺序很重要:自动加载项会按列表顺序在主场景之前添加。自动加载项不能依赖主场景的存在。
- 已重命名:在Godot 4中,
instance()改名为instance()。instantiate()在解析时运行(路径必须为常量);preload在运行时运行(路径可为变量)。load - 是延迟执行,而非立即生效——Godot会在当前帧末尾切换并释放旧场景。调用后的任何代码仍会基于旧场景树运行,且
change_scene_to_file()要到下一帧才会指向新场景。不要在调用同一行读取新场景的节点;应在新场景的get_tree().current_scene中进行操作。_ready()
References
参考资料
- For scene inheritance, /ownership when saving scenes from code, groups vs unique names, and node-path edge cases, read
owner.references/tree-and-instancing.md
- 关于场景继承、代码保存场景时的/所有权、组与唯一名称的对比,以及节点路径的边缘情况,请阅读
owner。references/tree-and-instancing.md
Related skills
相关技能
- — language, lifecycle, and
godot-gdscript.@onready - — decouple instanced scenes from their spawner.
godot-signals-groups - — share data between instances without duplicating it.
godot-resources - — persist scene/game state across runs.
save-systems
- — 语言、生命周期与
godot-gdscript相关内容。@onready - — 将实例化场景与其生成器解耦。
godot-signals-groups - — 在实例间共享数据而不重复复制。
godot-resources - — 在多次运行间持久化场景/游戏状态。
save-systems