Node.get_node
2026/4/14大约 7 分钟
最后同步日期:2026-04-16 | Godot 官方原文 — Node.get_node
Node.get_node
定义
get_node 就像在一个公司里"按姓名找人"——你告诉它要找的节点叫什么名字、在哪个部门(路径),它就把那个节点交给你。
在 Godot 中,所有东西都是节点,节点之间有父子关系,组成一棵"树"(就像家谱或公司组织架构图)。get_node 就是这棵树上最常用的"查找工具":你给它一个路径字符串,它沿着这棵树找到目标节点并返回给你。
这是 Godot 中使用频率最高的 API 之一,几乎所有脚本都需要用它来访问场景中的其他节点——比如拿到精灵节点来换图片、拿到碰撞体来检测攻击、拿到 UI 标签来更新分数。
函数签名
C#
// 泛型版本:直接返回目标类型(推荐)
public T GetNode<T>(NodePath path) where T : class
// 非泛型版本:返回 Node 基类,需要手动转型
public Node GetNode(NodePath path)GDScript
get_node(path: NodePath) -> Node参数说明
| 参数 | 类型 | 必需 | 说明 |
|---|---|---|---|
| path | NodePath(通常传字符串即可) | 是 | 目标节点的路径。可以是简单的名字(如 "Sprite2D"),也可以是包含层级关系的路径(如 "Body/Head/Hat"),还支持特殊符号(如 ".." 代表父节点) |
路径写法速查
| 写法 | 含义 | 生活类比 |
|---|---|---|
"ChildName" | 当前节点的直接子节点,名叫 ChildName | "找我手下的员工小王" |
"Path/To/Node" | 沿着层级往下找 | "找市场部下面的推广组的小李" |
".." | 当前节点的父节点 | "找我的直属上司" |
"../Sibling" | 父节点下的兄弟节点 | "找我上司手下的另一位员工" |
"/root/NodeName" | 从场景树根节点开始的绝对路径 | "从CEO开始往下找" |
什么是 NodePath?
NodePath 是 Godot 中专门用来表示节点路径的类型。不过你 不需要手动创建 NodePath 对象,直接传字符串就行——Godot 会自动把字符串转换成 NodePath。所以写 "Sprite2D" 和写 new NodePath("Sprite2D") 效果完全一样。
返回值
返回路径对应的 Node 对象。如果找不到该路径的节点,程序会直接报错崩溃(抛出异常)。
- C# 泛型版
GetNode<T>()返回的是T类型,不需要再手动转型。 - C# 非泛型版
GetNode()和 GDScript 的get_node()返回的是Node基类,如果需要使用子类特有的属性和方法,需要手动转型。
代码示例
基础用法
假设你的场景树结构如下:
Player(当前脚本挂在这里)
├── Sprite2D
├── CollisionShape2D
└── HealthBar在 Player 脚本中获取子节点:
C#
public override void _Ready()
{
// 获取名叫 "Sprite2D" 的直接子节点
var sprite = GetNode("Sprite2D");
GD.Print(sprite.Name); // 运行结果: Sprite2D
// 使用泛型版本,直接拿到 Sprite2D 类型,可以访问专属属性
var typedSprite = GetNode<Sprite2D>("Sprite2D");
GD.Print(typedSprite.Position); // 运行结果: (0, 0)
// 获取另一个子节点
var healthBar = GetNode("HealthBar");
GD.Print(healthBar.Name); // 运行结果: HealthBar
}GDScript
func _ready():
# 获取名叫 "Sprite2D" 的直接子节点
var sprite = get_node("Sprite2D")
print(sprite.name) # 运行结果: Sprite2D
# GDScript 返回的是 Node,但可以直接当作子类使用
# (GDScript 是动态类型,不需要手动转型)
var typed_sprite = get_node("Sprite2D") as Sprite2D
print(typed_sprite.position) # 运行结果: (0, 0)
# 获取另一个子节点
var health_bar = get_node("HealthBar")
print(health_bar.name) # 运行结果: HealthBar实际场景
在一个完整的游戏场景中,玩家角色需要访问自己的各个部件节点,并响应受伤时更新血条:
场景树结构:
Player
├── Body
│ ├── Sprite2D
│ └── CollisionShape2D
├── HealthBar
│ └── Label
└── WeaponC#
public partial class Player : CharacterBody2D
{
[Export] public int ExMaxHealth = 100;
private int _currentHealth;
private Sprite2D _bodySprite;
private Label _healthLabel;
public override void _Ready()
{
_currentHealth = ExMaxHealth;
// 用路径访问嵌套子节点:Body 下面的 Sprite2D
_bodySprite = GetNode<Sprite2D>("Body/Sprite2D");
// 用路径访问更深层的节点:HealthBar 下面的 Label
_healthLabel = GetNode<Label>("HealthBar/Label");
// 更新 UI 显示
UpdateHealthDisplay();
}
public void TakeDamage(int damage)
{
_currentHealth -= damage;
// 受伤时让角色闪烁(操作之前获取的节点引用)
_bodySprite.Modulate = new Color(1, 0.5f, 0.5f);
UpdateHealthDisplay();
// 运行结果: 受伤后血条文字更新为 "HP: 80/100"
// 角色精灵变为粉红色(受伤闪红效果)
}
private void UpdateHealthDisplay()
{
_healthLabel.Text = $"HP: {_currentHealth}/{ExMaxHealth}";
}
}GDScript
extends CharacterBody2D
@export var ex_max_health: int = 100
var _current_health: int
var _body_sprite: Sprite2D
var _health_label: Label
func _ready():
_current_health = ex_max_health
# 用路径访问嵌套子节点:Body 下面的 Sprite2D
_body_sprite = get_node("Body/Sprite2D")
# 用路径访问更深层的节点:HealthBar 下面的 Label
_health_label = get_node("HealthBar/Label")
# 更新 UI 显示
_update_health_display()
func take_damage(damage: int):
_current_health -= damage
# 受伤时让角色闪烁(操作之前获取的节点引用)
_body_sprite.modulate = Color(1, 0.5, 0.5)
_update_health_display()
# 运行结果: 受伤后血条文字更新为 "HP: 80/100"
# 角色精灵变为粉红色(受伤闪红效果)
func _update_health_display():
_health_label.text = "HP: %d/%d" % [_current_health, ex_max_health]进阶用法
使用相对路径和绝对路径,以及泛型访问和安全性检查:
场景树结构:
/root
├── GameManager
└── Level
├── Player
│ └── Weapon
└── EnemySpawnerC#
public partial class Weapon : Node2D
{
private Timer _cooldownTimer;
public override void _Ready()
{
// 1. 普通获取子节点
_cooldownTimer = GetNode<Timer>("CooldownTimer");
// 2. 用 ".." 获取父节点(Player)
var player = GetNode<CharacterBody2D>("..");
GD.Print(player.Name); // 运行结果: Player
// 3. 用 "../.." 获取祖父节点(Level)
var level = GetNode<Node>("../..");
GD.Print(level.Name); // 运行结果: Level
// 4. 用 "../Sibling" 获取兄弟节点(从 Player 的角度找 EnemySpawner)
var spawner = GetNode("../EnemySpawner");
GD.Print(spawner.Name); // 运行结果: EnemySpawner
// 5. 用绝对路径从根节点查找(适合访问全局单例)
var gameManager = GetNode<GameManager>("/root/GameManager");
GD.Print(gameManager.Name); // 运行结果: GameManager
// 6. 安全获取:用 GetNodeOrNull 避免找不到节点时报错
var maybeNode = GetNodeOrNull<Label>("UI/ScoreLabel");
if (maybeNode != null)
{
GD.Print("找到了分数标签");
}
else
{
GD.Print("分数标签不存在,跳过"); // 运行结果: 如果该路径不存在节点,走这个分支
}
}
}GDScript
extends Node2D
var _cooldown_timer: Timer
func _ready():
# 1. 普通获取子节点
_cooldown_timer = get_node("CooldownTimer") as Timer
# 2. 用 ".." 获取父节点(Player)
var player = get_node("..") as CharacterBody2D
print(player.name) # 运行结果: Player
# 3. 用 "../.." 获取祖父节点(Level)
var level = get_node("../..")
print(level.name) # 运行结果: Level
# 4. 用 "../Sibling" 获取兄弟节点(从 Player 的角度找 EnemySpawner)
var spawner = get_node("../EnemySpawner")
print(spawner.name) # 运行结果: EnemySpawner
# 5. 用绝对路径从根节点查找(适合访问全局单例)
var game_manager = get_node("/root/GameManager")
print(game_manager.name) # 运行结果: GameManager
# 6. 安全获取:用 get_node_or_null 避免找不到节点时报错
var maybe_node = get_node_or_null("UI/ScoreLabel")
if maybe_node != null:
print("找到了分数标签")
else:
print("分数标签不存在,跳过") # 运行结果: 如果该路径不存在节点,走这个分支绝对路径的根节点是 /root,不是 /
Godot 的绝对路径以 /root 开头,而不是 /。/root 就是场景树的真正根节点(SceneTree)。所以写成 "/root/MyNode" 才是对的,写成 "/MyNode" 会报错找不到。
注意事项
- 路径区分大小写:节点名
"Sprite2D"和"sprite2d"是不同的。Godot 默认节点名会保留大小写,写错一个字母都会导致找不到节点。 - 节点必须已经在场景树中:
get_node只能查找已经进入场景树的节点。如果在_Ready()之前调用(比如在构造函数或字段初始化时),节点可能还没被添加到树中,会报错。最安全的做法是在_Ready()及之后调用。 - 找不到节点会直接崩溃:
get_node在节点不存在时会抛出错误并中断执行。如果不确定节点是否存在,应该用GetNodeOrNull()(C#)或get_node_or_null()(GDScript)代替,它们在找不到时返回null而不报错。 - 缓存引用避免重复查找:
get_node每次调用都会遍历路径查找。如果你需要频繁访问同一个节点,应该在_Ready()中获取一次并保存到变量中(如示例中的_bodySprite),后续直接使用变量。 - 路径写死不利于重构:如果场景树结构改变(比如给节点改了名字或挪了位置),所有硬编码的路径字符串都会失效。Godot 提供了
@onready(GDScript)和[Export](C# 中的NodePath)机制来缓解这个问题。
C# 特别说明
- 方法名大小写:C# 中是
GetNode<T>()(PascalCase),GDScript 中是get_node()(snake_case)。 - 推荐使用泛型版本:C# 的
GetNode<T>()返回具体类型,省去了手动转型的麻烦,同时编译器会帮你检查类型是否正确。非泛型的GetNode()返回Node,需要你手动as转型。 - GetNodeOrNull:C# 也有对应的
GetNodeOrNull<T>(NodePath)方法,找不到时返回null而不报错,适合在不确定节点是否存在时使用。
