mcpField
G
godot-composition

godot-composition

github.com/thedivergentai开发907515 次安装2 周前更新
代码仓库

关于 godot-composition

这是一项 Godot 技能,指导使用相互协作的 Node 组合游戏实体,而非构建深层继承树。面向开发可复用实体行为的人员,强调类型化依赖注入、信号通信和专用组件脚本。

主要功能

  1. 01单一职责的 Node 组件
  2. 02通过编排器连接实体组件
  3. 03类型化 @export 依赖注入
  4. 04信号向上通知、方法向下调用
  5. 05可复用的生命值、移动和状态组件

适用场景

  1. 01将生命值和伤害行为从实体脚本中拆分
  2. 02通过组件组合玩家和敌人的行为
  3. 03避免组件依赖父级脚本进行连接
  4. 04使用子 Node 组织状态机

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/thedivergentai/game-dev-agentic-skills --skill godot-composition

安装后在 Claude Code 中输入/godot-composition确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
migration-notes.md
orchestrator-recipes.md
resources/
health_component.gd
hitbox_component.gd
scripts/
composition_root_init.gd
follower_component.gd
health_component.gd
hit_box_component.gd
hurt_box_component.gd
interaction_component.gd
state_component_vsm.gd
status_effect_component.gd
velocity_component.gd
visual_sync_component.gd

详细介绍

核心理念

此技能坚持组合优于继承(“拥有”而非“属于”)。 在 Godot 中,节点就是组件。复杂实体(Player)只是一个协调器,用于管理专门的工作节点(组件)。

黄金法则
  1. 单一职责:一个脚本 = 一项工作。
  2. 封装:组件“各司其职”。它们处理自己的内部逻辑,但不知道谁拥有它们。
  3. 协调器:根脚本(例如 player.gd)不包含任何逻辑。它只管理状态并在组件之间传递数据。
  4. 解耦:组件通过 Signals(向上)和 Methods(向下)通信。

决策树 — 组合与 Autoload 及继承的选择

情况选择
游戏实体行为(HP、受击区域、移动、交互)组合 — 子组件 + 协调器(composition_root_init.gd)
跨场景服务(音频总线、存档、网络、经济账本)Autoload — 而非玩家身上的组件
真正的 is-a 引擎特化(拥有共享生命周期的自定义 Control/Node)继承例外 — 很少使用;绝不用来“添加枪支”/“添加 HP”

可用脚本

health_component.gd

专用节点,用于管理任何实体的生命周期、伤害逻辑和死亡信号。

hit_box_component.gd

基于 Area 的组件,用于拦截伤害并将其交给 HealthComponent 处理。

hurt_box_component.gd

基于 Area 的组件,专门用于对 HitBoxComponent 造成伤害。

velocity_component.gd

封装的移动和加速度逻辑,可在玩家和敌人之间复用。

interaction_component.gd

解耦的交互处理器,通过注入 Callable 逻辑执行上下文感知操作。

follower_component.gd

解耦的跟踪逻辑,通过注入 NodePath 实现实体平滑跟随。

state_component_vsm.gd

基于组件的状态机模式,使用子节点表示各个独立状态。

status_effect_component.gd

通过将效果场景作为子节点堆叠,管理临时修正效果(增益/减益)。

visual_sync_component.gd

将逻辑状态(速度/方向)与视觉表现(精灵翻转)分离。

composition_root_init.gd

必须首先阅读 — 通过类型化的 @export(检查器 / 场景中的 %UniqueNames)连接协调器。严格遵循:绝不使用 $ / get_node 获取组件。

组合中绝不可做的事

  • 绝不要使用深层继承链(例如,Player > Entity > LivingThing > Node)——这会创建难以重构、脆弱的“上帝类”[21]。
  • 绝不要对组件使用 get_node() 或 $——场景树重新排列时,这种做法会失效。始终使用 @export 或 %UniqueNames [22]。
  • 绝不要让组件直接引用其父脚本——这会让组件无法复用。请使用信号或依赖注入 [23]。
  • 绝不要在一个脚本中混合输入、物理和游戏逻辑——这违反单一职责原则。应将其拆分为专用组件 [24, 13]。
  • 绝不要创建依赖特定 SceneTree 结构的组件——组件应当“专注自身”,只关心自身属性和直接子级。
  • 绝不要通过继承来“添加功能”——如果你想让敌人射击,就添加一个 ShootingComponent,不要让它继承自 ShooterEnemy。
  • 绝不要硬编码组件依赖项——如果 CombatComponent 需要 HealthComponent,请在 _ready() 中查找,或通过父级注入 [11]。
  • 绝不要把 Godot 节点视为纯数据——节点提供生命周期(_process)和信号。如果你只需要数据,请使用 Resource。
  • 绝不要忽略组件中的节点生命周期——对于无论父级状态如何都必须执行的设置/清理,请使用 _enter_tree() 和 _exit_tree()。
  • 绝不要隐藏组件的访问入口——公开 NodePath 或 Callable 属性,以便父级在检查器中连接组件 [13]。

实现规范

1. 连接策略:类型化导出

不要依赖树的顺序。使用带静态类型的 @export 进行显式依赖注入。

严格 Godot 组合的“Godot 方式”:

# The Orchestrator (e.g., player.gd)
class_name Player extends CharacterBody3D

# Dependency Injection: Define the "slots" in the backpack
@export var health_component: HealthComponent
@export var movement_component: MovementComponent
@export var input_component: InputComponent

# Use Scene Unique Names (%) for auto-assignment in Editor
# or drag-and-drop in the Inspector.
2. 组件思维

组件必须定义 class_name,才能被识别为类型。

标准组件样板代码:

class_name MyComponent extends Node 
# Use Node for logic, Node3D/2D if it needs position

@export var stats: Resource # Components can hold their own data
signal happened_something(value)

func _ready() -> void:
    _validate_dependencies()

func _validate_dependencies() -> void:
    # 2. Dependency-Validation: Fail early during development if setup is wrong [2]
    # NOTE: assert() is stripped in release builds [10].
    assert(stats != null, "Stats Resource missing on %s" % name)

func do_logic(delta: float) -> void:
    # Perform specific task
    pass

标准组件——使用脚本

已移除内联输入/移动/生命值示例。必须:从 composition_root_init.gd 开始,然后加载相应脚本:

类型化 @export 连接仍属于上文的实现规范。

专家级组合模式

1. 状态组件模式(FSM)

将复杂行为封装到充当状态的子节点中。父级 StateComponent 将生命周期调用委托给当前活动的子级 [4, 6]。

必须:阅读 state_component_vsm.gd——不要粘贴内联 StateMachine。要深入了解 VSM/分层 FSM,请打开 godot-state-machine-advanced。

2. 组件注册表(O(1) 查找)

避免通过缓慢的树遍历进行同级节点通信。在就绪时将子级登记到 Dictionary 中(按名称或组)。

var _components: Dictionary = {}

func _ready() -> void:
    for child in get_children():
        _components[child.name] = child
        for group in child.get_groups():
            _components[group] = child

func get_comp(key: StringName) -> Node:
    return _components.get(key)
3. 依赖项验证

使用**@export 断言**快速失败,而不是使用 get_node_or_null 路径(树重新排列时路径会失效)。

@export var health_component: HealthComponent
@export var input_component: InputComponent

func _ready() -> void:
    assert(health_component != null, "Missing HealthComponent export!")
    assert(input_component != null, "Missing InputComponent export!")

输入/移动/生命值协调器示例和注册表深度的必读资料:orchestrator-recipes.md。当 composition_root_init.gd 和一个组件脚本已经足够时,不要加载此资料。

性能说明

节点很轻量。每个实体添加 10-20 个节点不必担心。组合带来的组织优势远远超过 Node 实例微不足道的内存开销。

参考

渐进式披露:仅在研究特定 API 时打开官方文档链接;在切换到相邻领域时加载相关技能——不要预先加载整个技能体系。

官方文档
  • 场景组织 — 标准的信号向上/调用向下所有权模式,让编排器连接组件,而无需让兄弟节点紧密耦合。
  • 节点和场景 — 了解 Godot 为何将节点视为可复用的构建块(组件),并将它们组装成实体场景。
  • Godot 类是什么 — 对于游戏实体,优先使用场景组合和 class_name 组件,而非深层继承树。
  • 何时以及如何避免事事都使用节点 — 将纯数据保存在 Resources 中;仅在需要生命周期、信号和处理帧时使用节点。
  • 逻辑偏好 — 了解游戏逻辑在场景树中的放置方式,让父节点负责编排,子节点各司其职。
  • 数据偏好 — 为组件使用的属性和配置选择 Node、Resource 或普通数据。
  • 使用信号 — 了解过去时态的组件事件(health_depleted、state_changed),父节点可连接这些事件,而无需产生反向依赖。
  • GDScript 导出属性 — 使用类型化的 @export 槽位进行 Inspector 依赖注入,避免脆弱的 $ 路径。
  • 场景唯一节点 — 使用 %Name 查找,即使场景树顺序改变,连接组合根节点时也不会失效。
  • 分组 — 为组件添加标签,以便进行近似 O(1) 的注册表/类接口查找,而无需继承。
  • Godot 通知 — 了解安全的 _ready/进入场景树时机,以便验证并连接组件依赖。
  • 资源 — 将可调参数(最大生命值、速度)作为 Resources 共享,让组件能在不同实体间复用。
相关技能
前置条件
  • godot-project-foundations — 场景所有权、项目布局和 Inspector 连接约定是每个组合根节点的基础。
  • godot-gdscript-mastery — 类型化组件 API 所需的 class_name、类型化 @export、Callable 和断言模式。
  • godot-signal-architecture — 信号向上/调用向下的连接规范,确保组件各自独立,不会直接获取父脚本。
补充
  • godot-resource-data-patterns — 将属性和效果定义为 Resource;组合节点负责运行时变更并发出变更事件。
  • godot-state-machine-advanced — 可作为 StateComponent 接入的子节点 FSM / VSM 模式,避免编排器过于臃肿。
  • godot-input-handling — 感知层 InputComponents 仅采样动作;父节点将方向传递给移动组件。
  • godot-characterbody-2d — 物理体移动 API;VelocityComponents 和组合根通过 move_and_slide 调用。
  • godot-2d-physics — Area2D 层 / 遮罩及重叠规则,HitBox/HurtBox/Interaction 组件都依赖这些规则。
  • godot-scene-management — 将实体作为组合场景生成 / 销毁,并在实例替换时重新连接导出属性。
下游 / 使用方
  • godot-combat-system — 伤害流程在战斗编排器下组装 Health/HitBox/HurtBox 组件。
  • godot-ability-system — 能力以组合式工作组件(冷却、目标选择)的形式附加,而不是为每种施法者都创建子类。
  • godot-rpg-stats — 属性表通过 Resource 和变更信号为 Health/StatusEffect 组件提供数据。
  • godot-monte-carlo-balancer — 在确定实体配置前,模拟可调组件导出属性(HP、伤害、速度)。
  • godot-composition-apps — 将相同的 Has-A 节点组合应用于工具 / 应用,而不是游戏实体。
主索引
  • godot-master — 库路由器和镜像模块入口;查找哪个领域技能负责跨领域架构问题时,请打开此项。

评论

支持 Markdown