Ga naar hoofdinhoud

Audio

Muziek, geluidseffecten, en audio management.

Overzicht

┌─────────────────────────────────────────────────────────────────┐
│ Audio Systeem │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Audio Buses: │
│ ├── Master │
│ │ ├── Music (achtergrondmuziek) │
│ │ ├── SFX (geluidseffecten) │
│ │ ├── UI (interface sounds) │
│ │ └── Ambient (omgevingsgeluiden) │
│ │
│ File Formats: │
│ ├── Music: OGG Vorbis (streaming) │
│ ├── SFX: WAV (low latency) │
│ └── Ambient: OGG Vorbis (looping) │
│ │
└─────────────────────────────────────────────────────────────────┘

Audio Manager

# audio_manager.gd (autoload)
extends Node

# Bus indices
var _music_bus: int
var _sfx_bus: int
var _ui_bus: int
var _ambient_bus: int

# Players
@onready var music_player: AudioStreamPlayer = $MusicPlayer
@onready var ambient_player: AudioStreamPlayer = $AmbientPlayer
var _sfx_pool: Array[AudioStreamPlayer] = []

const SFX_POOL_SIZE = 8
const FADE_DURATION = 1.0

func _ready() -> void:
_music_bus = AudioServer.get_bus_index("Music")
_sfx_bus = AudioServer.get_bus_index("SFX")
_ui_bus = AudioServer.get_bus_index("UI")
_ambient_bus = AudioServer.get_bus_index("Ambient")

_create_sfx_pool()
_load_settings()

func _create_sfx_pool() -> void:
for i in range(SFX_POOL_SIZE):
var player = AudioStreamPlayer.new()
player.bus = "SFX"
add_child(player)
_sfx_pool.append(player)

Muziek Systeem

Afspelen met Crossfade

var current_music: String = ""
var music_tween: Tween = null

func play_music(track_name: String, fade: bool = true) -> void:
if track_name == current_music:
return

var stream = _load_music(track_name)
if not stream:
push_error("Music not found: " + track_name)
return

current_music = track_name

if fade and music_player.playing:
_crossfade_to(stream)
else:
music_player.stream = stream
music_player.play()

func _crossfade_to(new_stream: AudioStream) -> void:
if music_tween:
music_tween.kill()

# Create second player for crossfade
var new_player = AudioStreamPlayer.new()
new_player.stream = new_stream
new_player.bus = "Music"
new_player.volume_db = -80
add_child(new_player)
new_player.play()

music_tween = create_tween()
music_tween.set_parallel(true)

# Fade out old
music_tween.tween_property(music_player, "volume_db", -80, FADE_DURATION)

# Fade in new
music_tween.tween_property(new_player, "volume_db", 0, FADE_DURATION)

music_tween.chain().tween_callback(func():
music_player.stop()
music_player.stream = new_stream
music_player.volume_db = 0
music_player.play()
music_player.seek(new_player.get_playback_position())
new_player.queue_free()
)

func _load_music(track_name: String) -> AudioStream:
var path = "res://assets/audio/music/%s.ogg" % track_name
if ResourceLoader.exists(path):
return load(path)
return null

Scene Music

# Automatische muziek per scene
const SCENE_MUSIC = {
"main_menu": "menu_theme",
"treehouse": "cozy_home",
"garden": "nature_morning",
"world_map": "adventure",
"shop": "cheerful_shop"
}

func _on_scene_changed(scene_name: String) -> void:
if scene_name in SCENE_MUSIC:
play_music(SCENE_MUSIC[scene_name])

Geluidseffecten

SFX Afspelen

const SFX_LIBRARY = {
# UI
"button_click": "res://assets/audio/sfx/ui/click.wav",
"button_hover": "res://assets/audio/sfx/ui/hover.wav",
"panel_open": "res://assets/audio/sfx/ui/panel_open.wav",
"panel_close": "res://assets/audio/sfx/ui/panel_close.wav",
"notification": "res://assets/audio/sfx/ui/notification.wav",

# Gameplay
"plant_seed": "res://assets/audio/sfx/gameplay/plant.wav",
"harvest": "res://assets/audio/sfx/gameplay/harvest.wav",
"water_splash": "res://assets/audio/sfx/gameplay/water.wav",
"pickup_item": "res://assets/audio/sfx/gameplay/pickup.wav",
"level_up": "res://assets/audio/sfx/gameplay/level_up.wav",

# Pets
"cat_meow": "res://assets/audio/sfx/pets/cat_meow.wav",
"dog_bark": "res://assets/audio/sfx/pets/dog_bark.wav",
"pet_happy": "res://assets/audio/sfx/pets/happy.wav",

# Footsteps
"step_grass": "res://assets/audio/sfx/footsteps/grass.wav",
"step_wood": "res://assets/audio/sfx/footsteps/wood.wav",
"step_stone": "res://assets/audio/sfx/footsteps/stone.wav"
}

func play_sound(sound_name: String, volume_db: float = 0.0) -> void:
if sound_name not in SFX_LIBRARY:
push_warning("SFX not found: " + sound_name)
return

var player = _get_available_sfx_player()
if not player:
return

player.stream = load(SFX_LIBRARY[sound_name])
player.volume_db = volume_db
player.play()

func _get_available_sfx_player() -> AudioStreamPlayer:
for player in _sfx_pool:
if not player.playing:
return player
# All busy, use first one
return _sfx_pool[0]

Positional Audio

func play_sound_at(sound_name: String, position: Vector2, max_distance: float = 500.0) -> void:
var player = AudioStreamPlayer2D.new()
player.stream = load(SFX_LIBRARY[sound_name])
player.bus = "SFX"
player.max_distance = max_distance
player.global_position = position
player.finished.connect(player.queue_free)

get_tree().current_scene.add_child(player)
player.play()

Random Pitch Variation

func play_sound_varied(sound_name: String, pitch_range: float = 0.1) -> void:
var player = _get_available_sfx_player()
if not player:
return

player.stream = load(SFX_LIBRARY[sound_name])
player.pitch_scale = randf_range(1.0 - pitch_range, 1.0 + pitch_range)
player.play()

# Voorbeeld: voetstappen met variatie
func play_footstep(surface: String) -> void:
play_sound_varied("step_" + surface, 0.15)

Ambient Audio

Omgevingsgeluiden

const AMBIENT_LAYERS = {
"treehouse": [
{"stream": "birds_chirping", "volume": -10},
{"stream": "wind_light", "volume": -15}
],
"garden": [
{"stream": "birds_chirping", "volume": -5},
{"stream": "insects", "volume": -12},
{"stream": "wind_leaves", "volume": -8}
],
"night": [
{"stream": "crickets", "volume": -8},
{"stream": "owl_distant", "volume": -20}
]
}

var ambient_players: Array[AudioStreamPlayer] = []

func set_ambient(preset: String) -> void:
# Stop current ambient
for player in ambient_players:
player.queue_free()
ambient_players.clear()

if preset not in AMBIENT_LAYERS:
return

# Start new layers
for layer in AMBIENT_LAYERS[preset]:
var player = AudioStreamPlayer.new()
player.stream = load("res://assets/audio/ambient/%s.ogg" % layer.stream)
player.bus = "Ambient"
player.volume_db = layer.volume
player.autoplay = true
add_child(player)
ambient_players.append(player)

Day/Night Transition

func transition_ambient(from: String, to: String, duration: float = 3.0) -> void:
var old_players = ambient_players.duplicate()
ambient_players.clear()

# Fade in new ambient
if to in AMBIENT_LAYERS:
for layer in AMBIENT_LAYERS[to]:
var player = AudioStreamPlayer.new()
player.stream = load("res://assets/audio/ambient/%s.ogg" % layer.stream)
player.bus = "Ambient"
player.volume_db = -80
add_child(player)
player.play()
ambient_players.append(player)

var tween = create_tween()
tween.tween_property(player, "volume_db", layer.volume, duration)

# Fade out old ambient
for player in old_players:
var tween = create_tween()
tween.tween_property(player, "volume_db", -80, duration)
tween.tween_callback(player.queue_free)

Volume Controle

func set_music_volume(value: float) -> void:
# value: 0.0 - 1.0
var db = linear_to_db(value)
AudioServer.set_bus_volume_db(_music_bus, db)

func set_sfx_volume(value: float) -> void:
var db = linear_to_db(value)
AudioServer.set_bus_volume_db(_sfx_bus, db)

func set_master_volume(value: float) -> void:
var db = linear_to_db(value)
AudioServer.set_bus_volume_db(0, db) # Master bus

func mute(bus_name: String, muted: bool) -> void:
var bus_idx = AudioServer.get_bus_index(bus_name)
AudioServer.set_bus_mute(bus_idx, muted)

func _load_settings() -> void:
var settings = Settings.load_settings()
set_music_volume(settings.music_volume)
set_sfx_volume(settings.sfx_volume)

Audio Specificaties

Muziek

TrackFormaatBitrateLoopDuur
menu_themeOGG192kbpsYes2:00
cozy_homeOGG192kbpsYes3:00
nature_morningOGG192kbpsYes2:30
adventureOGG192kbpsYes2:30
night_peacefulOGG192kbpsYes3:00

SFX

EffectFormaatSample RateLengte
button_clickWAV44.1kHz<0.1s
harvestWAV44.1kHz0.5s
level_upWAV44.1kHz1.5s
footstepWAV44.1kHz<0.2s

Ambient

LayerFormaatLoop PointVolume
birds_chirpingOGGSeamless-10dB
wind_lightOGGSeamless-15dB
cricketsOGGSeamless-8dB

Volgende