godot-composition-apps

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Godot 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
    AuthComponent
    on a rock allows the rock to log in. (Context Agnostic)
  • Fail: A
    LoginForm
    script on a rock tries to grab text fields the rock doesn't have. (Coupled)
编写脚本前,请先自问:“如果将此脚本附加到一个普通的岩石节点上,它还能正常工作吗?”
  • 通过: 岩石节点上的
    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):
    SubmitButton
    extends
    AnimatedButton
    extends
    BaseButton
    .
  • Right (Composition):
    SubmitButton
    (Root) HAS-A
    AnimationComponent
    and HAS-A
    NetworkRequestComponent
    .
停止通过继承基类来添加功能。将根节点视为一个空的背包
  • 错误(继承):
    SubmitButton
    继承自
    AnimatedButton
    ,而
    AnimatedButton
    又继承自
    BaseButton
  • 正确(组合):
    SubmitButton
    (根节点)拥有一个
    AnimationComponent
    和一个
    NetworkRequestComponent

The Hierarchy of Power (Communication Rules)

通信规则层级

Strictly enforce this communication flow to prevent "Spaghetti Code":
DirectionSource → TargetMethodReason
DownwardOrchestrator → ComponentFunction CallManager owns the workers; knows they exist.
UpwardComponent → OrchestratorSignalsWorkers are blind; they just yell "I'm done!"
SidewaysComponent A ↔ Component BFORBIDDENSiblings 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.,
LoginScreen.gd
,
UserProfile.gd
) is now an Orchestrator.
  • Math/Logic: 0%
  • State Management: 100%
  • Job: Wire components together. Listen to Component signals and trigger other Component functions.
根节点脚本(如
LoginScreen.gd
UserProfile.gd
)现在担任编排器的角色。
  • 数学/逻辑处理: 0%
  • 状态管理: 100%
  • 职责: 连接各个组件。监听组件的Signals并触发其他组件的函数。

Example: App/UI Context

示例:应用/UI场景

ConceptApp/UI Example
Orchestrator
UserProfile.gd
Component 1
AuthValidator
(Logic)
Component 2
FormListener
(Input)
Component 3
ThemeManager
(Visual)
概念应用/UI示例
编排器
UserProfile.gd
组件1
AuthValidator
(逻辑处理)
组件2
FormListener
(输入处理)
组件3
ThemeManager
(视觉处理)

Implementation Standards

实施标准

1. Type Safety

1. 类型安全

Define components globally. Never use dynamic typing for core architecture.
gdscript
undefined
全局定义组件。核心架构绝不能使用动态类型。
gdscript
undefined

auth_component.gd

auth_component.gd

class_name AuthComponent extends Node
undefined
class_name AuthComponent extends Node
undefined

2. Dependency Injection

2. 依赖注入

NEVER use
get_node("Path/To/Child")
. Paths are brittle. ALWAYS use Typed Exports and drag-and-drop in the Inspector.
gdscript
undefined
绝对不要使用
get_node("Path/To/Child")
。路径依赖非常脆弱。 务必使用类型化导出,并在Inspector中拖放关联。
gdscript
undefined

Orchestrator script

Orchestrator script

@export var auth: AuthComponent @export var form_ui: Control
undefined
@export var auth: AuthComponent @export var form_ui: Control
undefined

3. 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 = %SubmitButton

4. Stateless Components

4. 无状态组件

Components should process the data given to them.
  • Bad:
    NetworkComponent
    finds the username text field itself.
  • Good:
    NetworkComponent
    has a function
    login(username, password)
    . The Orchestrator passes the text field data into that function.
组件应仅处理传入的数据。
  • 错误示例:
    NetworkComponent
    自行查找用户名输入框。
  • 正确示例:
    NetworkComponent
    包含
    login(username, password)
    函数。编排器将输入框的数据传入该函数。

Anti-Patterns (NEVER DO THIS)

反模式(绝对禁止)

  1. The Monolith: A root script that handles UI events, HTTP requests, AND business logic.
  2. The Chain: Passing data through 4 layers of nodes to get to the destination. (Use Signals).
  3. Hard Dependency:
    InputComponent
    checking
    get_parent().health
    . (The component must work on a rock; rocks don't have health).
  1. 单体脚本: 一个根脚本同时处理UI事件、HTTP请求和业务逻辑。
  2. 链式传递: 通过4层节点传递数据以到达目标节点(应使用Signals)。
  3. 硬依赖:
    InputComponent
    调用
    get_parent().health
    (组件必须能在岩石节点上工作;岩石没有health属性)。

Code Structure Example (General App)

代码结构示例(通用应用)

Component:
clipboard_copier.gd

组件:
clipboard_copier.gd

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()
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

gdscript
extends Control
gdscript
extends Control

Wired 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!")
undefined

Reference

参考

  • Master Skill: godot-master
  • 主Skill:godot-master