Skip to content
Development
Skill

/scene-organization

Use when designing scene tree structure — composition vs inheritance, when to split scenes, node hierarchy patterns

From plugin
godot-prompter
96061 skills9 agents1 hook

Claude Code

$ npx -y skills add jame581/GodotPrompter --skill scene-organization --agent claude-code

SKILL.md

scene-organization.SKILL.md
name: scene-organization
description: Use when designing scene tree structure — composition vs inheritance, when to split scenes, node hierarchy patterns

Scene Organization

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.

---

1. Core Principle

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.

---

2. Composition Over Inheritance

Player Scene — Composed from Reusable Parts

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.

HealthComponent — Full Example

**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;
}

When to Use Inheritance Instead

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 art
  • `Weapon` → `Sword`, `Bow` — same slot attachment logic, different animations and damage type
  • `Pickup` → `HealthPickup`, `AmmoPickup` — same Area2D + CollisionShape2D + animation, different effect on collection

Rule of Thumb

| 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** |

---

3. Scene Splitting Rules

Split a scene when:

  • **Reuse** — the sub-scene is needed in more than one parent scene
  • **Complexity** — the scene exceeds roughly 15 nodes; it is carrying more than one concern
  • **Independence** — the sub-scene can be tested, previewed, or modified without opening its parent
  • **Team** — separate scenes reduce merge conflicts when multiple people work on the same feature

Keep nodes together when:

  • Nodes are **tightly coupled** — splitting them would require excessive signal wiring just to replicate what a direct node reference handles cleanly
  • The grouping is **small and used only once** — a two-node helper that exists in a single scene does not warrant its own `.tscn` file
  • Splitting would create **simple-operation overhead** — if a parent must wire three signals just to tell a child "you were hit", the split is not paying for itself

---

4. Node Communication Patterns

        [Parent]
        /      \
  [Child A]  [Child B]
       \
     [Child C]

Signals travel up (child → parent)

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)

Method calls travel down (parent → child)

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")

EventBus travels sideways (peer → peer)

For communication between scenes that

Read more
@jame581
Ships withgodot-prompter

Agentic skills framework for Godot 4.x game development. Gives AI coding agents domain-specific expertise for GDScript and C# projects.

96061 skills9 agents
Get the whole plugin

Stars on GitHub

Momentum

stars in total
960
Star history of jame581/GodotPrompter on GitHub, a chart by star-history.com

Other skills on godot-prompter.

2d-essentials

2d-essentials

Use when working with 2D-specific systems — TileMaps, parallax scrolling, 2D lights and shadows, canvas layers, particles 2D,…

3d-essentials

3d-essentials

Use when working with 3D-specific systems — materials, lighting, shadows, environment, global illumination, fog, LOD, occlusion…

ability-system

ability-system

Use when building or replacing an ability system — abilities with cost/cooldown, buffs/debuffs, stat modifiers, and gameplay…

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills13d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc2w293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react4d252k
@facebook@facebookInstall Skill

Questions about scene-organization.

Which coding agents can use scene-organization?
scene-organization installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.