Godot 信号与分组
将以下提示词粘贴到你的 AI 对话框中:
请根据 https://skillhub.cn/install/skillhub.md,安装 @user_3c6cb52e/godot-signals-groups-c522。
技能介绍
要解决的具体问题
在 Godot 项目里,如果你习惯让节点之间直接持有彼此的引用——比如玩家直接调用敌人的 take_damage(),主场景直接保存每一个检查点——很快会发现两个问题:一是场景耦合得厉害,换个父节点就得到处改代码;二是当需要批量操作某一类节点(比如“暂停所有敌人”“保存所有检查点”)时,只能到处遍历查找,代码又散又慢。Godot Signals & Groups 技能就是为了打破这种硬连接,让节点之间通过信号通信、通过分组广播。
核心能力与关键步骤
信号:让子节点说话,父节点倾听
- 由子节点或子场景 声明类型化信号,在事件发生时
emit();父节点负责connect(),而不是反过来。 - 子节点因此可复用、不用关心谁在监听;父节点控制何时响应。
- 支持
CONNECT_ONE_SHOT(只触发一次)、bind()附加额外上下文,甚至可以await信号内联等待。 - 用
is_connected()做保护,避免在_ready()里重复连接导致回调叠加触发。
分组:把一类节点放进命名集合
- 通过
add_to_group("enemies")或编辑器的 Node > Groups 面板把节点加入具名分组。 - 运行时用
get_tree().get_nodes_in_group("enemies")遍历,或用call_group("enemies", "pause")批量调用方法。 - 分组名在整个 SceneTree 中全局有效,不是按场景隔离的,所以跨场景用同一个名字会互相“串台”。
连接与断开
- 连接时用 Callable(
sig.connect(_on_sig)),目标隐含在 Callable 里,不要再写 3.x 时代的connect("died", self, "_on_died")字符串形式。 - 如果监听者是长生命周期对象,释放前要主动
disconnect(),否则连到已释放节点会报错;Godot 会在被连接对象本身被释放时自动断开,但不能依赖它处理你手动free()的情况。
适用边界与注意点
- 目标版本是 Godot 4.3+;3.x 的旧式连接签名已废弃,虽然
Object.connect("died", Callable(...))还能用,但方法名字符串形式不再支持。 - 重复连接是常见问题:节点被重新添加进树时,
_ready()里的连接会再执行一次,导致回调被调用多次,务必先判断if not sig.is_connected(cb)。 call_group会静默忽略没有该方法的节点,方法名写错不会报错,所以在契约重要时优先用类型化信号而不是分组方法广播。- 信号参数必须匹配:声明了几个类型化参数,
emit()就要传几个、类型对上,否则运行时报错。 - 信号和直接调用之间需要权衡:跨场景全局事件建议从 autoload 发出(参见
godot-nodes-scenes),而不是让每个子节点自己到处广播。 - 分组名需要命名空间化(比如
boss_enemies、minor_enemies),否则两个层级用了同名会共享成员,排查起来很痛苦。
使用场景
- 子场景需要通知父节点事件(如玩家死亡、拾取物品),但不想让子节点知道谁在监听
- 游戏需要批量操作同一类节点(如暂停所有敌人、保存所有检查点),不想逐个遍历引用
- 信号需要只触发一次或附带额外参数,并且要防止重复连接导致回调多次执行
- 从 Godot 3.x 迁移到 4.x,旧的字符串式信号连接代码需要更新为 Callable 形式
适合人员
- 正在用 Godot 4.x 开发游戏,遇到节点间硬连接导致场景难以复用和重构的工程师
- 需要对同一类节点进行批量操作,但不想写散落各处的遍历和查找逻辑的开发者
- 从 Godot 3.x 升级到 4.x,需要修正信号连接语法和处理重复连接问题的维护者
- 正在设计跨场景全局事件总线,需要决定信号和分组使用边界的架构师