godot-composition
Expert architectural standards for building scalable Godot GAMES (RPGs, Platformers, Shooters) using the Composition pattern (Entity-Component). Use…
它会碰到什么
这一栏是扫描器报的事实,不是结论。命中多不等于有毒(安全工具、规则库、示例脚本本来就会包含危险写法),命中少也不等于干净。它和你手上的凭据、文件、网络有什么关系,需要你自己看。
技能内容
Core Philosophy
This skill enforces Composition over Inheritance ("Has-a" vs "Is-a").
In Godot, Nodes are components. A complex entity (Player) is simply an Orchestrator managing specialized Worker Nodes (Components).
The Golden Rules
- Single Responsibility: One script = One job.
- Encapsulation: Components are "selfish." They handle their internal logic but don't know who owns them.
- The Orchestrator: The root script (e.g.,
player.gd) does no logic. It only manages state and passes data between components. - Decoupling: Components communicate via Signals (up) and Methods (down).
Decision Tree — Composition vs Autoload vs Inheritance
| Situation | Choose |
|-----------|--------|
| Gameplay entity behaviors (HP, hitbox, move, interact) | Composition — child components + orchestrator ([composition_root_init.gd](scripts/composition_root_init.gd)) |
| Cross-scene services (audio bus, save, net, economy ledger) | Autoload — not a component on the player |
| True is-a engine specialization (custom Control/Node with shared lifecycle) | Inheritance exception — rare; never for "adds a gun" / "adds HP" |
Available Scripts
[health_component.gd](scripts/health_component.gd)
Specialized Node for managing lifespan, damage logic, and death signals across any entity.
[hit_box_component.gd](scripts/hit_box_component.gd)
Area-based component for intercepting damage and delegating it to a HealthComponent.
[hurt_box_component.gd](scripts/hurt_box_component.gd)
Area-based component for dealing damage specifically to HitBoxComponents.
[velocity_component.gd](scripts/velocity_component.gd)
Encapsulated movement and acceleration logic for reuse across Players and Enemies.
[interaction_component.gd](scripts/interaction_component.gd)
Decoupled interaction handler using injecting Callable logic for context-aware actions.
[follower_component.gd](scripts/follower_component.gd)
Decoupled tracking logic using NodePath injection for smooth entity following.
[state_component_vsm.gd](scripts/state_component_vsm.gd)
Component-based state machine pattern using child nodes as individual states.
[status_effect_component.gd](scripts/status_effect_component.gd)
Managing temporary modifiers (buffs/debuffs) by stacking effect scenes as children.
[visual_sync_component.gd](scripts/visual_sync_component.gd)
Separating logical state (velocity/direction) from visual representation (sprite flipping).
[composition_root_init.gd](scripts/composition_root_init.gd)
MANDATORY first read — Orchestrator wiring via typed @export (Inspector / %UniqueNames in the scene). Matches NEVER: no $ / get_node for components.
NEVER Do in Composition
- NEVER use deep inheritance chains (e.g.,
Player > Entity > LivingThing > Node) — Creates brittle "God Classes" that are hard to refactor [21]. - NEVER use
get_node()or$for components — This breaks if the scene tree is rearranged. Always use@exportor%UniqueNames[22]. - NEVER let a component reference its parent script directly — This makes the component impossible to reuse. Use signals or dependency injection [23].
- NEVER mix Input, Physics, and Game Logic in one script — This violates Single Responsibility. Split them into specialized components [24, 13].
- NEVER create components that require a specific SceneTree structure — A component should be "selfish" and only care about its own properties and direct children.
- NEVER use inheritance to "add a feature" — If you want an enemy to shoot, add a
ShootingComponent, don't make it inherit fromShooterEnemy. - NEVER hardcode component dependencies — If
CombatComponentneedsHealthComponent, look it up in_ready()or inject it via the parent [11]. - NEVER treat Godot nodes as pure data — Nodes provide lifecycle (
_process) and signals. If you only need data, use aResource. - NEVER ignore the Node lifecycle in components — Use
_enter_tree()and_exit_tree()for setup/cleanup that must happen regardless of the parent's state. - NEVER hide component points of access — Expose
NodePathorCallableproperties so the parent can wire the component in the Inspector [13].
Implementation Standards
1. Connection Strategy: Typed Exports
Do not rely on tree order. Use explicit dependency injection via @export with static typing.
The "Godot Way" for strict godot-composition:
# 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. Component Mindset
Components must define class_name to be recognized as types.
Standard Component Boilerplate:
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
Standard Components — Use Scripts
> Inline Input/Movement/Health recipes removed. MANDATORY: start from [composition_root_init.gd](scripts/composition_root_init.gd), then load the matching script:
- Health / death: [health_component.gd](scripts/health_component.gd)
- Damage areas: [hit_box_component.gd](scripts/hit_box_component.gd), [hurt_box_component.gd](scripts/hurt_box_component.gd)
- Motion: [velocity_component.gd](scripts/velocity_component.gd)
- Interact / follow / VFX sync: [interaction_component.gd](scripts/interaction_component.gd), [follower_component.gd](scripts/follower_component.gd), [visual_sync_component.gd](scripts/visual_sync_component.gd)
- States / statuses: [state_component_vsm.gd](scripts/state_component_vsm.gd), [status_effect_component.gd](scripts/status_effect_component.gd)
Typed @export wiring stays under Implementation Standards above.
Expert Composition Patterns
1. State-Component Pattern (FSM)
Encapsulate complex behaviors into child nodes that act as states. The parent StateComponent delegates lifecycle calls to the active child [4, 6].
> MANDATORY: Read [state_component_vsm.gd](scripts/state_component_vsm.gd) — do not paste an inline StateMachine. For deeper VSM / hierarchical FSMs, open godot-state-machine-advanced.
2. Component-Registry (O(1) Lookup)
Avoid slow tree traversal for sibling communication. Catalog children in a Dictionary at ready (by name or group).
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. Dependency-Validation
Fail fast with @export asserts, not get_node_or_null paths (paths break when the tree is rearranged).
@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!")
> MANDATORY for Input/Movement/Health orchestrator recipes and registry depth: [orchestrator-recipes.md](references/orchestrator-recipes.md). Do NOT Load when [composition_root_init.gd](scripts/composition_root_init.gd) + one component script suffice.
Performance Note
Nodes are lightweight. Do not fear adding 10-20 nodes per entity. The organizational benefit of Composition vastly outweighs the negligible memory cost of Node instances.
Reference
> Progressive disclosure: open Official Documentation links only when researching a specific API; load Related Skills when routing to a peer domain — do not preload the whole lattice.
Official Documentation
- Scene organization — Canonical signal-up / call-down ownership so orchestrators wire components without sibling hard-coupling.
- Nodes and Scenes — Why Godot treats nodes as reusable building blocks (components) assembled into entity scenes.
- What are Godot classes — Prefer scene composition and
class_namecomponents over deep inheritance trees for gameplay entities. - When and how to avoid using nodes for everything — Keep pure data in Resources; reserve Nodes for lifecycle, signals, and process ticks.
- Logic preferences — Placement of game logic across scene trees so parents orchestrate and children stay single-purpose.
- Data preferences — Choose Node vs Resource vs plain data for stats and config that components consume.
- Using signals — Past-tense component events (
health_depleted,state_changed) parents connect without reverse dependencies. - GDScript exported properties — Typed
@exportslots for Inspector dependency injection instead of brittle$paths. - Scene Unique Nodes —
%Namelookups that survive scene-tree reorders when wiring composition roots. - Groups — Tag components for O(1)-style registry / interface-like lookup without inheritance.
- Godot notifications — Safe
_ready/ enter-tree timing for validating and connecting component dependencies. - Resources — Share tunables (max health, speeds) as Resources so components stay reusable across entities.
Related Skills
Prerequisites
- godot-project-foundations — Scene ownership, project layout, and Inspector wiring conventions every composition root assumes.
- godot-gdscript-mastery —
class_name, typed@export, Callables, and assert patterns required for typed component APIs. - godot-signal-architecture — Signal-up / call-down connect hygiene so selfish components never grab parent scripts.
Complements
- godot-resource-data-patterns — Stats and effect definitions as Resources; composition nodes own runtime mutation and emit change events.
- godot-state-machine-advanced — Child-node FSM / VSM patterns that plug in as a StateComponent without bloating the orchestrator.
- godot-input-handling — Sense-layer InputComponents that only sample actions; parents pass directions into movement components.
- godot-characterbody-2d — Physics-body movement APIs VelocityComponents and composition roots call via
move_and_slide. - godot-2d-physics — Area2D layers/masks and overlap rules HitBox/HurtBox/Interaction components depend on.
- godot-scene-management — Spawn/despawn entities as composed scenes and re-wire exports when instances are swapped.
Downstream / consumers
- godot-combat-system — Damage pipelines assemble Health/HitBox/HurtBox components under combat orchestrators.
- godot-ability-system — Abilities attach as composed workers (cooldowns, targeting) rather than subclassing every caster.
- godot-rpg-stats — Stat sheets feed Health/StatusEffect components as Resources plus change signals.
- godot-monte-carlo-balancer — Simulate tunable component exports (HP, damage, speeds) before locking entity kits.
- godot-composition-apps — Same Has-A node composition applied to tools/apps rather than gameplay entities.
Master
- godot-master — Library router and mirrored module entry; open when discovering which Domain Skill owns a cross-cutting architecture concern.
想直接用这个技能?
本站把开放许可(MIT / Apache 等)的技能按仓库打包整理到网盘,点一下转存到你自己的网盘,不用一个个从 GitHub 拉。许可未声明的技能只给原始仓库链接,不打包。