Skills/godot-architecture/SKILL.md

220 lines
6.3 KiB
Markdown

---
name: godot-architecture
description: "Game architecture patterns for Godot 4.x: ECS-like composition, scene organization, dependency injection via signals, service locator vs direct reference, and scalable project structure. Use when designing or refactoring a Godot game's overall architecture."
license: MIT
compatibility:
- godot-4.0
- godot-4.1
- godot-4.2
- godot-4.3
- godot-4.4
- godot-4.5
- godot-4.6
- godot-4.7
metadata:
author: vl4dt
version: 0.1.0
tags:
- godot
- architecture
- ecs
- composition
- service-locator
created: 2026-06-21
---
# Godot Game Architecture Patterns
Architectural approaches for building scalable, maintainable Godot games.
## Scene Composition Over Inheritance
Godot's scene system naturally supports composition. Build complex behavior by combining small, focused scenes rather than deep inheritance chains.
### Component-Based Composition
```gdscript
# Player.tscn hierarchy:
# CharacterBody2D (root — movement logic)
# Sprite2D (visual)
# CollisionShape2D (physics body)
# HealthComponent (Node — health logic, emits signals)
# InventoryComponent (Node — item management)
# ExperienceComponent (Node — XP/leveling)
# Each component is a standalone .tscn with its own script.
# The player scene composes them without inheritance coupling.
```
### Component Script Pattern
```gdscript
class_name HealthComponent extends Node
signal health_changed(current: int, maximum: int)
signal died()
@export var max_health: int = 100
var current_health: int = 100:
set(value):
current_health = clampi(value, 0, max_health)
health_changed.emit(current_health, max_health)
if current_health <= 0:
died.emit()
func take_damage(amount: int) -> void:
current_health -= amount
func heal(amount: int) -> void:
current_health += amount
```
## Dependency Management Patterns
### Direct Reference (Simple Cases)
```gdscript
# Good for tightly-coupled parent-child relationships
@onready var health_component = $HealthComponent as HealthComponent
@onready var animation_player = $AnimationPlayer as AnimationPlayer
```
### Signal-Based Decoupling (Recommended)
```gdscript
# Components communicate via signals — no direct references needed
func _ready():
$HealthComponent.died.connect(_on_player_died)
$InventoryComponent.item_collected.connect(_on_item_collected)
func _on_player_died():
get_tree().reload_current_scene()
```
### Service Locator (Autoload Singletons)
```gdscript
# GameManager.gd — added as autoload in Project Settings
extends Node
var current_level: int = 0
var player_score: int = 0
var is_paused: bool = false
signal game_started()
signal game_paused(paused: bool)
signal level_completed(level_id: int, score: int)
# Access from anywhere: GameManager.current_level
```
### When to Use Each Approach
| Pattern | Use When | Avoid When |
|---------|----------|------------|
| Direct reference (`$Path`) | Parent-child, same scene | Cross-scene communication |
| Signals | Decoupled components, event-driven flow | Simple data access |
| Autoload singleton | Global state (score, settings) | Per-instance data |
| Resource-based | Data that needs saving/loading | Runtime-only behavior |
## ECS-Like Patterns in Godot
Godot isn't a true ECS engine, but you can approximate entity-component systems:
### Entity Registry Pattern
```gdscript
class_name EntityRegistry extends Node
# Tracks all active entities by type
var _entities: Dictionary = {}
func register(entity_id: String, entity: Node) -> void:
if not _entities.has(entity_id):
_entities[entity_id] = []
_entities[entity_id].append(entity)
func unregister(entity_id: String, entity: Node) -> void:
if _entities.has(entity_id):
_entities[entity_id].erase(entity)
func get_all(entity_id: String) -> Array:
return _entities.get(entity_id, [])
```
### Data-Driven with Resources
```gdscript
# EntityData.tres — defines entity properties without code
class_name EntityData extends Resource
@export var entity_name: String = ""
@export var health: int = 100
@export var speed: float = 100.0
@export var components: Array[String] = [] # Component scene paths
```
## Project Structure Patterns
### Feature-Based Organization
```
res://
├── core/ # Engine-level systems (autoloads, event bus)
│ ├── GameManager.gd
│ └── EventBus.gd
├── features/ # Game features as self-contained modules
│ ├── combat/
│ │ ├── scripts/
│ │ ├── scenes/
│ │ └── resources/
│ ├── inventory/
│ │ ├── scripts/
│ │ ├── scenes/
│ │ └── resources/
│ └── dialogue/
├── levels/ # Level scenes and data
├── ui/ # UI components and screens
└── assets/ # Art, audio, fonts
```
### Module Pattern for Large Projects
```gdscript
# FeatureModule.gd — reusable feature registration pattern
class_name FeatureModule extends Node
func initialize() -> void:
pass # Override in subclasses
func cleanup() -> void:
pass # Override in subclasses
# Usage: CombatModule.gd extends FeatureModule
# - Registers signals with EventBus
# - Spawns combat-related autoloads if needed
# - Connects to game lifecycle events
```
## Godot 4.7 Architecture Notes
- **tween_await()** — Use for sequential animations without callback chains in component scripts
- **DrawableTexture2D** — Procedural rendering for debug visualization of entity bounds
- **Perfetto tracing** — Profile architecture decisions with CPU/GPU trace markers
## MCP Bridge Tools (Optional — Live Editor Integration)
When the MCP bridge is running (Phase 2), these tools can inspect and modify your project live:
- **`scene_tree`** — Inspect scene hierarchy to audit composition patterns
- **`script_read`** / **`script_edit`** — Read and modify architecture scripts
- **`class_db_info`** — Query available methods on Godot classes for integration points
> **Note:** MCP tools require the Godot editor to be running with the MCP plugin enabled. Skills work independently without the bridge.
## References
- [Composition Patterns](references/composition-patterns.md)
- [Service Locator vs Signals](references/service-locator-vs-signals.md)
- [ECS Approximation Guide](references/ecs-approximation.md)