godot-audio-systems

安装量: 81
排名: #9752

安装

npx skills add https://github.com/thedivergentai/gd-agentic-skills --skill godot-audio-systems
Audio Systems
Expert guidance for Godot's audio engine and mixing architecture.
NEVER Do
NEVER create new AudioStreamPlayer nodes for every sound
— Causes memory bloat and GC spikes. Use audio pooling (reuse players) or one-shot helper function.
NEVER set AudioServer bus volume with linear values
set_bus_volume_db()
expects decibels (-80 to 0). Use
linear_to_db()
for 0.0-1.0 conversion.
NEVER forget to set
autoplay = false
on music players
— Music autoplays on scene load by default. Causes overlapping tracks when changing scenes.
NEVER use AudioStreamPlayer3D without attenuation model
— Default attenuation is NONE (no falloff). Set
attenuation_model
to ATTENUATION_INVERSE_DISTANCE or audio is global.
NEVER play AudioStreamPlayer without checking
playing
first
— Restarting an already-playing sound cuts it off. Check
if not player.playing:
before play().
Available Scripts
MANDATORY
Read the appropriate script before implementing the corresponding pattern. audio_manager.gd AudioManager singleton with sound pooling (32-player pool), bus assignment, and crossfade preparation. Prevents node spam and GC spikes. audio_visualizer.gd Real-time FFT spectrum analysis. Captures low/mid/high frequency ranges to drive visual effects like lighting pulses or shader parameters. AudioStreamPlayer Variants AudioStreamPlayer (Global/UI)

No spatial positioning, same volume everywhere

Use for: Music, UI sounds, voiceovers

@ onready var music := AudioStreamPlayer . new ( ) func _ready ( ) -> void : music . stream = load ( "res://audio/music_main.ogg" ) music . volume_db = - 10

Quieter

music . autoplay = false music . bus = "Music"

Route to Music bus

add_child ( music ) music . play ( ) AudioStreamPlayer2D (Positional)

2D panning based on distance from camera

Use for: 2D games, top-down audio cues

extends Area2D @ onready var footstep := AudioStreamPlayer2D . new ( ) func _ready ( ) -> void : footstep . stream = load ( "res://audio/footstep.ogg" ) footstep . max_distance = 500

Audible range (pixels)

footstep . attenuation = 2.0

Falloff curve (higher = faster fadeout)

add_child ( footstep ) func play_footstep ( ) -> void : if not footstep . playing : footstep . play ( ) AudioStreamPlayer3D (Spatial)

3D spatial audio with doppler, reverb send

Use for: 3D games, realistic sound positioning

extends Node3D @ onready var explosion := AudioStreamPlayer3D . new ( ) func _ready ( ) -> void : explosion . stream = load ( "res://audio/explosion.ogg" ) explosion . unit_size = 10.0

Size of sound source

explosion . max_distance = 100.0

Range

explosion . attenuation_model = AudioStreamPlayer3D . ATTENUATION_INVERSE_DISTANCE explosion . doppler_tracking = AudioStreamPlayer3D . DOPPLER_TRACKING_PHYSICS_STEP add_child ( explosion ) explosion . play ( ) AudioBus Architecture Bus Setup (Project Settings) Master (always exists) ├─ Music │ └─ Effects: Compressor, EQ ├─ SFX │ └─ Effects: Reverb (for environment) └─ Ambient └─ Effects: LowPassFilter (muffled ambience) Volume Control (Decibels)

❌ BAD: Linear volume (doesn't work)

AudioServer . set_bus_volume_db ( music_bus_idx , 0.5 )

WRONG!

✅ GOOD: Use decibels

var music_bus := AudioServer . get_bus_index ( "Music" ) AudioServer . set_bus_volume_db ( music_bus , - 10 )

-10 dB (quieter)

Convert linear (0.0-1.0) to dB:

var linear_volume := 0.5

50%

var db := linear_to_db ( linear_volume )

~-6 dB

AudioServer . set_bus_volume_db ( music_bus , db )

Convert dB to linear:

var current_db := AudioServer . get_bus_volume_db ( music_bus ) var linear := db_to_linear ( current_db ) print ( "Current volume: %d%%" % int ( linear * 100 ) ) Mute Bus func toggle_mute ( bus_name : String ) -> void : var bus_idx := AudioServer . get_bus_index ( bus_name ) var is_muted := AudioServer . is_bus_mute ( bus_idx ) AudioServer . set_bus_mute ( bus_idx , not is_muted ) Audio Pooling (Performance) Problem: Creating Players Every Frame

❌ BAD: Creates 60 new nodes/second at 60 FPS

func play_footstep ( ) -> void : var player := AudioStreamPlayer . new ( ) add_child ( player ) player . stream = load ( "res://audio/footstep.ogg" ) player . finished . connect ( player . queue_free ) player . play ( )

Result: 3600 nodes created in 1 minute!

Solution: Audio Pool

audio_pool.gd (AutoLoad)

extends Node const POOL_SIZE = 10 var pool : Array [ AudioStreamPlayer ] = [ ] var pool_index := 0 func _ready ( ) -> void :

Pre-create players

for i in range ( POOL_SIZE ) : var player := AudioStreamPlayer . new ( ) player . bus = "SFX" add_child ( player ) pool . append ( player ) func play_sound ( stream : AudioStream , volume_db := 0.0 ) -> void : var player := pool [ pool_index ] pool_index = ( pool_index + 1 ) % POOL_SIZE

Round-robin

Stop previous sound if still playing

if player . playing : player . stop ( ) player . stream = stream player . volume_db = volume_db player . play ( )

Usage:

AudioPool . play_sound ( load ( "res://audio/coin.ogg" ) , - 5.0 ) Music Transitions Crossfade Between Tracks

music_manager.gd (AutoLoad)

extends Node @ onready var track_a := AudioStreamPlayer . new ( ) @ onready var track_b := AudioStreamPlayer . new ( ) var current_track : AudioStreamPlayer var fade_duration := 2.0 func _ready ( ) -> void : track_a . bus = "Music" track_b . bus = "Music" add_child ( track_a ) add_child ( track_b ) current_track = track_a func crossfade_to ( new_stream : AudioStream ) -> void : var next_track := track_b if current_track == track_a else track_a

Start new track at 0 dB

next_track . stream = new_stream next_track . volume_db = - 80

Silent

next_track . play ( )

Fade out current, fade in next

var tween := create_tween ( ) . set_parallel ( true ) tween . tween_property ( current_track , "volume_db" , - 80 , fade_duration ) tween . tween_property ( next_track , "volume_db" , 0 , fade_duration ) await tween . finished

Stop old track

current_track . stop ( ) current_track = next_track BPM-Synced Transitions

Transition on beat boundary

var bpm := 120.0

Beats per minute

var beat_duration := 60.0 / bpm

0.5s per beat

func queue_transition_on_beat ( new_stream : AudioStream ) -> void :

Wait for next beat

var current_time := current_track . get_playback_position ( ) var time_to_next_beat := beat_duration - fmod ( current_time , beat_duration ) await get_tree ( ) . create_timer ( time_to_next_beat ) . timeout crossfade_to ( new_stream ) Dynamic Audio Effects Add Effect at Runtime

Add reverb to SFX bus

var sfx_bus := AudioServer . get_bus_index ( "SFX" ) var reverb := AudioEffectReverb . new ( ) reverb . room_size = 0.8

Large room

reverb . damping = 0.5 reverb . wet = 0.3

30% effect, 70% dry

AudioServer . add_bus_effect ( sfx_bus , reverb ) Underwater Effect func set_underwater ( enabled : bool ) -> void : var sfx_bus := AudioServer . get_bus_index ( "SFX" ) if enabled :

Add low-pass filter (muffled sound)

var lowpass := AudioEffectLowPassFilter . new ( ) lowpass . cutoff_hz = 500

Cut frequencies above 500 Hz

AudioServer . add_bus_effect ( sfx_bus , lowpass ) else :

Remove all effects

for i in range ( AudioServer . get_bus_effect_count ( sfx_bus ) ) : AudioServer . remove_bus_effect ( sfx_bus , 0 ) Procedural Audio Synthesize Beep

Generate simple sine wave

func create_beep ( frequency : float , duration : float ) -> AudioStreamGenerator : var stream := AudioStreamGenerator . new ( ) stream . mix_rate = 44100

Sample rate

var playback := stream . instantiate_playback ( ) var increment := frequency / stream . mix_rate var phase := 0.0 for i in range ( int ( stream . mix_rate * duration ) ) : var sample := sin ( phase * TAU ) playback . push_frame ( Vector2 ( sample , sample ) )

Stereo

phase += increment phase = fmod ( phase , 1.0 ) return stream

Usage:

var beep_stream := create_beep ( 440.0 , 0.1 )

440 Hz (A4), 0.1s

$AudioStreamPlayer . stream = beep_stream $AudioStreamPlayer . play ( ) Advanced Patterns Audio Ducking (Lower Music During Dialogue)

auto_duck.gd (on Dialogue AudioStreamPlayer)

extends AudioStreamPlayer func _ready ( ) -> void : playing . connect ( _on_playing ) finished . connect ( _on_finished ) func _on_playing ( ) -> void :

Duck music to -15 dB

var music_bus := AudioServer . get_bus_index ( "Music" ) var tween := create_tween ( ) tween . tween_method ( set_music_volume , 0.0 , - 15.0 , 0.5 ) func _on_finished ( ) -> void :

Restore music to 0 dB

var tween := create_tween ( ) tween . tween_method ( set_music_volume , - 15.0 , 0.0 , 0.5 ) func set_music_volume ( db : float ) -> void : var music_bus := AudioServer . get_bus_index ( "Music" ) AudioServer . set_bus_volume_db ( music_bus , db ) Randomize Pitch for Variation

Prevent identical sounds (footsteps, gunshots)

func play_varied_sound ( stream : AudioStream ) -> void : $AudioStreamPlayer . stream = stream $AudioStreamPlayer . pitch_scale = randf_range ( 0.9 , 1.1 )

±10% pitch

$AudioStreamPlayer . play ( ) Layered Music (Adaptive)

Intensity-based music layers (start quiet, add layers as intensity increases)

Example: Peaceful exploration → Combat

@ onready var layer_drums := $Music / Drums @ onready var layer_bass := $Music / Bass @ onready var layer_melody := $Music / Melody var intensity := 0.0

0.0 = calm, 1.0 = intense

func _ready ( ) -> void :

Start all layers in sync

layer_drums . play ( ) layer_bass . play ( ) layer_melody . play ( )

Mute high-intensity layers

layer_bass . volume_db = - 80 layer_melody . volume_db = - 80 func set_music_intensity ( new_intensity : float ) -> void : intensity = clamp ( new_intensity , 0.0 , 1.0 )

Fade in layers based on intensity

var tween := create_tween ( ) . set_parallel ( true )

Layer 1 (drums): always audible

tween . tween_property ( layer_drums , "volume_db" , 0 , 1.0 )

Layer 2 (bass): fade in at 33% intensity

var bass_db := - 80 if intensity < 0.33 else lerp ( - 80.0 , 0.0 , ( intensity - 0.33 ) / 0.67 ) tween . tween_property ( layer_bass , "volume_db" , bass_db , 1.0 )

Layer 3 (melody): fade in at 66% intensity

var melody_db := - 80 if intensity < 0.66 else lerp ( - 80.0 , 0.0 , ( intensity - 0.66 ) / 0.34 ) tween . tween_property ( layer_melody , "volume_db" , melody_db , 1.0 )

Usage (combat system):

func _on_enemy_spotted ( ) -> void : MusicManager . set_music_intensity ( 1.0 )

Full intensity

func _on_all_enemies_defeated ( ) -> void : MusicManager . set_music_intensity ( 0.0 )

Back to calm

Performance Optimization Disable Far Audio

Don't play sounds the player can't hear

extends AudioStreamPlayer3D func _process ( delta : float ) -> void : var listener := get_viewport ( ) . get_camera_3d ( ) if not listener : return var distance := global_position . distance_to ( listener . global_position ) if distance

max_distance * 1.5 :

1.5x max range

if playing : stop ( ) Edge Cases Audio Doesn't Play

Check:

1. Is stream assigned?

if not $AudioStreamPlayer . stream : push_error ( "No audio stream assigned!" )

2. Is bus muted?

var bus_idx := AudioServer . get_bus_index ( $AudioStreamPlayer . bus ) if AudioServer . is_bus_mute ( bus_idx ) : print ( "Bus is muted!" )

3. Is volume too low?

if $AudioStreamPlayer . volume_db < - 60 : print ( "Volume too quiet (< -60 dB)" ) Decision Matrix: Which AudioStreamPlayer? Feature AudioStreamPlayer AudioStreamPlayer2D AudioStreamPlayer3D Spatial ❌ Global ✅ 2D panning ✅ 3D positioning Doppler ❌ ❌ ✅ Attenuation ❌ ✅ Distance-based ✅ 3D falloff Reverb send ❌ ❌ ✅ Use for Music, UI 2D games 3D games Performance Fastest Medium Slowest Reference Master Skill: godot-master

返回排行榜