关于 godot-composition
这是一项 Godot 技能,指导使用相互协作的 Node 组合游戏实体,而非构建深层继承树。面向开发可复用实体行为的人员,强调类型化依赖注入、信号通信和专用组件脚本。
主要功能
- 01单一职责的 Node 组件
- 02通过编排器连接实体组件
- 03类型化 @export 依赖注入
- 04信号向上通知、方法向下调用
- 05可复用的生命值、移动和状态组件
适用场景
- 01将生命值和伤害行为从实体脚本中拆分
- 02通过组件组合玩家和敌人的行为
- 03避免组件依赖父级脚本进行连接
- 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.mdorchestrator-recipes.mdresources/health_component.gdhitbox_component.gdscripts/composition_root_init.gdfollower_component.gdhealth_component.gdhit_box_component.gdhurt_box_component.gdinteraction_component.gdstate_component_vsm.gdstatus_effect_component.gdvelocity_component.gdvisual_sync_component.gd详细介绍
核心理念
此技能坚持组合优于继承(“拥有”而非“属于”)。 在 Godot 中,节点就是组件。复杂实体(Player)只是一个协调器,用于管理专门的工作节点(组件)。
黄金法则
- 单一职责:一个脚本 = 一项工作。
- 封装:组件“各司其职”。它们处理自己的内部逻辑,但不知道谁拥有它们。
- 协调器:根脚本(例如
player.gd)不包含任何逻辑。它只管理状态并在组件之间传递数据。 - 解耦:组件通过 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 开始,然后加载相应脚本:
- 生命值/死亡:health_component.gd
- 伤害区域:hit_box_component.gd、hurt_box_component.gd
- 移动:velocity_component.gd
- 交互/跟随/VFX 同步:interaction_component.gd、follower_component.gd、visual_sync_component.gd
- 状态/状态效果:state_component_vsm.gd、status_effect_component.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 — 库路由器和镜像模块入口;查找哪个领域技能负责跨领域架构问题时,请打开此项。