Godot Programming Patterns 是一个面向 Godot 4.x 的代码设计技能,目标是在出现明确的设计或性能压力时,选择并落地最小的模式,而不是默认堆叠抽象。
用途与范围 用于:把 SOLID 作为诊断工具应用到 Godot 脚本与场景组合;将行为拆分为职责聚焦的组件而避免不必要的继承;设计、发射、连接和测试类型化信号;把可逆、可排队、可重放或可重映射的动作封装为命令;实现具备明确 acquire、activate、release、reset 契约的对象池。 不用于:节点、场景、资源、Input Map 或编辑器的入门指导(应使用 fundamentals 技能);也不用于决定权威游戏状态、autoload 管理器、场景切换或持久化格式(应使用 game-architecture 技能)。本地模式可以支撑这些系统,但本技能不决定其归属。
重构前检查 读取 project.godot 的 config/features 确认具体 Godot 4.x 基线;查看相邻脚本与场景,匹配静态类型、class_name、命名、目录、组件、连接与测试约定;复现当前行为并记录基线,对象池需以 profiler 或监控证据确认分配确实是瓶颈;梳理调用方、被调用方、信号、分组、节点路径、导出引用和运行时创建节点;标记生命周期与所有权,信号连接、命令目标或池化对象不得比其所有者存活更久;先搜索是否已有组件、事件、命令基类、历史服务或对象池;明确压力类型:多种变更理由、一对多通知、动作历史或实测的高频创建销毁;除非请求本身要改契约,否则保留公开行为与编辑器连线。
选择最小模式 调用方需要结果或控制顺序时,对一个已知协作者使用直接方法调用;对已经发生且可能被零个或多个本地监听者关注的事实使用信号;行为有自身状态、生命周期或复用边界时使用聚焦组件;动作需要被存储、反转、排队、重放、记录或与执行分离生成时使用命令;仅在反复实例化/销毁被实测为昂贵且对象可可靠重置时使用对象池;仅当所有子类型都遵守基类契约时使用继承,可选能力优先用场景组合。默认不组合模式,例如一次性按钮动作不需要命令对象、事件总线和对象池。
实施工作流 先写下现有可观察契约或添加聚焦回归测试;引入一个接缝(类型化方法、组件、信号、命令或池边界);在项目可运行的前提下迁移一个生产者和一个消费者;通过导出引用、设置参数或所属场景明确所有权;所有调用方迁移后再移除旧路径;覆盖生命周期边界:场景退出、目标删除、重复连接、删除后撤销、池耗尽;与基线比较行为和性能。
模式要点 SOLID:一个脚本应有一个连贯的变更理由,而非任意行数限制;输入意图、模拟与表现独立演化时分开;通过聚焦组件或稳定契约扩展;子类型前置条件不更严、结果不更弱;依赖协作者所需的最小 API,依赖传入而非全局查找;不要为三行方法新建节点,拆分只有在明确所有权、变更压力、测试或复用时才有价值。 信号:以已完成事实命名(如 value_changed),为负载指定类型;发射方拥有信号含义,监听方拥有反应;信号不是存储状态,晚到的监听者应先读取当前值再订阅后续变化;确保一次变更只发射一次,值未变不发射。 命令:命令应包含执行所需的一切,以及承诺撤销时恢复精确先前状态所需的信息;输入收集留在命令外;长期历史中不保存未定义失效行为的活节点引用;用于语义动作而非连续移动的每一帧,拖拽类交互应合并或设检查点。 对象池:先定义生命周期——获取非活动实例、用全部生成数据激活、使用时不分配替代状态、任务结束请求释放、重置每个可变子系统、恰好归还一次;仅隐藏可见性不是足够的非活动契约,处理、监控、计时器、动画、粒子、音频、速度和临时信号连接在隐藏时仍可能继续。
验证 运行项目既有检查,并直接验证所选模式:重构后旧调用方仍可用、提取单元可独立测试、未引入新的全局查找;信号一次变更发射一次、未变不发射、负载正确、重建监听者不产生重复反应;命令执行后撤销恢复精确先前状态、重做重新应用、新命令清空重做、空历史安全、无效目标遵循既定策略;对象池预热达到预期大小、稳态复用停止分配、耗尽时遵循增长/丢弃/失败策略、释放幂等、复用对象无残留状态;在相同负载下比较 profiler 捕获,报告帧时间、分配或节点变动是否改善;反复切换或重载场景检查孤立节点、过期回调和保留引用。
常见失败模式 类更多但耦合不变(只改了文件名未改契约);把信号当命令用(发射方期待恰好一个监听者完成必需工作);错过启动更新(初始发射后才订阅);重复回调(初始化连接多次或长寿命发射方意外保留监听者);撤销漂移(用计算出的逆操作代替恢复捕获的先前状态);命令目标消失(历史保留了超出生命周期的场景节点,需按明确策略拒绝、修剪或解析稳定目标);池双重归还(碰撞与超时同时释放同一对象,需显式活动状态与幂等释放);隐藏的池化对象仍在行动(碰撞、处理、计时器、动画或音频未禁用);对象池反而更慢(负载并非分配受限、线性扫描或重置成本高于实例化,应保留实测更简单的设计)。