All skills
thedivergentai avatar

/godot-autoload-architecture

@9d6e91e

Expert patterns for Godot AutoLoad (singleton) architecture including global state management, scene transitions, signal-based communication, dependency injection, autoload initialization order, and anti-patterns to avoid. Use for game managers, save systems, audio controllers, or cross-scene resources. Trigger keywords: AutoLoad, singleton, GameManager, SceneTransitioner, SaveManager, global_state, autoload_order, signal_bus, dependency_injection.

Use this Skill: https://skilld.dev/gh/thedivergentai/gd-agentic-skills/godot-autoload-architecture

This session only. Nothing lands on disk.

referencesexpert-patterns.md

≈1.1k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Expert Patterns

Best Practices

1. Use Static Typing

# ✅ Good
var score: int = 0

# ❌ Bad
var score = 0

2. Emit Signals for State Changes

# ✅ Good - allows decoupled listeners
signal score_changed(new_score: int)

func add_score(points: int) -> void:
    score += points
    score_changed.emit(score)

# ❌ Bad - tight coupling
func add_score(points: int) -> void:
    score += points
    ui.update_score(score)  # Don't directly call UI

3. Organize AutoLoads by Feature

res://autoloads/
    game_manager.gd
    audio_manager.gd
    scene_transitioner.gd
    save_manager.gd

4. Scene Transitioning Pattern

# scene_transitioner.gd
extends Node

signal scene_changed(scene_path: String)

func change_scene(scene_path: String) -> void:
    # Fade out effect (optional)
    await get_tree().create_timer(0.3).timeout
    get_tree().change_scene_to_file(scene_path)
    scene_changed.emit(scene_path)

Testing AutoLoads

Since AutoLoads are always loaded, avoid heavy initialization in _ready(). Use lazy initialization or explicit init functions:

var _initialized: bool = false

func initialize() -> void:
    if _initialized:
        return
    _initialized = true
    # Heavy setup here

Expert Architecture Patterns

1. Service-Locator-Pattern (Dynamic Registration)

Lightweight alternative to hardcoded Autoloads for dependency management.

  • Why: Standard Autoloads must be Node types, which incur memory and SceneTree overhead [4]. For pure data systems, use Engine.register_singleton().
  • The Script: Create a ServiceLocator autoload at the top of the list.
  • Registration: Register lightweight RefCounted objects globally into the engine's scope [5, 6].
# ServiceLocator.gd (Autoload)
func register_service(name: StringName, service: Object) -> void:
    if not Engine.has_singleton(name):
        Engine.register_singleton(name, service)

func _exit_tree() -> void:
    # Cleanup to prevent dangling pointers [6]
    if Engine.has_singleton(&"CombatService"):
        Engine.unregister_singleton(&"CombatService")
  • Consumption: Other systems fetch services via Engine.get_singleton(&"Name"). This bypasses the global variable namespace and allows for O(1) lookups of non-node systems [7].

2. Singleton-Dependency-Diagram (Visual Mapping)

Managing the initialization order and coupling of global systems.

  • The Rule: Autoloads are initialized sequentially in the order they appear in the Project Settings [2]. Singletons at the top of the list MUST NOT depend on those below them.
  • The Template: Use a Mermaid diagram to map out "Who initializes whom".
graph TD
    subgraph SceneTree [SceneTree Execution]
        A[OS & Servers Initialize] --> B
        
        subgraph Autoloads [Project Settings: Autoload Order]
            B[1. GlobalAudio.gd] -->|Initialized First| C[2. ServiceLocator.gd]
            C -->|Initialized Second| D[3. QuestManager.gd]
        end
        
        D --> E[Current Active Scene]
    end

    %% Dependency Coupling
    E -->|Queries| C
    D -->|Registers self into| C
    E -->|Plays sound via| B
  • Verification: If SaveManager (pos 1) calls PlayerManager (pos 5) in _ready(), it will receive a null reference. Always move managers with dependencies to the bottom of the list.

3. Singleton-Health-Check (State Verification)

Automated verification to ensure global states are initialized correctly.

  • The Pattern: Create a specialized test utility that verifies core singletons are non-null and have their default values reset.
  • Validation: Use assert() for debug-time crashes and is_instance_valid() for runtime safety checks [8, 9].
func run_health_checks() -> void:
    # 1. Verify Autoload Node Existence
    var player_vars := get_tree().root.get_node_or_null("PlayerVariables")
    assert(player_vars != null, "Critical Error: PlayerVariables Autoload missing!")
    
    # 2. Verify Dynamic Service Registration
    assert(Engine.has_singleton(&"CombatService"), "Critical Error: CombatService not registered!")
    
    # 3. Verify Memory Safety
    assert(is_instance_valid(player_vars), "Critical Error: PlayerVariables instance invalid!")
  • Integration: Run these checks during game boot (if in debug mode) or within a CI/CD test suite like GUT to prevent state regression.

Source: SKILL.md on GitHub

1 warning17d4 checks · Risk SAFE
  • Gen Agent Trust Hub17d

    This skill provides a set of architectural patterns and GDScript templates for managing global state, singletons (AutoLoads), and scene transitions in the Godot Engine. No security risks or malicious behaviors were detected.

  • Socket17d

    No alerts

  • Snyk17d

    Risk: LOW · No issues

  • Runlayer7mo

    4/4 files flagged

Signed by skilld at 9d6e91e. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 3 weeks ago.

Activeupdated 2 months ago

README badge

README badge for thedivergentai/gd-agentic-skills/godot-autoload-architecture