Godot 信号与组 (4.x)
使用观察者模式(信号)解耦节点,并一次性对多个节点采取行动(组),而不是在场景之间硬编码引用。面向 Godot 4.7。
何时使用
- 当节点需要告诉其他节点“某事发生了”(玩家死亡、拾取物品、波次清除)而不持有它们的直接引用时使用。
- 当你需要一次性寻址一类节点(“暂停所有敌人”、“保存每个检查点”)时使用。
**何时*不*使用:** 原始信号*语法*基础 → godot-gdscript;场景结构和实例化 → godot-nodes-scenes。对于跨场景全局事件,从 autoload 发射(参见 godot-nodes-scenes)。
核心工作流
- 决定方向。 子节点/子场景应向上*发射*信号;父节点*连接*到它。这使子节点可复用,并且不关心谁在监听。
- 在发射器上声明类型化信号;当事件发生时使用
emit()发射它们。 - 使用 Callable 连接(
sig.connect(_on_sig)),也可在编辑器的 Node 面板中完成。使用CONNECT_ONE_SHOT实现仅触发一次,使用bind()传递额外上下文。 - 使用组进行广播:将节点添加到命名组,然后遍历
get_tree().get_nodes_in_group(...)或使用call_group(...)。 - 需要时断开连接(例如在释放长生命周期监听器之前),并检查
is_connected()以避免重复连接。
模式
1. 向上发射,从父节点连接
# coin.gd (reusable pickup — knows nothing about the player or HUD)
extends Area2D
signal collected(value: int)
func _on_body_entered(body: Node) -> void:
if body.is_in_group("player"):
collected.emit(10)
queue_free()
# level.gd (the parent wires the coin to game state)
func _ready() -> void:
for coin in get_tree().get_nodes_in_group("coins"):
coin.collected.connect(_on_coin_collected)
func _on_coin_collected(value: int) -> void:
GameState.add_score(value)
2. 连接标志:一次性与 bind 附加参数
func _ready() -> void:
# Fire exactly once, then auto-disconnect.
$Door.opened.connect(_on_door_opened, CONNECT_ONE_SHOT)
# bind() appends arguments supplied at connect time (after the signal's own args).
$RedButton.pressed.connect(_on_button.bind("red"))
func _on_button(color: String) -> void:
print("Pressed the %s button" % color)
3. 组:向多个节点广播
func pause_all_enemies() -> void:
# Call a method on every node in the "enemies" group (no-op if missing).
get_tree().call_group("enemies", "set_paused", true)
func count_enemies() -> int:
return get_tree().get_nodes_in_group("enemies").size()
从代码或通过编辑器的 Node > Groups 选项卡将节点添加到组:
func _ready() -> void:
add_to_group("enemies") # remove_from_group("enemies") to leave
4. 内联等待信号
func open_chest() -> void:
$AnimationPlayer.play("open")
await $AnimationPlayer.animation_finished # pause until it emits
spawn_loot()
陷阱
- 3.x 的 connect 签名已移除。
connect("died", self, "_on_died")→died.connect(_on_died)。目标由 Callable 隐含。旧式Object.connect("died", Callable(self, "_on_died"))仍有效,但方法名字符串形式无效。 - 重复连接会多次触发处理程序。 在重新添加节点后,于
_ready()中再次连接会叠加回调。使用if not sig.is_connected(cb): sig.connect(cb)进行保护。 - 连接到已释放的节点会出错。 断开长生命周期监听器,或依赖 Godot 在*已连接对象*被释放时自动断开(对节点有效)。
- 组对 SceneTree 全局有效,不是按场景有效。两个关卡使用相同组名会共享成员。如果这很重要,请为组名添加命名空间。
call_group会静默忽略缺少该方法的节点。 方法名拼写错误会静默失败——当契约很重要时,优先使用类型化信号。- 信号参数必须匹配。 使用错误的参数数量/类型发射会引发错误;声明类型化参数并精确发射这些参数。
参考
- 有关连接标志、延迟连接、自定义信号参数、带超时的等待,以及信号与直接调用之间的权衡,请阅读
references/signal-patterns.md。
相关技能
godot-gdscript— 信号/await语法基础。godot-nodes-scenes— 用于全局事件总线的 autoload。game-ai— 经常驱动和消费这些事件的状态机。