关于 godot-signal-architecture
面向 Godot 开发者的信号架构技能指南,说明如何在节点层级、跨场景系统和异步流程中组织事件。它强调类型化连接、信号向上通知与方法向下调用,并提供连接清理和调试模式。
主要功能
- 01信号向上、方法向下的组织方式
- 02类型化信号与 Callable 绑定
- 03全局事件总线路由
- 04异步信号序列
- 05动态连接清理与信号调试
适用场景
- 01让子节点向父节点报告状态变化
- 02在跨场景系统间传递事件
- 03编排加载、淡出和生成等异步步骤
- 04切换跟踪目标并诊断遗留连接
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ thedivergentai/ game-dev-agentic-skills --skill godot-signal-architecture安装后在 Claude Code 中输入/godot-signal-architecture确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/implementation-patterns.mdmigration-notes.mdscripts/await_signal_sequencing.gdcallable_bind_context.gdcomplex_signal_sequencer.gddisconnect_ghost_signals.gdglobal_event_bus.gdglobal_signal_bus_router.gdone_shot_deferred_connections.gdsafe_dynamic_connections.gdsignal_debugger.gdsignal_spy.gdsignal_up_call_down_pattern.gdtrack_signal_emitter_source.gdunbind_unwanted_args.gd详细介绍
信号架构中绝对不要做的事
- 绝对不要使用基于字符串的旧版
Object.connect()— 拼写错误会导致静默失败。始终使用signal.connect(_callback)进行编译时验证。 - 绝对不要使用信号自上而下地指挥行为 — 信号表示过去发生的事件(例如“死亡”)。命令应使用直接方法调用(例如“击杀”)。
- 绝对不要将同一个信号重复连接到同一个 Callable — 这会在运行时抛出
ERR_INVALID_PARAMETER,除非使用Object.CONNECT_REFERENCE_COUNTED标志来叠加连接。 - 绝对不要将全局信号总线用于本地数据 — 这会污染全局状态,让调试更加困难。场景专属逻辑应使用本地连接。
- 绝对不要假设回调必须接收信号的所有参数 — 使用
unbind()丢弃不需要的参数,让 API 保持简洁。 - 绝对不要创建循环信号依赖 — A 向 B 发送信号,B 又向 A 发回信号?使用中介(父节点或 AutoLoad)来打破循环。
- 绝对不要跳过信号类型标注 — 不带类型的
signal moved缺少编辑器支持。始终使用signal moved(dir: Vector2)。 - 绝对不要忘记断开动态信号 — 残留连接会导致“对空实例调用”错误。在
_exit_tree()中断开连接,或在重新指定目标时断开连接(disconnect_ghost_signals.gd)。 - 绝对不要发送会立即对发射者产生副作用的信号 — 如果
died.emit()调用queue_free(),监听器可能无法响应。先发送信号。 - 绝对不要将信号用于高频数据流 — 每秒发送 1000 个以上的信号(例如逐粒子更新)效率很低。应使用共享数组或直接缓冲区。
向上发信号 / 向下调用
- 子节点 → 父节点: 过去时信号(
health_changed、died)。 - 父节点 → 子节点: 直接调用 / 属性(
apply_damage、play_anim)。 - 兄弟节点: 使用父节点中介或作用域明确的 Autoload 总线 — 绝不要直接硬引用兄弟节点。
使用信号的场景: UI 按下、死亡 → 游戏结束、战利品 → 背包、跨场景总线事件。 使用直接调用的场景: 父节点指挥子节点、本地属性访问。
决策树:在哪里连接
| 作用域 | 模式 | 必需脚本 |
|---|---|---|
| 子节点通知父节点 / UI | 在父节点 _ready 中进行本地 signal.connect | signal_up_call_down_pattern.gd |
| 父节点协调子节点 | 向下调用方法(而不是信号) | 同上 |
| 跨场景 / 系统(成就、存档) | Autoload 总线 | global_signal_bus_router.gd / global_event_bus.gd |
| 线性异步步骤(加载 → 淡出 → 生成) | await 信号序列 | await_signal_sequencing.gd / complex_signal_sequencer.gd |
| 重新指定跟踪目标(新敌人) | 先断开旧连接 | disconnect_ghost_signals.gd |
| 单次 / 物理安全 | CONNECT_ONE_SHOT / CONNECT_DEFERRED | one_shot_deferred_connections.gd |
| 附加上下文 / 丢弃参数 | Callable.bind / unbind | callable_bind_context.gd / unbind_unwanted_args.gd |
可用脚本
- signal_up_call_down_pattern.gd — 接入层级前必须使用。
- global_signal_bus_router.gd / global_event_bus.gd — 使用 Autoload 总线前必须使用。
- disconnect_ghost_signals.gd — 切换跟踪的发射器时必须使用。
- await_signal_sequencing.gd / complex_signal_sequencer.gd — 处理多步 await 时必须使用。
- safe_dynamic_connections.gd —
is_connected守卫。 - one_shot_deferred_connections.gd — 单次 / 延迟标志。
- callable_bind_context.gd / unbind_unwanted_args.gd — 绑定 / 解绑。
- track_signal_emitter_source.gd —
CONNECT_APPEND_SOURCE_OBJECT。 - signal_debugger.gd / signal_spy.gd — 调试 / 测试间谍。
Lambda 捕获清理(完整)
Godot 在节点释放时会自动断开大多数连接。**例外:**捕获局部变量的 lambda——必须手动断开连接。
var my_lambda: Callable
func _ready() -> void:
var x := 10
my_lambda = func(): print(x)
player.died.connect(my_lambda)
func _exit_tree() -> void:
if player and player.died.is_connected(my_lambda):
player.died.disconnect(my_lambda)
重新指定目标时,优先使用命名方法或 disconnect_ghost_signals.gd。
CONNECT_REFERENCE_COUNTED — 正确语义
CONNECT_REFERENCE_COUNTED 表示多个相同的连接共享一个带引用计数的连接(连接 N 次 / 断开 N 次)。它不表示“发射器释放时自动清理”,也不能修复捕获 lambda 导致的泄漏。
- 释放时自动清理:当任一端释放时,与 Object 方法的普通连接(非捕获)会被清除。
- 捕获 lambda:始终需要手动
disconnect(见上文)。 - 触发后自动移除的单次连接:
CONNECT_ONE_SHOT。
深入教程(按需)
大模型无知规则:如果通用 Agent 在阅读前不会知道某项内容,它就应放在这里或
scripts/中——绝不删除,只能移动。
| 主题 | 参考资料 |
|---|---|
| 模式 1–7 + 注意事项 | implementation-patterns.md |
参考资料
渐进式披露:仅在研究特定 API 时打开官方文档链接;在路由到同领域时加载相关技能——不要预加载整个技能网络。
官方文档
- 使用信号 — 核心的发射/连接模型,以及信号如何在不使用硬引用的情况下解耦节点。
- 场景组织 — 经典的“向上发信号,向下调用”所有权规则,让父→子命令流保持明确。
- 使用信号实例化 — 从生成的场景发射信号,让父节点/管理器接收子弹、战利品及其他产物,而不依赖固定节点路径。
- Autoload 与常规节点 — 何时适合使用全局 EventBus,何时场景内的信号连接更安全。
- 单例(Autoload) — 如何注册可跨场景切换而存在的类型化信号总线。
- Signal — 类型化的
SignalAPI:emit、connect、is_connected和本技能中使用的断开连接辅助方法。 - Callable — 使用
bind()/unbind()注入或丢弃回调上下文,无需包装 lambda。 - Object —
CONNECT_ONE_SHOT、CONNECT_DEFERRED、CONNECT_REFERENCE_COUNTED和CONNECT_APPEND_SOURCE_OBJECT标志。 - GDScript 基础 — 类型化的
signal声明,以及在信号上使用await实现线性异步序列。 - 使用 SceneTree — 进入/退出场景树时的连接生命周期,以及动态监听器在重新指定目标时必须断开连接的原因。
- Godot 通知 — 关于
_ready、父节点缓存和用户信号的安全连接时机。 - 空闲处理与物理处理 — 为什么回调会在物理步进期间修改物理实体时,延迟执行的信号处理函数很重要。
相关技能
前置技能
- godot-project-foundations — 信号所依托的项目布局、Autoload 注册和场景所有权约定。
- godot-gdscript-mastery — 使用高级连接标志和序列器前所需的类型化 Callable、
await和信号语法。 - godot-autoload-architecture — 全局 EventBus 路由器的单例启动顺序和所有权规则(不适用于本地场景事件)。
配套内容
- godot-composition — 组件节点发出过去时事件;父节点通过连接这些信号并向下调用来进行组合。
- godot-scene-management — 场景切换和加载器必须通过总线重新连接或重新发出信号,避免留下幽灵监听器。
- godot-state-machine-advanced — 状态进入/退出通常会触发信号扇出;可避免 FSM 转换形成循环信号图。
- godot-resource-data-patterns — 共享配置优先使用 Resources;信号用于传递变更事件,而非重复的可变状态数据块。
- godot-testing-patterns —
watch_signals/ 间谍对象可与此技能的发出约定配合,用于单元测试和集成测试。 - godot-ui-containers — 按钮和菜单应向上发出意图信号;控制器向下调用以更新 Control 树。
下游 / 消费方
- godot-dialogue-system — 对话行/选项完成事件应遵循信号向上的编排方式,传递到 UI 和任务监听器。
- godot-ability-system — 冷却、施法和命中数据载荷需要使用类型化信号,让 HUD/VFX 与能力节点保持解耦。
- godot-combat-system — 伤害/死亡/得分链是信号向上扇出到 UI、音频和进度系统的典型场景。
- godot-performance-optimization — 出现高频信号风暴时应升级处理;用缓冲区或直接读取替代逐帧信号。
主索引
- godot-master — 技能库路由器和镜像模块入口;需要确定哪个领域技能负责跨领域架构问题时,请打开此项。
评论
支持 Markdown