From godot-prompter
Guides Godot 4.3+ scene tree organization: composition over inheritance, when to split scenes, node hierarchies for players, enemies, components.
npx claudepluginhub jame581/godotprompter --plugin godot-prompterThis skill uses the workspace's default tool permissions.
A guide for structuring Godot 4.3+ scene trees: when to split, when to compose, and how nodes should communicate.
Brainstorms Godot 4.3+ features/systems: clarifies requirements, proposes architectures with trade-offs, designs scene trees/nodes/signals/data flow before implementation.
Constructs Godot scenes from patterns like platformer characters, top-down characters, UI screens, projectiles, pickups, and tilemaps, enforcing required companion nodes such as CollisionShape2D.
Structures scenes, nodes, autoloads, and resources in Godot projects for scalability and clarity. Use when scene nesting grows, autoloads become dumping grounds, or node ownership is unclear.
Share bugs, ideas, or general feedback.
A guide for structuring Godot 4.3+ scene trees: when to split, when to compose, and how nodes should communicate.
Related skills: component-system for composition patterns, event-bus for decoupled communication, godot-brainstorming for scene tree planning, 2d-essentials for TileMapLayer and CanvasLayer organization.
Scenes are building blocks. Each scene encapsulates exactly one concept — a player, an enemy, a health bar, a weapon. A scene should be understandable in isolation, reusable without modification, and replaceable without breaking its neighbors.
One scene = one responsibility. If you struggle to name a scene in two words or fewer, it is probably doing too much.
Player (CharacterBody2D)
├── Sprite2D
├── CollisionShape2D
├── HealthComponent
├── HitboxComponent
├── StateMachine
└── AnimationPlayer
HealthComponent, HitboxComponent, and StateMachine are separate .tscn files instantiated as child scenes. Any entity that needs health — enemy, destructible crate, boss — can include HealthComponent without duplicating logic.
GDScript
# health_component.gd
class_name HealthComponent
extends Node
signal health_changed(old_value: int, new_value: int)
signal died
@export var max_health: int = 100
var current_health: int
func _ready() -> void:
current_health = max_health
func take_damage(amount: int) -> void:
if amount <= 0:
return
var old_health := current_health
current_health = max(0, current_health - amount)
health_changed.emit(old_health, current_health)
if current_health == 0:
died.emit()
func heal(amount: int) -> void:
if amount <= 0:
return
var old_health := current_health
current_health = min(max_health, current_health + amount)
health_changed.emit(old_health, current_health)
func is_alive() -> bool:
return current_health > 0
C#
// HealthComponent.cs
using Godot;
[GlobalClass]
public partial class HealthComponent : Node
{
[Signal]
public delegate void HealthChangedEventHandler(int oldValue, int newValue);
[Signal]
public delegate void DiedEventHandler();
[Export]
public int MaxHealth { get; set; } = 100;
public int CurrentHealth { get; private set; }
public override void _Ready()
{
CurrentHealth = MaxHealth;
}
public void TakeDamage(int amount)
{
if (amount <= 0)
return;
int oldHealth = CurrentHealth;
CurrentHealth = Mathf.Max(0, CurrentHealth - amount);
EmitSignal(SignalName.HealthChanged, oldHealth, CurrentHealth);
if (CurrentHealth == 0)
EmitSignal(SignalName.Died);
}
public void Heal(int amount)
{
if (amount <= 0)
return;
int oldHealth = CurrentHealth;
CurrentHealth = Mathf.Min(MaxHealth, CurrentHealth + amount);
EmitSignal(SignalName.HealthChanged, oldHealth, CurrentHealth);
}
public bool IsAlive() => CurrentHealth > 0;
}
Inheritance suits cases where scenes share structure, not just behavior — when child scenes are variations of the same thing with identical node layout and only a few exported properties differ.
Good candidates:
Enemy → Orc, Goblin — same bones (Sprite2D, CollisionShape2D, HealthComponent, AI), different stats and artWeapon → Sword, Bow — same slot attachment logic, different animations and damage typePickup → HealthPickup, AmmoPickup — same Area2D + CollisionShape2D + animation, different effect on collection| Scenario | Pattern |
|---|---|
| You would copy-paste the entire scene and change a few exported properties | Inheritance |
| You want to mix and match a subset of nodes across different entity types | Composition |
.tscn file [Parent]
/ \
[Child A] [Child B]
\
[Child C]
A child node announces that something happened. The parent — or any node that has connected to the signal — decides what to do about it. This keeps children ignorant of their context and fully reusable.
# Child emits; it does not know who is listening
health_component.died.connect(_on_player_died)
A parent drives its children by calling their methods directly. The parent owns the reference; the child exposes a clean API and does not need to know about its parent.
# Parent calls into child
$HealthComponent.take_damage(10)
$AnimationPlayer.play("hurt")
For communication between scenes that have no ancestor–descendant relationship — e.g., an enemy notifying the HUD — use an Autoload event bus. Emitting on the bus decouples sender from receiver entirely.
# Autoload: EventBus.gd
signal enemy_killed(enemy: Enemy)
# Enemy scene
EventBus.enemy_killed.emit(self)
# HUD scene
EventBus.enemy_killed.connect(_on_enemy_killed)
Enemy (CharacterBody2D)
├── Visuals
│ ├── Sprite2D
│ └── AnimationPlayer
├── Collision
│ └── CollisionShape2D
├── Components
│ ├── HealthComponent
│ └── HitboxComponent
└── AI
├── NavigationAgent2D
└── StateMachine
Group by concern using plain Node containers (Visuals, Collision, Components, AI). Each sub-group can be collapsed in the editor and worked on independently.
HUD (CanvasLayer)
├── MarginContainer
│ ├── TopBar
│ │ ├── HealthBar
│ │ └── ResourceBar
│ └── BottomBar
│ ├── Hotbar
│ └── MiniMap
└── PauseMenu
CanvasLayer ensures HUD elements are always rendered on top. MarginContainer handles safe-area padding. TopBar, BottomBar, and PauseMenu are separate instantiated scenes so each can be edited without opening the root HUD scene.
Level01 (Node2D)
├── TileMapLayer
├── Entities
│ ├── Player (instance)
│ └── Enemies (Node2D)
│ ├── Orc (instance)
│ └── Goblin (instance)
├── Pickups (Node2D)
├── Navigation
│ └── NavigationRegion2D
└── Camera2D
The level scene is a composition root — it owns the layout and spawns instances, but contains no gameplay logic itself. Entities, Pickups, and Navigation are plain Node2D containers used for organizational grouping and to simplify get_children() iteration.
HealthComponent, StateMachine, etc.) are separate .tscn filesget_parent() chainsget_parent().get_parent() or get_node("../../SomeNode") paths in codeVisuals, Components, AI, etc.) for readability