[agent:claude-cli] feat(audio): système audio complet — swim loop, boost, block break, echo ping, pearl collect, mort/respawn, deep sea ambient (placeholders AudioStreamGenerator si .ogg absent)

This commit is contained in:
2026-04-21 08:38:59 +00:00
parent 2e4d697977
commit 2c49e0c9db
4 changed files with 230 additions and 11 deletions

View File

@@ -3,6 +3,8 @@ extends Node
var music_player: AudioStreamPlayer
var ambient_loop: AudioStreamPlayer
var whale_player: AudioStreamPlayer
var swim_loop_player: AudioStreamPlayer
var boost_player: AudioStreamPlayer
var _whale_timer: Timer
var _music_tween: Tween
@@ -10,6 +12,9 @@ var _music_tween: Tween
const MUSIC_PATH := "res://audio/music/"
const SFX_PATH := "res://audio/sfx/"
# Generated stream cache (AudioStreamGenerator-based placeholders)
var _gen_streams: Dictionary = {}
func _ready() -> void:
music_player = AudioStreamPlayer.new()
music_player.bus = "Music"
@@ -26,6 +31,18 @@ func _ready() -> void:
whale_player.volume_db = -15.0
add_child(whale_player)
# Swim loop player (continuous while moving)
swim_loop_player = AudioStreamPlayer.new()
swim_loop_player.bus = "SFX"
swim_loop_player.volume_db = -18.0
add_child(swim_loop_player)
# Boost player (one-shot on boost start)
boost_player = AudioStreamPlayer.new()
boost_player.bus = "SFX"
boost_player.volume_db = -10.0
add_child(boost_player)
_whale_timer = Timer.new()
_whale_timer.one_shot = true
_whale_timer.timeout.connect(_on_whale_timer)
@@ -33,6 +50,18 @@ func _ready() -> void:
_schedule_next_whale()
# Returns (or creates) a silent AudioStreamGenerator for placeholder sounds.
func _get_silent_stream(duration_sec: float = 0.5) -> AudioStreamGenerator:
var key := "silent_%.1f" % duration_sec
if _gen_streams.has(key):
return _gen_streams[key]
var gen := AudioStreamGenerator.new()
gen.mix_rate = 22050.0
gen.buffer_length = duration_sec
_gen_streams[key] = gen
return gen
func play_music(track_name: String) -> void:
var path := MUSIC_PATH + track_name
if not ResourceLoader.exists(path):
@@ -56,6 +85,8 @@ func play_music(track_name: String) -> void:
func play_ambient_loop(loop_name: String) -> void:
var path := SFX_PATH + loop_name
if not ResourceLoader.exists(path):
# Fall back to silent generator so the slot is occupied
ambient_loop.stream = _get_silent_stream(2.0)
return
var stream: AudioStream = load(path)
@@ -84,9 +115,6 @@ func play_whale_call_random() -> void:
func play_bubble_sfx(position: Vector3) -> void:
var path := SFX_PATH + "bubbles.ogg"
if not ResourceLoader.exists(path):
return
var player := AudioStreamPlayer3D.new()
player.bus = "SFX"
player.volume_db = -8.0
@@ -95,16 +123,178 @@ func play_bubble_sfx(position: Vector3) -> void:
add_child(player)
player.global_position = position
var stream: AudioStream = load(path)
if stream == null:
player.queue_free()
return
if ResourceLoader.exists(path):
var stream: AudioStream = load(path)
if stream != null:
player.stream = stream
player.play()
player.finished.connect(player.queue_free)
return
player.stream = stream
# Placeholder: silent generator
player.stream = _get_silent_stream(0.3)
player.play()
player.finished.connect(player.queue_free)
# --- New: swim loop ---
func play_swim_loop() -> void:
if swim_loop_player.playing:
return
var path := SFX_PATH + "swim_loop.ogg"
if ResourceLoader.exists(path):
var s: AudioStream = load(path)
if s != null:
swim_loop_player.stream = s
swim_loop_player.play()
return
# Silent placeholder keeps the architecture wired
swim_loop_player.stream = _get_silent_stream(1.0)
swim_loop_player.play()
func stop_swim_loop() -> void:
if swim_loop_player.playing:
swim_loop_player.stop()
# --- New: boost sound ---
func play_boost() -> void:
var path := SFX_PATH + "boost.ogg"
if ResourceLoader.exists(path):
var s: AudioStream = load(path)
if s != null:
boost_player.stream = s
boost_player.play()
return
boost_player.stream = _get_silent_stream(0.4)
boost_player.play()
# --- New: block break ---
func play_block_break(position: Vector3) -> void:
var path := SFX_PATH + "block_break.ogg"
var player := AudioStreamPlayer3D.new()
player.bus = "SFX"
player.volume_db = -6.0
player.max_distance = 15.0
add_child(player)
player.global_position = position
if ResourceLoader.exists(path):
var s: AudioStream = load(path)
if s != null:
player.stream = s
player.play()
player.finished.connect(player.queue_free)
return
player.stream = _get_silent_stream(0.3)
player.play()
player.finished.connect(player.queue_free)
# --- New: echolocation ping ---
func play_echo_ping(position: Vector3) -> void:
var path := SFX_PATH + "echo_ping.ogg"
var player := AudioStreamPlayer3D.new()
player.bus = "SFX"
player.volume_db = -4.0
player.max_distance = 40.0
add_child(player)
player.global_position = position
if ResourceLoader.exists(path):
var s: AudioStream = load(path)
if s != null:
player.stream = s
player.play()
player.finished.connect(player.queue_free)
return
player.stream = _get_silent_stream(0.5)
player.play()
player.finished.connect(player.queue_free)
# --- New: pearl collect ---
func play_pearl_collect(position: Vector3) -> void:
var path := SFX_PATH + "pearl_collect.ogg"
var player := AudioStreamPlayer3D.new()
player.bus = "SFX"
player.volume_db = -5.0
player.max_distance = 20.0
add_child(player)
player.global_position = position
if ResourceLoader.exists(path):
var s: AudioStream = load(path)
if s != null:
player.stream = s
player.play()
player.finished.connect(player.queue_free)
return
player.stream = _get_silent_stream(0.4)
player.play()
player.finished.connect(player.queue_free)
# --- New: death / respawn ---
func play_death() -> void:
var path := SFX_PATH + "death.ogg"
if ResourceLoader.exists(path):
var s: AudioStream = load(path)
if s != null:
var p := AudioStreamPlayer.new()
p.bus = "SFX"
p.volume_db = -5.0
add_child(p)
p.stream = s
p.play()
p.finished.connect(p.queue_free)
return
# silent fallback — architecture is wired
var p2 := AudioStreamPlayer.new()
p2.bus = "SFX"
p2.stream = _get_silent_stream(0.6)
add_child(p2)
p2.play()
p2.finished.connect(p2.queue_free)
func play_respawn() -> void:
var path := SFX_PATH + "respawn.ogg"
if ResourceLoader.exists(path):
var s: AudioStream = load(path)
if s != null:
var p := AudioStreamPlayer.new()
p.bus = "SFX"
p.volume_db = -6.0
add_child(p)
p.stream = s
p.play()
p.finished.connect(p.queue_free)
return
var p2 := AudioStreamPlayer.new()
p2.bus = "SFX"
p2.stream = _get_silent_stream(0.5)
add_child(p2)
p2.play()
p2.finished.connect(p2.queue_free)
# --- New: ambient deep sea (low drone, played on top of underwater_ambient) ---
func play_deep_sea_ambient() -> void:
var path := SFX_PATH + "deep_sea_ambient.ogg"
var player := AudioStreamPlayer.new()
player.bus = "SFX"
player.volume_db = -20.0
add_child(player)
if ResourceLoader.exists(path):
var s: AudioStream = load(path)
if s != null:
player.stream = s
player.play()
return
player.stream = _get_silent_stream(2.0)
player.play()
func set_music_volume(db: float) -> void:
music_player.volume_db = db