godot-composition-apps
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseGodot Composition & Architecture (Apps & UI)
Godot组合模式与架构设计(应用与UI)
This skill enforces the Single Responsibility Principle within Godot's Node system. Whether building an RPG or a SaaS Dashboard, the rule remains: One Script = One Job.
本Skill在Godot的Node系统中严格遵循单一职责原则。无论是开发RPG游戏还是SaaS仪表盘,规则始终不变:一个脚本 = 一项职责。
The Core Philosophy
核心理念
The Litmus Test
验证测试
Before writing a script, ask: "If I attached this script to a literal rock, would it still function?"
- Pass: An on a rock allows the rock to log in. (Context Agnostic)
AuthComponent - Fail: A script on a rock tries to grab text fields the rock doesn't have. (Coupled)
LoginForm
编写脚本前,请先自问:“如果将此脚本附加到一个普通的岩石节点上,它还能正常工作吗?”
- 通过: 岩石节点上的仍能实现登录功能(上下文无关)。
AuthComponent - 不通过: 岩石节点上的脚本试图获取岩石不存在的文本输入框(耦合性高)。
LoginForm
The Backpack Model (Has-A > Is-A)
背包模型(优先“拥有”而非“继承”)
Stop extending base classes to add functionality. Treat the Root Node as an empty Backpack.
- Wrong (Inheritance): extends
SubmitButtonextendsAnimatedButton.BaseButton - Right (Composition): (Root) HAS-A
SubmitButtonand HAS-AAnimationComponent.NetworkRequestComponent
停止通过继承基类来添加功能。将根节点视为一个空的背包。
- 错误(继承): 继承自
SubmitButton,而AnimatedButton又继承自AnimatedButton。BaseButton - 正确(组合): (根节点)拥有一个
SubmitButton和一个AnimationComponent。NetworkRequestComponent
The Hierarchy of Power (Communication Rules)
通信规则层级
Strictly enforce this communication flow to prevent "Spaghetti Code":
| Direction | Source → Target | Method | Reason |
|---|---|---|---|
| Downward | Orchestrator → Component | Function Call | Manager owns the workers; knows they exist. |
| Upward | Component → Orchestrator | Signals | Workers are blind; they just yell "I'm done!" |
| Sideways | Component A ↔ Component B | FORBIDDEN | Siblings must never talk directly. |
The Sideways Fix: Component A signals the Orchestrator; Orchestrator calls function on Component B.
严格遵循以下通信流程,避免出现“面条代码”:
| 方向 | 源 → 目标 | 方式 | 原因 |
|---|---|---|---|
| 向下 | 编排器 → 组件 | 函数调用 | 管理者掌控工作组件;清楚组件的存在。 |
| 向上 | 组件 → 编排器 | Signals | 工作组件无需了解上下文;只需发出“任务完成”的信号即可。 |
| 横向 | 组件A ↔ 组件B | 禁止 | 同级组件绝不能直接通信。 |
横向通信解决方案: 组件A向编排器发送信号;编排器调用组件B的函数。
The Orchestrator Pattern
编排器模式
The Root Node script (e.g., , ) is now an Orchestrator.
LoginScreen.gdUserProfile.gd- Math/Logic: 0%
- State Management: 100%
- Job: Wire components together. Listen to Component signals and trigger other Component functions.
根节点脚本(如、)现在担任编排器的角色。
LoginScreen.gdUserProfile.gd- 数学/逻辑处理: 0%
- 状态管理: 100%
- 职责: 连接各个组件。监听组件的Signals并触发其他组件的函数。
Example: App/UI Context
示例:应用/UI场景
| Concept | App/UI Example |
|---|---|
| Orchestrator | |
| Component 1 | |
| Component 2 | |
| Component 3 | |
| 概念 | 应用/UI示例 |
|---|---|
| 编排器 | |
| 组件1 | |
| 组件2 | |
| 组件3 | |
Implementation Standards
实施标准
1. Type Safety
1. 类型安全
Define components globally. Never use dynamic typing for core architecture.
gdscript
undefined全局定义组件。核心架构绝不能使用动态类型。
gdscript
undefinedauth_component.gd
auth_component.gd
class_name AuthComponent extends Node
undefinedclass_name AuthComponent extends Node
undefined2. Dependency Injection
2. 依赖注入
NEVER use . Paths are brittle.
ALWAYS use Typed Exports and drag-and-drop in the Inspector.
get_node("Path/To/Child")gdscript
undefined绝对不要使用。路径依赖非常脆弱。
务必使用类型化导出,并在Inspector中拖放关联。
get_node("Path/To/Child")gdscript
undefinedOrchestrator script
Orchestrator script
@export var auth: AuthComponent
@export var form_ui: Control
undefined@export var auth: AuthComponent
@export var form_ui: Control
undefined3. Scene Unique Names
3. 场景唯一名称
If internal referencing within a scene is strictly necessary for the Orchestrator, use the Unique Name feature.
%gdscript
@onready var submit_btn = %SubmitButton如果编排器确实需要在场景内部引用节点,请使用唯一名称特性。
%gdscript
@onready var submit_btn = %SubmitButton4. Stateless Components
4. 无状态组件
Components should process the data given to them.
- Bad: finds the username text field itself.
NetworkComponent - Good: has a function
NetworkComponent. The Orchestrator passes the text field data into that function.login(username, password)
组件应仅处理传入的数据。
- 错误示例: 自行查找用户名输入框。
NetworkComponent - 正确示例: 包含
NetworkComponent函数。编排器将输入框的数据传入该函数。login(username, password)
Anti-Patterns (NEVER DO THIS)
反模式(绝对禁止)
- The Monolith: A root script that handles UI events, HTTP requests, AND business logic.
- The Chain: Passing data through 4 layers of nodes to get to the destination. (Use Signals).
- Hard Dependency: checking
InputComponent. (The component must work on a rock; rocks don't have health).get_parent().health
- 单体脚本: 一个根脚本同时处理UI事件、HTTP请求和业务逻辑。
- 链式传递: 通过4层节点传递数据以到达目标节点(应使用Signals)。
- 硬依赖: 调用
InputComponent(组件必须能在岩石节点上工作;岩石没有health属性)。get_parent().health
Code Structure Example (General App)
代码结构示例(通用应用)
Component: clipboard_copier.gd
clipboard_copier.gd组件:clipboard_copier.gd
clipboard_copier.gdgdscript
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("Text empty")
return
DisplayServer.clipboard_set(text)
copy_success.emit()gdscript
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("Text empty")
return
DisplayServer.clipboard_set(text)
copy_success.emit()Orchestrator: share_menu.gd
share_menu.gd编排器:share_menu.gd
share_menu.gdgdscript
extends Controlgdscript
extends ControlWired via Inspector
Wired via Inspector
@export var copier: ClipboardCopier
@export var link_label: Label
func _ready():
# Downward communication
%CopyButton.pressed.connect(_on_copy_button_pressed)
# Upward communication listening
copier.copy_success.connect(_on_copy_success)
func _on_copy_button_pressed():
# Orchestrator delegation
copier.copy_text(link_label.text)
func _on_copy_success():
# Orchestrator managing UI state based on signal
%ToastNotification.show("Link Copied!")
undefined@export var copier: ClipboardCopier
@export var link_label: Label
func _ready():
# Downward communication
%CopyButton.pressed.connect(_on_copy_button_pressed)
# Upward communication listening
copier.copy_success.connect(_on_copy_success)
func _on_copy_button_pressed():
# Orchestrator delegation
copier.copy_text(link_label.text)
func _on_copy_success():
# Orchestrator managing UI state based on signal
%ToastNotification.show("Link Copied!")
undefinedReference
参考
- Master Skill: godot-master
- 主Skill:godot-master