
Godot Best Practices
Install this skill when you want thin Godot 4 autoload singleton templates with signals, lifecycle hooks, and typed GDScript patterns for global managers like audio.
Overview
godot-best-practices is an agent skill for the Build phase that provides Godot 4 autoload singleton templates and thin global-manager patterns (e.g. audio) in typed GDScript.
Install
npx skills add https://github.com/jwynia/agent-skills --skill godot-best-practicesWhat is this skill?
- Autoload manager template for Godot 4.x with class_name, signals, and _ready lifecycle
- Guidance to keep autoloads thin—global services only, not game logic
- Concrete AudioManager example with music/SFX buses, fade constant, and sfx player pool
- Placeholder-driven snippets (${ManagerName}, ${description}) for fast codegen
- Public API section pattern with typed params and ReturnType placeholders
Adoption & trust: 1.8k installs on skills.sh; 92 GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
Your Godot project needs consistent global managers but ad-hoc autoload scripts grow into untestable god objects.
Who is it for?
Solo indie devs on Godot 4 who want agent-generated autoloads that follow service-only singleton discipline.
Skip if: Non-Godot stacks, server-only backends, or teams that already enforce a custom ECS/framework with no autoloads.
When should I use this skill?
You are adding or refactoring Godot autoload global managers and want thin service templates with signals and typed APIs.
What do I get? / Deliverables
You get a registered autoload-ready manager skeleton with signals, lifecycle hooks, and a clear public API to fill in for audio or other cross-scene services.
- Autoload manager GDScript skeleton
- Documented public API methods for a global service
Recommended Skills
Journey fit
How it compares
Structured Godot autoload template skill, not a generic “write game code” prompt or an engine-agnostic game framework.
Common Questions / FAQ
Who is godot-best-practices for?
Godot 4 builders—often solo—who use coding agents to scaffold global managers and want thin autoload patterns instead of bloated singletons.
When should I use godot-best-practices?
During Build while setting up project autoloads, audio buses, and cross-scene services before you polish scenes for Ship.
Is godot-best-practices safe to install?
Check the Security Audits panel on this page; the skill is documentation and templates only, but generated GDScript still runs in your game with full project access.
SKILL.md
READMESKILL.md - Godot Best Practices
# Autoload Manager Template Template for global singleton managers in Godot 4.x. ## Usage Register as autoload in Project Settings > Autoload. Keep autoloads thin - services only, not game logic. ## Basic Manager Template ```gdscript class_name ${ManagerName}Manager extends Node ## Global ${description} manager. ## ## Access via ${ManagerName}Manager singleton. # === Signals === signal ${event_occurred}(${data}: ${Type}) # === Private Variables === var _${internal_state}: ${Type} # === Lifecycle === func _ready() -> void: # Initialize manager state pass # === Public API === ## ${Description of this method} func ${public_method}(${param}: ${Type}) -> ${ReturnType}: # Implementation pass ``` ## Audio Manager Example ```gdscript class_name AudioManager extends Node ## Global audio playback manager. ## ## Handles music and sound effects with volume control. # === Signals === signal music_changed(track_name: String) # === Constants === const MUSIC_FADE_DURATION: float = 1.0 # === Exports === @export var music_bus: StringName = &"Music" @export var sfx_bus: StringName = &"SFX" # === Private Variables === var _music_player: AudioStreamPlayer var _sfx_players: Array[AudioStreamPlayer] = [] var _current_music: String = "" # === Lifecycle === func _ready() -> void: _setup_music_player() _setup_sfx_pool() func _setup_music_player() -> void: _music_player = AudioStreamPlayer.new() _music_player.bus = music_bus add_child(_music_player) func _setup_sfx_pool() -> void: for i in 8: # Pool of 8 SFX players var player := AudioStreamPlayer.new() player.bus = sfx_bus add_child(player) _sfx_players.append(player) # === Music === func play_music(stream: AudioStream, fade_in: bool = true) -> void: if _music_player.stream == stream and _music_player.playing: return _current_music = stream.resource_path if fade_in and _music_player.playing: await _fade_out_music() _music_player.stream = stream _music_player.play() if fade_in: await _fade_in_music() music_changed.emit(_current_music) func stop_music(fade_out: bool = true) -> void: if fade_out: await _fade_out_music() _music_player.stop() _current_music = "" func _fade_out_music() -> void: var tween := create_tween() tween.tween_property(_music_player, "volume_db", -40.0, MUSIC_FADE_DURATION) await tween.finished func _fade_in_music() -> void: _music_player.volume_db = -40.0 var tween := create_tween() tween.tween_property(_music_player, "volume_db", 0.0, MUSIC_FADE_DURATION) await tween.finished # === Sound Effects === func play_sfx(stream: AudioStream, volume_db: float = 0.0) -> void: var player := _get_available_sfx_player() if player: player.stream = stream player.volume_db = volume_db player.play() func _get_available_sfx_player() -> AudioStreamPlayer: for player in _sfx_players: if not player.playing: return player return _sfx_players[0] # Fallback to first # === Volume Control === func set_music_volume(linear: float) -> void: AudioServer.set_bus_volume_db( AudioServer.get_bus_index(music_bus), linear_to_db(linear) ) func set_sfx_volume(linear: float) -> void: AudioServer.set_bus_volume_db( AudioServer.get_bus_index(sfx_bus), linear_to_db(linear) ) func get_music_volume() -> float: return db_to_linear(AudioServer.get_bus_volume_db( AudioServer.get_bus_index(music_bus) )) func get_sfx_volume() -> float: return db_to_linear(AudioServer.get_bus_volume_db( AudioServer.get_bus_index(sfx_bus) )) ``` ## Event Bus Example ```gdscript class_name EventBus extends Node ## Global event bus for decoupled communication. ## ## Use for game-wide events that multiple systems care about. # === Game State Events === signal game_started si