feat(audio): música només a PLAYING; so del rètol al final del fade-in d'INTRO
This commit is contained in:
1 parent
7d725f99f4
commit
15a7c4e18a
6 files changed
+64
-5
No files matched your search
@@ -18,6 +18,11 @@ room_intro:
|
|||||||
# "més detall" sonor a l'INTRO; els salts del joc i els respawns
|
# "més detall" sonor a l'INTRO; els salts del joc i els respawns
|
||||||
# post-mort cauen al so sec. Buit o absent = sense reverb.
|
# post-mort cauen al so sec. Buit o absent = sense reverb.
|
||||||
land_reverb: stone_hall
|
land_reverb: stone_hall
|
||||||
|
# So puntual disparat quan apareix el text d'INTRO (phase 2,
|
||||||
|
# text_enter). Es resol contra `data/sounds/<text_sound>`. Buit o
|
||||||
|
# absent = sense so. Es preloda al ctor de GameScene perquè el
|
||||||
|
# primer dispar no faça disc.
|
||||||
|
text_sound: intro_room.wav
|
||||||
|
|
||||||
room_outro:
|
room_outro:
|
||||||
text_enter_ms: 400
|
text_enter_ms: 400
|
||||||
|
|||||||
Binary file not shown.
@@ -423,11 +423,12 @@ GameScene::GameScene(Config config)
|
|||||||
hud_->setPyramidRoom(mgr.currentPyramid(), mgr.currentRoom());
|
hud_->setPyramidRoom(mgr.currentPyramid(), mgr.currentRoom());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Música de fons en bucle infinit. El track ja està al Resource::Cache
|
// La música de fons no s'arrenca ací: la sala comença a ROOM_INTRO,
|
||||||
// (preload al Director), així que playMusic només mou el punter cap a
|
// que és silenciós per disseny. La música la posa en marxa
|
||||||
// jail_audio i obri l'stream. Audio::get() està garantit no-null:
|
// `setState(PLAYING)` quan la cinemàtica acaba (i la torna a aturar
|
||||||
// Director::init crida Audio::init abans d'instanciar GameScene.
|
// `enterRoomIntro` quan entrem a una nova sala). El track ja està
|
||||||
Audio::get()->playMusic(config_.music_track, -1);
|
// al Resource::Cache (preload al Director), per tant la primera
|
||||||
|
// crida a playMusic és immediata.
|
||||||
|
|
||||||
// Font de l'overlay de pausa: reutilitzem la Surface ja cachejada al
|
// Font de l'overlay de pausa: reutilitzem la Surface ja cachejada al
|
||||||
// Director (PLACEHOLDER_NAME + ".gif"). Text només parseja el .fnt a
|
// Director (PLACEHOLDER_NAME + ".gif"). Text només parseja el .fnt a
|
||||||
@@ -448,6 +449,17 @@ GameScene::GameScene(Config config)
|
|||||||
// vegada; canvis requereixen reiniciar el joc.
|
// vegada; canvis requereixen reiniciar el joc.
|
||||||
transition_config_ = RoomFsm::loadConfig(Defaults::GameScene::CONFIG_YAML);
|
transition_config_ = RoomFsm::loadConfig(Defaults::GameScene::CONFIG_YAML);
|
||||||
|
|
||||||
|
// Preload del so del rètol d'INTRO (no hi ha manifest de sons; el
|
||||||
|
// pack agafa tot `data/` automàticament i ací registrem la clau
|
||||||
|
// al Resource::Cache perquè Audio::playSound la trobe). Si el camp
|
||||||
|
// del YAML està buit, no carreguem res — el dispar consultarà el
|
||||||
|
// cache i caurà al silenci sense crash.
|
||||||
|
if (!transition_config_.intro_text_sound.empty()) {
|
||||||
|
Resource::Cache::get().loadSound(
|
||||||
|
transition_config_.intro_text_sound,
|
||||||
|
"data/sounds/" + transition_config_.intro_text_sound);
|
||||||
|
}
|
||||||
|
|
||||||
// Log d'estat inicial: la FSM arrenca a ROOM_INTRO. En aquesta fita
|
// Log d'estat inicial: la FSM arrenca a ROOM_INTRO. En aquesta fita
|
||||||
// les transicions són instantànies (sense fades ni text) i el primer
|
// les transicions són instantànies (sense fades ni text) i el primer
|
||||||
// tick d'`updateRoomTransition` ja saltarà a PLAYING.
|
// tick d'`updateRoomTransition` ja saltarà a PLAYING.
|
||||||
@@ -537,6 +549,13 @@ void GameScene::enterRoomIntro() {
|
|||||||
transition_ = {};
|
transition_ = {};
|
||||||
room_intro_elapsed_ms_ = 0;
|
room_intro_elapsed_ms_ = 0;
|
||||||
players_spawned_in_intro_.fill(false);
|
players_spawned_in_intro_.fill(false);
|
||||||
|
intro_text_sound_played_ = false;
|
||||||
|
// La cinemàtica d'entrada és silenciosa per disseny: és INTRO qui
|
||||||
|
// posseeix l'aturada. L'OUTRO no toca la música — així el cicle
|
||||||
|
// OUTRO → INTRO transmet la sensació de "tall" en entrar a la sala
|
||||||
|
// nova, no de "fade-out" anterior. Si ja no sona res, stopMusic és
|
||||||
|
// no-op.
|
||||||
|
if (Audio::get() != nullptr) { Audio::get()->stopMusic(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameScene::triggerPlayerSpawn(std::size_t slot_index) {
|
void GameScene::triggerPlayerSpawn(std::size_t slot_index) {
|
||||||
@@ -575,6 +594,13 @@ void GameScene::setState(RoomFsm::State new_state) {
|
|||||||
if (new_state == RoomFsm::State::ROOM_INTRO) {
|
if (new_state == RoomFsm::State::ROOM_INTRO) {
|
||||||
room_intro_elapsed_ms_ = 0;
|
room_intro_elapsed_ms_ = 0;
|
||||||
players_spawned_in_intro_.fill(false);
|
players_spawned_in_intro_.fill(false);
|
||||||
|
intro_text_sound_played_ = false;
|
||||||
|
}
|
||||||
|
// En sortir d'INTRO cap a PLAYING, la música arrenca: és el moment
|
||||||
|
// en què la cinemàtica d'entrada acaba i el jugador agafa control.
|
||||||
|
// L'OUTRO no atura la música; ho fa el següent `enterRoomIntro`.
|
||||||
|
if (new_state == RoomFsm::State::PLAYING && Audio::get() != nullptr) {
|
||||||
|
Audio::get()->playMusic(config_.music_track, -1);
|
||||||
}
|
}
|
||||||
// L'avanç de sala (OUTRO → INTRO) NO viu ací: el dispara
|
// L'avanç de sala (OUTRO → INTRO) NO viu ací: el dispara
|
||||||
// `updateRoomTransition` quan la FSM detecta el final de l'OUTRO,
|
// `updateRoomTransition` quan la FSM detecta el final de l'OUTRO,
|
||||||
@@ -610,6 +636,17 @@ void GameScene::updateRoomTransition(int delta_ms) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto NEXT = RoomFsm::advance(state_, transition_, transition_config_, delta_ms);
|
const auto NEXT = RoomFsm::advance(state_, transition_, transition_config_, delta_ms);
|
||||||
|
|
||||||
|
// Disparador del so del rètol: la FSM acaba d'avançar; si estem a
|
||||||
|
// INTRO i ja hem deixat enrere phase 0 (fade_in), reproduïm el so
|
||||||
|
// una sola vegada. El so es dispara al final del fade-in, anticipant
|
||||||
|
// el text que apareix a phase 2 (text_enter): així sembla que el so
|
||||||
|
// "anuncia" l'arribada del rètol enlloc de coincidir amb la seva
|
||||||
|
// entrada visual.
|
||||||
|
if (state_ == RoomFsm::State::ROOM_INTRO && transition_.phase >= 1 && !intro_text_sound_played_ && !transition_config_.intro_text_sound.empty() && Audio::get() != nullptr) {
|
||||||
|
Audio::get()->playSound(transition_config_.intro_text_sound, Audio::Group::GAME);
|
||||||
|
intro_text_sound_played_ = true;
|
||||||
|
}
|
||||||
if (NEXT != state_) {
|
if (NEXT != state_) {
|
||||||
// El final de l'OUTRO no es resol amb un setState normal: cal
|
// El final de l'OUTRO no es resol amb un setState normal: cal
|
||||||
// demanar al LevelManager el següent nivell i carregar-lo abans
|
// demanar al LevelManager el següent nivell i carregar-lo abans
|
||||||
|
|||||||
@@ -107,6 +107,13 @@ class GameScene : public Scene {
|
|||||||
int room_intro_elapsed_ms_{0};
|
int room_intro_elapsed_ms_{0};
|
||||||
std::array<bool, 2> players_spawned_in_intro_{false, false};
|
std::array<bool, 2> players_spawned_in_intro_{false, false};
|
||||||
|
|
||||||
|
// One-shot del so del rètol d'INTRO (`text_sound` a game.yaml).
|
||||||
|
// Es disparen quan la FSM passa a phase 2 (text_enter); el flag
|
||||||
|
// evita repeticions si phase 2 s'estén multi-frame. Es reseteja a
|
||||||
|
// `enterRoomIntro` / `setState(ROOM_INTRO)`. Si la clau del so és
|
||||||
|
// buida o el cache no l'ha carregat, queda inutilitzat.
|
||||||
|
bool intro_text_sound_played_{false};
|
||||||
|
|
||||||
// Cinemàtica de spawn d'un slot concret. Posa el slot indicat en
|
// Cinemàtica de spawn d'un slot concret. Posa el slot indicat en
|
||||||
// AIRBORNE des d'una altura fora de pantalla. Cridat per
|
// AIRBORNE des d'una altura fora de pantalla. Cridat per
|
||||||
// `updateRoomTransition` quan `room_intro_elapsed_ms_` supera el
|
// `updateRoomTransition` quan `room_intro_elapsed_ms_` supera el
|
||||||
|
|||||||
@@ -50,6 +50,9 @@ namespace RoomFsm {
|
|||||||
if (n.contains("land_reverb")) {
|
if (n.contains("land_reverb")) {
|
||||||
cfg.intro_land_reverb_preset = n["land_reverb"].get_value<std::string>();
|
cfg.intro_land_reverb_preset = n["land_reverb"].get_value<std::string>();
|
||||||
}
|
}
|
||||||
|
if (n.contains("text_sound")) {
|
||||||
|
cfg.intro_text_sound = n["text_sound"].get_value<std::string>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (yaml.contains("room_outro")) {
|
if (yaml.contains("room_outro")) {
|
||||||
const auto& n = yaml["room_outro"];
|
const auto& n = yaml["room_outro"];
|
||||||
|
|||||||
@@ -87,6 +87,13 @@ namespace RoomFsm {
|
|||||||
// cinemàtica d'entrada sona igual que un salt qualsevol).
|
// cinemàtica d'entrada sona igual que un salt qualsevol).
|
||||||
std::string intro_land_reverb_preset;
|
std::string intro_land_reverb_preset;
|
||||||
|
|
||||||
|
// Clau d'un so puntual (data/sounds/<key>) que GameScene
|
||||||
|
// dispara just quan la cinemàtica d'INTRO arriba a la fase
|
||||||
|
// d'entrada del text (phase == 2). Buit o absent = sense so.
|
||||||
|
// Convenció Resource::Cache: el basename val tant de clau com
|
||||||
|
// de path (loadSound ho fa servir igual que el land_sound).
|
||||||
|
std::string intro_text_sound;
|
||||||
|
|
||||||
// Panel translúcid sota el rètol de transició (INTRO i OUTRO).
|
// Panel translúcid sota el rètol de transició (INTRO i OUTRO).
|
||||||
// Centrat verticalment al centre del playfield, ocupa tota
|
// Centrat verticalment al centre del playfield, ocupa tota
|
||||||
// l'amplada del canvas. Creix d'altura 0 → height_px durant la
|
// l'amplada del canvas. Creix d'altura 0 → height_px durant la
|
||||||
|
|||||||
Reference in new issue
Block a user