220 lines
6.3 KiB
Markdown
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)
|