name: godot-composition-apps description: “使用组合模式构建可扩展Godot应用程序(应用、工具、UI或游戏)的专家级架构标准。适用于设计节点结构、重构单体脚本或实现复杂行为。强制使用‘Has-A’关系而非‘Is-A’继承。”
Godot组合与架构(应用与UI)
这个技能在Godot的节点系统中强制实施单一职责原则。无论是构建角色扮演游戏还是SaaS仪表盘,规则始终是:一个脚本 = 一个职责。
核心理念
试金石测试
在编写脚本之前,问:“如果我将这个脚本附加到一个字面上的岩石上,它还能正常工作吗?”
- 通过: 岩石上的
AuthComponent允许岩石登录。(上下文无关) - 失败: 岩石上的
LoginForm脚本试图抓取岩石没有的文本字段。(耦合)
背包模型(Has-A > Is-A)
停止扩展基类来添加功能。将根节点视为一个空的背包。
- 错误(继承):
SubmitButton扩展AnimatedButton扩展BaseButton。 - 正确(组合):
SubmitButton(根节点)拥有AnimationComponent和 拥有NetworkRequestComponent。
权力层次结构(通信规则)
严格执行此通信流以防止“意大利面代码”:
| 方向 | 源 → 目标 | 方法 | 原因 |
|---|---|---|---|
| 向下 | 编排器 → 组件 | 函数调用 | 管理者拥有工作者;知道它们存在。 |
| 向上 | 组件 → 编排器 | 信号 | 工作者是盲目的;它们只是喊“我完成了!” |
| 横向 | 组件 A ↔ 组件 B | 禁止 | 兄弟节点绝不能直接交流。 |
横向修复: 组件 A 向编排器发送信号;编排器调用组件 B 的函数。
编排器模式
根节点脚本(例如,LoginScreen.gd、UserProfile.gd)现在是一个编排器。
- 数学/逻辑: 0%
- 状态管理: 100%
- 工作: 将组件连接在一起。监听组件信号并触发其他组件函数。
示例:应用/UI上下文
| 概念 | 应用/UI示例 |
|---|---|
| 编排器 | UserProfile.gd |
| 组件 1 | AuthValidator(逻辑) |
| 组件 2 | FormListener(输入) |
| 组件 3 | ThemeManager(视觉) |
实现标准
1. 类型安全
全局定义组件。切勿在核心架构中使用动态类型。
# auth_component.gd
class_name AuthComponent extends Node
2. 依赖注入
绝不使用 get_node("Path/To/Child")。路径是脆弱的。
总是使用类型化导出和在检查器中拖放。
# 编排器脚本
@export var auth: AuthComponent
@export var form_ui: Control
3. 场景唯一名称
如果编排器在场景内严格需要内部引用,使用 % 唯一名称功能。
@onready var submit_btn = %SubmitButton
4. 无状态组件
组件应该处理提供给它们的数据。
- 坏:
NetworkComponent自己找到用户名文本字段。 - 好:
NetworkComponent有一个函数login(username, password)。编排器将文本字段数据传递给该函数。
反模式(切勿这样做)
- 单体: 一个根脚本处理UI事件、HTTP请求和业务逻辑。
- 链式传递: 通过4层节点传递数据以到达目的地。(使用信号)。
- 硬依赖:
InputComponent检查get_parent().health。(组件必须在岩石上工作;岩石没有健康值)。
代码结构示例(通用应用)
组件:clipboard_copier.gd
class_name ClipboardCopier extends Node
signal copy_success
signal copy_failed(reason)
func copy_text(text: String) -> void:
if text.is_empty():
copy_failed.emit("文本为空")
return
DisplayServer.clipboard_set(text)
copy_success.emit()
编排器:share_menu.gd
extends Control
# 通过检查器连接
@export var copier: ClipboardCopier
@export var link_label: Label
func _ready():
# 向下通信
%CopyButton.pressed.connect(_on_copy_button_pressed)
# 向上通信监听
copier.copy_success.connect(_on_copy_success)
func _on_copy_button_pressed():
# 编排器委托
copier.copy_text(link_label.text)
func _on_copy_success():
# 编排器基于信号管理UI状态
%ToastNotification.show("链接已复制!")
参考
- 主技能:godot-master