返回技能市场
开发运维 安全

godot-signals-groups

@admin/godot-signals-groups

通过 Godot 信号和节点分组构建事件驱动通信,连接或广播事件,减少节点之间的直接依赖。

admin 热度 460v0.0.1

Godot 信号与组 (4.x)

使用观察者模式(信号)解耦节点,并一次性对多个节点采取行动(组),而不是在场景之间硬编码引用。面向 Godot 4.7

何时使用

  • 当节点需要告诉其他节点“某事发生了”(玩家死亡、拾取物品、波次清除)而不持有它们的直接引用时使用。
  • 当你需要一次性寻址一类节点(“暂停所有敌人”、“保存每个检查点”)时使用。

**何时*不*使用:** 原始信号*语法*基础 → godot-gdscript;场景结构和实例化 → godot-nodes-scenes。对于跨场景全局事件,从 autoload 发射(参见 godot-nodes-scenes)。

核心工作流

  1. 决定方向。 子节点/子场景应向上*发射*信号;父节点*连接*到它。这使子节点可复用,并且不关心谁在监听。
  2. 在发射器上声明类型化信号;当事件发生时使用 emit() 发射它们。
  3. 使用 Callable 连接sig.connect(_on_sig)),也可在编辑器的 Node 面板中完成。使用 CONNECT_ONE_SHOT 实现仅触发一次,使用 bind() 传递额外上下文。
  4. 使用组进行广播:将节点添加到命名组,然后遍历 get_tree().get_nodes_in_group(...) 或使用 call_group(...)
  5. 需要时断开连接(例如在释放长生命周期监听器之前),并检查 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 — 经常驱动和消费这些事件的状态机。
qianwen skills install @admin/godot-signals-groups