godot-composition

安装量: 76
排名: #10330

安装

npx skills add https://github.com/thedivergentai/gd-agentic-skills --skill godot-composition
Godot Composition Architecture
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). Anti-Patterns (NEVER Do This) NEVER use deep inheritance chains (e.g., Player > Entity > LivingThing > Node ). This creates brittle "God Classes." NEVER use get_node("Path/To/Thing") or $ syntax for components. This breaks if the scene tree changes. NEVER let components reference the Parent directly (unless absolutely necessary via typed injection). NEVER mix Input, Physics, and Game Logic in a single script. 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.

  1. 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 do_logic ( delta : float ) -> void :

Perform specific task

pass
Standard Components
The Input Component (The Senses)
Responsibility
Read hardware state. Store it. Do NOT act on it. State : move_dir , jump_pressed , attack_just_pressed . class_name InputComponent extends Node var move_dir : Vector2 var jump_pressed : bool func update ( ) -> void :

Called by Orchestrator every frame

move_dir

Input
.
get_vector
(
"left"
,
"right"
,
"up"
,
"down"
)
jump_pressed
=
Input
.
is_action_just_pressed
(
"jump"
)
The Movement Component (The Legs)
Responsibility
Manipulate physics body. Handle velocity/gravity.
Constraint
Requires a reference to the physics body it moves. class_name MovementComponent extends Node @ export var body : CharacterBody3D

The thing we move

@ export var speed : float = 8.0 @ export var jump_velocity : float = 12.0 func tick ( delta : float , direction : Vector2 , wants_jump : bool ) -> void : if not body : return

Handle Gravity

if not body . is_on_floor ( ) : body . velocity . y -= 9.8 * delta

Handle Movement

if direction : body . velocity . x = direction . x * speed body . velocity . z = direction . y * speed

3D conversion

else : body . velocity . x = move_toward ( body . velocity . x , 0 , speed ) body . velocity . z = move_toward ( body . velocity . z , 0 , speed )

Handle Jump

if
wants_jump
and
body
.
is_on_floor
(
)
:
body
.
velocity
.
y
=
jump_velocity
body
.
move_and_slide
(
)
The Health Component (The Life)
Responsibility
Manage HP, Clamp values, Signal changes.
Context Agnostic
Can be put on a Player, Enemy, or a Wooden Crate. class_name HealthComponent extends Node signal died signal health_changed ( current , max ) @ export var max_health : float = 100.0 var current_health : float func _ready ( ) : current_health = max_health func damage ( amount : float ) : current_health = clamp ( current_health - amount , 0 , max_health ) health_changed . emit ( current_health , max_health ) if current_health == 0 : died . emit ( ) The Orchestrator (Putting it Together) The Orchestrator ( player.gd ) binds the components in the _physics_process . It acts as the bridge. class_name Player extends CharacterBody3D @ onready var input : InputComponent = % InputComponent @ onready var move : MovementComponent = % MovementComponent @ onready var health : HealthComponent = % HealthComponent func _ready ( ) :

Connect signals (The ears)

health . died . connect ( _on_death ) func _physics_process ( delta ) :

1. Update Senses

input . update ( )

2. Pass Data to Workers (State Management)

The Player script decides that "Input Direction" maps to "Movement Direction"

move . tick ( delta , input . move_dir , input . jump_pressed ) func _on_death ( ) : queue_free ( ) 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 Master Skill: godot-master

返回排行榜