godot-autoload-architecture
npx skills add https://github.com/thedivergentai/gd-agentic-skills --skill godot-autoload-architecture
Agent 安装分布
Skill 文档
AutoLoad Architecture
AutoLoads are Godot’s singleton pattern, allowing scripts to be globally accessible throughout the project lifecycle. This skill guides implementing robust, maintainable singleton architectures.
NEVER Do
- NEVER access AutoLoads in
_init()â AutoLoads aren’t guaranteed to exist yet during _init(). Use_ready()or_enter_tree()instead. - NEVER create circular dependencies â If GameManager depends on SaveManager and SaveManager depends on GameManager, initialization deadlocks. Use signals or dependency injection.
- NEVER store scene-specific state in AutoLoads â AutoLoads persist across scene changes. Storing temporary state (current enemy count, UI state) causes leaks. Reset in
_ready(). - NEVER use AutoLoads for everything â Over-reliance creates “God objects” and tight coupling. Limit to 5-10 AutoLoads max. Use scene trees for local logic.
- NEVER assume AutoLoad initialization order â AutoLoads initialize top-to-bottom in Project Settings. If order matters, add explicit
initialize()calls or use@onreadycarefully.
Available Scripts
MANDATORY: Read the appropriate script before implementing the corresponding pattern.
service_locator.gd
Service locator pattern for decoupled system access. Allows swapping implementations (e.g., MockAudioManager) without changing game code.
stateless_bus.gd
Stateless signal bus pattern. Domain-specific signals (player_health_changed, level_completed) without storing state. The bus is a post office, not a warehouse.
autoload_initializer.gd
Manages explicit initialization order and dependency injection to avoid circular dependencies.
Do NOT Load service_locator.gd unless implementing dependency injection patterns.
When to Use AutoLoads
Good Use Cases:
- Game Managers: PlayerManager, GameManager, LevelManager
- Global State: Score, inventory, player stats
- Scene Transitions: SceneTransitioner for loading/unloading scenes
- Audio Management: Global music/SFX controllers
- Save/Load Systems: Persistent data management
Avoid AutoLoads For:
- Scene-specific logic (use scene trees instead)
- Temporary state (use signals or direct references)
- Over-architecting simple projects
Implementation Pattern
Step 1: Create the Singleton Script
Example: GameManager.gd
extends Node
# Signals for global events
signal game_started
signal game_paused(is_paused: bool)
signal player_died
# Global state
var score: int = 0
var current_level: int = 1
var is_paused: bool = false
func _ready() -> void:
# Initialize autoload state
print("GameManager initialized")
func start_game() -> void:
score = 0
current_level = 1
game_started.emit()
func pause_game(paused: bool) -> void:
is_paused = paused
get_tree().paused = paused
game_paused.emit(paused)
func add_score(points: int) -> void:
score += points
Step 2: Register as AutoLoad
Project â Project Settings â AutoLoad
- Click the folder icon, select
game_manager.gd - Set Node Name:
GameManager(PascalCase convention) - Enable if needed globally
- Click “Add”
Verify in project.godot:
[autoload]
GameManager="*res://autoloads/game_manager.gd"
The * prefix makes it active immediately on startup.
Step 3: Access from Any Script
extends Node2D
func _ready() -> void:
# Access the singleton
GameManager.connect("game_paused", _on_game_paused)
GameManager.start_game()
func _on_button_pressed() -> void:
GameManager.add_score(100)
func _on_game_paused(is_paused: bool) -> void:
print("Game paused: ", is_paused)
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)
Common Patterns
Game State Machine
enum GameState { MENU, PLAYING, PAUSED, GAME_OVER }
var current_state: GameState = GameState.MENU
func change_state(new_state: GameState) -> void:
current_state = new_state
match current_state:
GameState.MENU:
# Load menu
pass
GameState.PLAYING:
get_tree().paused = false
GameState.PAUSED:
get_tree().paused = true
GameState.GAME_OVER:
# Show game over screen
pass
Resource Preloading
# Preload heavy resources once
const PLAYER_SCENE := preload("res://scenes/player.tscn")
const EXPLOSION_EFFECT := preload("res://effects/explosion.tscn")
func spawn_player(position: Vector2) -> Node2D:
var player := PLAYER_SCENE.instantiate()
player.global_position = position
return player
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
Reference
Related
- Master Skill: godot-master