feat(title): fade-in inicial de negre a escena (500 ms, configurable a ui.yaml)

This commit is contained in:
JailDesigner committed 2026-05-06 19:34:59 +02:00
1 parent 29cb4c2211
commit 1431f27cfa
6 files changed
+36

No files matched your search

+4
View File
@@ -139,6 +139,10 @@ title:
# mosaic — capa negra lineal damunt del canvas. La música no es toca:
# `title.ogg` continua sonant cap a LOGO, que ja veurà què en fa.
attract_fade_to_black_ms: 800
# Fade-in inicial: capa negra al començament que s'esvaeix cap al fons.
# Mateix valor que `room_intro.fade_in_ms` (data/scenes/game.yaml) perquè
# l'arribada a la TITLE i a una sala de joc tinguin el mateix ritme.
intro_fade_in_ms: 500
# Distorsió sinusoidal del fons (efecte de calor del desert).
# `amplitude_px`: desplaçament màxim de cada fila en píxels.
# `wavelength_px`: distància vertical entre dos pics de l'ona.
+2
View File
@@ -108,6 +108,7 @@ UiConfig::UiConfig()
title_fade_out_max_block_(Defaults::UI::Title::FADE_OUT_MAX_BLOCK),
title_music_fade_out_ms_(Defaults::UI::Title::MUSIC_FADE_OUT_MS),
title_attract_fade_to_black_ms_(Defaults::UI::Title::ATTRACT_FADE_TO_BLACK_MS),
title_intro_fade_in_ms_(Defaults::UI::Title::INTRO_FADE_IN_MS),
title_heat_haze_{
.enabled = Defaults::UI::Title::HeatHaze::ENABLED,
.amplitude_px = Defaults::UI::Title::HeatHaze::AMPLITUDE_PX,
@@ -238,6 +239,7 @@ void UiConfig::load(const std::string& file_path) {
readField(t, "fade_out_max_block", title_fade_out_max_block_);
readField(t, "music_fade_out_ms", title_music_fade_out_ms_);
readField(t, "attract_fade_to_black_ms", title_attract_fade_to_black_ms_);
readField(t, "intro_fade_in_ms", title_intro_fade_in_ms_);
if (t.contains("heat_haze")) {
const auto& hh = t["heat_haze"];
readField(hh, "enabled", title_heat_haze_.enabled);
+2
View File
@@ -101,6 +101,7 @@ class UiConfig {
[[nodiscard]] auto titleFadeOutMaxBlock() const -> int { return title_fade_out_max_block_; }
[[nodiscard]] auto titleMusicFadeOutMs() const -> int { return title_music_fade_out_ms_; }
[[nodiscard]] auto titleAttractFadeToBlackMs() const -> int { return title_attract_fade_to_black_ms_; }
[[nodiscard]] auto titleIntroFadeInMs() const -> int { return title_intro_fade_in_ms_; }
[[nodiscard]] auto titleHeatHaze() const -> const TitleHeatHaze& { return title_heat_haze_; }
// Corbes d'easing triades al YAML. Si el nom no és conegut, s'usa
@@ -180,6 +181,7 @@ class UiConfig {
int title_fade_out_max_block_;
int title_music_fade_out_ms_;
int title_attract_fade_to_black_ms_;
int title_intro_fade_in_ms_;
TitleHeatHaze title_heat_haze_;
std::string menu_select_sound_;
+16
View File
@@ -101,6 +101,10 @@ TitleScene::TitleScene(std::shared_ptr<Text> display_font, std::shared_ptr<Text>
.max_block_size = UiConfig::get().titleFadeOutMaxBlock(),
});
attract_fade_.emplace();
// Fade-in inicial: arrenquem en negre opac i ens esvaïm cap a la
// imatge de fons. Sense lligar a cap fase — viu sempre al frame i
// un cop finished, `render()` és no-op fins que la TitleScene mor.
intro_fade_.begin(UiConfig::get().titleIntroFadeInMs(), Transitions::FadeToBlack::Direction::FADE_IN);
// Música de fons en bucle. Es preloda al Director, així que la
// primera crida és immediata. El fade-out arrenca en entrar a
@@ -122,6 +126,11 @@ void TitleScene::update(int delta_ms) {
if (heat_haze_) { heat_haze_->tick(delta_ms); }
// Fade-in inicial: tica sempre. Un cop finished, FadeToBlack::tick
// és idempotent (clamp intern al duration_ms_) i `render()` pinta
// alpha=0 (no-op) durant la resta del cicle.
intro_fade_.tick(delta_ms);
// Comptador del parpadeig: avança sempre amb `delta_ms` cru. La
// lectura (cicle/visible) la decideix `render()` segons la fase:
// IDLE usa visible_ms / hidden_ms del yaml (asimètric, 80/20 per
@@ -336,6 +345,13 @@ void TitleScene::render() {
Notifier::get().render();
// Fade-in inicial: capa negra que s'esvaeix cap a l'escena durant
// els primers `title.intro_fade_in_ms` ms. `render()` és no-op un
// cop finished, així que es queda penjat aquí sense lògica
// condicional. Va per damunt del Notifier — coherent amb la resta
// de transicions — però per davall del RenderInfo de debug.
intro_fade_.render();
// Capa de transició final segons la fase. Sempre cobreix l'escena
// i el Notifier, mai el RenderInfo (overlay de debug, sempre
// visible). STARTING usa mosaic dissolve; ATTRACT_OUT, fade-to-black
+5
View File
@@ -61,4 +61,9 @@ class TitleScene : public Scene {
DebugConfig::StartingPlayers pending_players_{DebugConfig::StartingPlayers::BOTH};
std::optional<Transitions::Mosaic> fade_out_;
std::optional<Transitions::FadeToBlack> attract_fade_;
// Fade-in inicial: capa negra que s'esvaeix cap a la imatge de fons
// durant els primers `title.intro_fade_in_ms` ms. Sempre activa al
// ctor; un cop finished, `render()` pinta alpha=0 (no-op) la resta
// del cicle de l'escena.
Transitions::FadeToBlack intro_fade_;
};
+7
View File
@@ -294,6 +294,13 @@ namespace Defaults {
// veurà què en fa.
constexpr int ATTRACT_FADE_TO_BLACK_MS = 800;
// Fade-in inicial de l'escena: capa negra al començament que
// s'esvaeix cap a la imatge de fons. Lineal, sense mosaic —
// mateixa convenció que el `room_intro.fade_in_ms` del joc
// perquè l'arribada a la TITLE i l'arribada a una sala de
// joc tinguin el mateix ritme visual.
constexpr int INTRO_FADE_IN_MS = 500;
// Distorsió sinusoidal horitzontal de l'imatge de fons
// (efecte de calor del desert). `amplitude_px` és el
// desplaçament màxim per fila; `wavelength_px` la distància