mirror of
https://github.com/deunix-educ/aventuriers-des-mots.git
synced 2026-10-13 02:11:58 +02:00
Add sound effects, ambient music and volume mixing (0.5.0)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
+94
-12
@@ -1,26 +1,108 @@
|
||||
extends Node
|
||||
## Lecture des voix, dictees, musiques et effets sonores, y compris l'audio
|
||||
## provisoire des dictees. Aucun fichier audio n'est encore fourni (etape
|
||||
## "Graphismes et audio provisoires", a venir) : play_dictation se degrade
|
||||
## silencieusement (avertissement journalise) si le fichier est absent,
|
||||
## plutot que de faire planter le jeu.
|
||||
## Lecture des voix (dictees), des effets sonores et de la musique d'ambiance.
|
||||
## Les sons viennent d'identifiants resolus par SoundCatalog dans un
|
||||
## repertoire fixe. Un fichier absent se degrade silencieusement
|
||||
## (avertissement journalise) plutot que de faire planter le jeu. La musique
|
||||
## baisse pendant qu'une dictee est lue, pour ne jamais couvrir la voix.
|
||||
|
||||
const DICTATION_AUDIO_DIR := "res://assets/audio/dictations"
|
||||
const MUSIC_DUCK_DB := -12.0
|
||||
const MUSIC_BASE_DB := -6.0
|
||||
|
||||
var _player: AudioStreamPlayer
|
||||
var _voice_player: AudioStreamPlayer
|
||||
var _music_player: AudioStreamPlayer
|
||||
var _effect_players: Array[AudioStreamPlayer] = []
|
||||
var _current_music_id := ""
|
||||
var _stream_cache: Dictionary = {}
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_player = AudioStreamPlayer.new()
|
||||
add_child(_player)
|
||||
AudioBuses.ensure()
|
||||
_voice_player = AudioStreamPlayer.new()
|
||||
_voice_player.finished.connect(_on_voice_finished)
|
||||
add_child(_voice_player)
|
||||
|
||||
_music_player = AudioStreamPlayer.new()
|
||||
_music_player.bus = AudioBuses.MUSIC
|
||||
_music_player.volume_db = MUSIC_BASE_DB
|
||||
add_child(_music_player)
|
||||
|
||||
for _i in range(4):
|
||||
var player := AudioStreamPlayer.new()
|
||||
player.bus = AudioBuses.EFFECTS
|
||||
add_child(player)
|
||||
_effect_players.append(player)
|
||||
|
||||
# Un clic sur n'importe quel bouton du jeu produit le meme petit son.
|
||||
get_tree().node_added.connect(_on_node_added)
|
||||
|
||||
|
||||
func play_dictation(audio_id: String) -> void:
|
||||
if audio_id.is_empty():
|
||||
return
|
||||
var path := "%s/%s.ogg" % [DICTATION_AUDIO_DIR, audio_id]
|
||||
var path := "%s/%s.ogg" % ["res://assets/audio/dictations", audio_id]
|
||||
if not ResourceLoader.exists(path):
|
||||
push_warning("Audio de dictee introuvable (pas encore genere) : %s" % path)
|
||||
return
|
||||
_player.stream = load(path)
|
||||
_player.play()
|
||||
_voice_player.stream = load(path)
|
||||
_voice_player.play()
|
||||
_music_player.volume_db = MUSIC_BASE_DB + MUSIC_DUCK_DB
|
||||
|
||||
|
||||
func play_sfx(sfx_id: String) -> void:
|
||||
var stream := _load_stream(SoundCatalog.sfx_path(sfx_id))
|
||||
if stream == null:
|
||||
return
|
||||
for player in _effect_players:
|
||||
if not player.playing:
|
||||
player.stream = stream
|
||||
player.play()
|
||||
return
|
||||
# Tous les lecteurs sont occupes : on coupe le premier plutot que d'ignorer.
|
||||
_effect_players[0].stream = stream
|
||||
_effect_players[0].play()
|
||||
|
||||
|
||||
## Lance la musique d'ambiance en boucle. Sans effet si c'est deja celle-ci.
|
||||
func play_music(music_id: String) -> void:
|
||||
if music_id == _current_music_id and _music_player.playing:
|
||||
return
|
||||
var stream := _load_stream(SoundCatalog.music_path(music_id))
|
||||
if stream == null:
|
||||
return
|
||||
if stream is AudioStreamOggVorbis:
|
||||
(stream as AudioStreamOggVorbis).loop = true
|
||||
_current_music_id = music_id
|
||||
_music_player.stream = stream
|
||||
_music_player.play()
|
||||
|
||||
|
||||
func current_music_id() -> String:
|
||||
return _current_music_id
|
||||
|
||||
|
||||
func stop_music() -> void:
|
||||
_current_music_id = ""
|
||||
_music_player.stop()
|
||||
|
||||
|
||||
func _load_stream(path: String) -> AudioStream:
|
||||
if path.is_empty():
|
||||
return null
|
||||
if _stream_cache.has(path):
|
||||
return _stream_cache[path]
|
||||
if not ResourceLoader.exists(path):
|
||||
push_warning("Son introuvable : %s" % path)
|
||||
return null
|
||||
var stream: AudioStream = load(path)
|
||||
_stream_cache[path] = stream
|
||||
return stream
|
||||
|
||||
|
||||
func _on_voice_finished() -> void:
|
||||
_music_player.volume_db = MUSIC_BASE_DB
|
||||
|
||||
|
||||
func _on_node_added(node: Node) -> void:
|
||||
var button := node as BaseButton
|
||||
if button != null and not button.has_meta("silent"):
|
||||
button.pressed.connect(play_sfx.bind("click"))
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
extends Node
|
||||
## Volume, taille du texte et animations. Les reglages sont ceux de l'appareil
|
||||
## Volumes (general, musique, effets), taille du texte et animations. Les
|
||||
## reglages sont ceux de l'appareil
|
||||
## (user://settings.json), pas ceux d'un profil. Les valeurs sont nettoyees par
|
||||
## AppSettings a chaque lecture ; un fichier absent ou abime redonne les valeurs
|
||||
## par defaut sans erreur. Le volume agit sur le bus principal ; la taille du
|
||||
@@ -11,6 +12,8 @@ const SETTINGS_PATH := "user://settings.json"
|
||||
|
||||
var animations_enabled := true
|
||||
var volume := 0.8
|
||||
var music_volume := 0.5
|
||||
var effects_volume := 0.8
|
||||
var text_scale_index := 0
|
||||
|
||||
var _base_font_sizes: Dictionary = {}
|
||||
@@ -36,6 +39,16 @@ func set_volume(value: float) -> void:
|
||||
_commit()
|
||||
|
||||
|
||||
func set_music_volume(value: float) -> void:
|
||||
music_volume = clampf(value, 0.0, 1.0)
|
||||
_commit()
|
||||
|
||||
|
||||
func set_effects_volume(value: float) -> void:
|
||||
effects_volume = clampf(value, 0.0, 1.0)
|
||||
_commit()
|
||||
|
||||
|
||||
func set_text_scale_index(index: int) -> void:
|
||||
text_scale_index = clampi(index, 0, AppSettings.TEXT_SCALES.size() - 1)
|
||||
_commit()
|
||||
@@ -54,6 +67,8 @@ func load_from_disk() -> void:
|
||||
raw = JSON.parse_string(file.get_as_text())
|
||||
var clean := AppSettings.sanitize(raw)
|
||||
volume = clean["volume"]
|
||||
music_volume = clean["music_volume"]
|
||||
effects_volume = clean["effects_volume"]
|
||||
text_scale_index = clean["text_scale_index"]
|
||||
animations_enabled = clean["animations_enabled"]
|
||||
|
||||
@@ -62,6 +77,8 @@ func load_from_disk() -> void:
|
||||
func save_to_disk() -> bool:
|
||||
var data := AppSettings.defaults()
|
||||
data["volume"] = volume
|
||||
data["music_volume"] = music_volume
|
||||
data["effects_volume"] = effects_volume
|
||||
data["text_scale_index"] = text_scale_index
|
||||
data["animations_enabled"] = animations_enabled
|
||||
var tmp_path := SETTINGS_PATH + ".tmp"
|
||||
@@ -79,7 +96,14 @@ func save_to_disk() -> bool:
|
||||
## Applique les reglages au moteur : volume du bus principal et tailles du
|
||||
## theme global.
|
||||
func apply() -> void:
|
||||
AudioBuses.ensure()
|
||||
AudioServer.set_bus_volume_db(0, AppSettings.volume_db(volume))
|
||||
AudioServer.set_bus_volume_db(
|
||||
AudioServer.get_bus_index(AudioBuses.MUSIC), AppSettings.volume_db(music_volume)
|
||||
)
|
||||
AudioServer.set_bus_volume_db(
|
||||
AudioServer.get_bus_index(AudioBuses.EFFECTS), AppSettings.volume_db(effects_volume)
|
||||
)
|
||||
_apply_text_scale()
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user