From ae30c9b34f6e2b545e8b4cc3b0ab2a57d80b7cb5 Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Wed, 17 Sep 2025 13:53:31 +0200 Subject: [PATCH] magic numbers: title.cpp --- config/param_320x240.txt | 10 +-- config/param_320x256.txt | 10 +-- source/defaults.h | 2 +- source/director.cpp | 2 +- source/param.cpp | 2 +- source/param.h | 2 +- source/sections/title.cpp | 126 +++++++++----------------------------- source/sections/title.h | 57 ++++++++++++----- 8 files changed, 87 insertions(+), 124 deletions(-) diff --git a/config/param_320x240.txt b/config/param_320x240.txt index 86d5b4e..2f27303 100644 --- a/config/param_320x240.txt +++ b/config/param_320x240.txt @@ -39,11 +39,11 @@ scoreboard.text_color2 FFFFFF # Color secundario del texto del marca scoreboard.skip_countdown_value 8 # Valor para saltar la cuenta atrás (segundos) # --- TITLE --- -title.press_start_position 180 # Posición Y del texto "Press Start" -title.title_duration 800 # Duración de la pantalla de título (frames) -title.arcade_edition_position 123 # Posición Y del subtítulo "Arcade Edition" -title.title_c_c_position 80 # Posición Y del título principal -title.bg_color 41526F # Color de fondo en la sección titulo +title.press_start_position 180 # Posición Y del texto "Press Start" +title.title_duration 14000 # Duración de la pantalla de título (milisegundos) +title.arcade_edition_position 123 # Posición Y del subtítulo "Arcade Edition" +title.title_c_c_position 80 # Posición Y del título principal +title.bg_color 41526F # Color de fondo en la sección titulo # --- BACKGROUND --- background.attenuate_color FFFFFF00 # Color de atenuación del fondo (RGBA hexadecimal) diff --git a/config/param_320x256.txt b/config/param_320x256.txt index d9d41ef..7d24543 100644 --- a/config/param_320x256.txt +++ b/config/param_320x256.txt @@ -39,11 +39,11 @@ scoreboard.text_color2 FFFFFF # Color secundario del texto del marca scoreboard.skip_countdown_value 8 # Valor para saltar la cuenta atrás (segundos) # --- TITLE --- -title.press_start_position 180 # Posición Y del texto "Press Start" -title.title_duration 800 # Duración de la pantalla de título (frames) -title.arcade_edition_position 123 # Posición Y del subtítulo "Arcade Edition" -title.title_c_c_position 80 # Posición Y del título principal -title.bg_color 41526F # Color de fondo en la sección titulo +title.press_start_position 180 # Posición Y del texto "Press Start" +title.title_duration 14000 # Duración de la pantalla de título (milisegundos) +title.arcade_edition_position 123 # Posición Y del subtítulo "Arcade Edition" +title.title_c_c_position 80 # Posición Y del título principal +title.bg_color 41526F # Color de fondo en la sección titulo # --- BACKGROUND --- background.attenuate_color FFFFFF00 # Color de atenuación del fondo (RGBA hexadecimal) diff --git a/source/defaults.h b/source/defaults.h index 5b6c95d..5308b79 100644 --- a/source/defaults.h +++ b/source/defaults.h @@ -58,7 +58,7 @@ constexpr int SKIP_COUNTDOWN_VALUE = 8; // --- TITLE --- namespace Title { constexpr int PRESS_START_POSITION = 180; -constexpr int DURATION = 800; +constexpr float DURATION = 14000; constexpr int ARCADE_EDITION_POSITION = 123; constexpr int TITLE_C_C_POSITION = 80; constexpr const char* BG_COLOR = "41526F"; diff --git a/source/director.cpp b/source/director.cpp index 3dc6a53..525f3cf 100644 --- a/source/director.cpp +++ b/source/director.cpp @@ -42,7 +42,7 @@ Director::Director(int argc, std::span argv) { Section::name = Section::Name::GAME; Section::options = Section::Options::GAME_PLAY_1P; #elif _DEBUG - Section::name = Section::Name::LOGO; + Section::name = Section::Name::TITLE; Section::options = Section::Options::GAME_PLAY_1P; #else // NORMAL GAME Section::name = Section::Name::LOGO; diff --git a/source/param.cpp b/source/param.cpp index 020c55c..6d8e3aa 100644 --- a/source/param.cpp +++ b/source/param.cpp @@ -106,7 +106,6 @@ auto setParams(const std::string& var, const std::string& value) -> bool { {"scoreboard.rect.h", [](const std::string& v) { param.scoreboard.rect.h = std::stoi(v); }}, {"scoreboard.skip_countdown_value", [](const std::string& v) { param.scoreboard.skip_countdown_value = std::stoi(v); }}, {"title.press_start_position", [](const std::string& v) { param.title.press_start_position = std::stoi(v); }}, - {"title.title_duration", [](const std::string& v) { param.title.title_duration = std::stoi(v); }}, {"title.arcade_edition_position", [](const std::string& v) { param.title.arcade_edition_position = std::stoi(v); }}, {"title.title_c_c_position", [](const std::string& v) { param.title.title_c_c_position = std::stoi(v); }}, {"intro.text_distance_from_bottom", [](const std::string& v) { param.intro.text_distance_from_bottom = std::stoi(v); }}}; @@ -182,6 +181,7 @@ auto setParams(const std::string& var, const std::string& value) -> bool { {"balloon.settings[3].grav", [](const std::string& v) { param.balloon.settings.at(3).grav = std::stof(v); }}, {"tabe.min_spawn_time", [](const std::string& v) { param.tabe.min_spawn_time = std::stof(v); }}, {"tabe.max_spawn_time", [](const std::string& v) { param.tabe.max_spawn_time = std::stof(v); }}, + {"title.title_duration", [](const std::string& v) { param.title.title_duration = std::stof(v); }}, {"service_menu.window_message.padding", [](const std::string& v) { param.service_menu.window_message.padding = std::stof(v); }}, {"service_menu.window_message.line_spacing", [](const std::string& v) { param.service_menu.window_message.line_spacing = std::stof(v); }}, {"service_menu.window_message.title_separator_spacing", [](const std::string& v) { param.service_menu.window_message.title_separator_spacing = std::stof(v); }}, diff --git a/source/param.h b/source/param.h index 9f87cf1..9223904 100644 --- a/source/param.h +++ b/source/param.h @@ -38,7 +38,7 @@ struct ParamFade { // --- Parámetros de la pantalla de título --- struct ParamTitle { int press_start_position = GameDefaults::Title::PRESS_START_POSITION; - int title_duration = GameDefaults::Title::DURATION; + float title_duration = GameDefaults::Title::DURATION; int arcade_edition_position = GameDefaults::Title::ARCADE_EDITION_POSITION; int title_c_c_position = GameDefaults::Title::TITLE_C_C_POSITION; Color bg_color = Color::fromHex(GameDefaults::Title::BG_COLOR); diff --git a/source/sections/title.cpp b/source/sections/title.cpp index 2c46f90..347ea8b 100644 --- a/source/sections/title.cpp +++ b/source/sections/title.cpp @@ -42,7 +42,7 @@ Title::Title() tiled_bg_(std::make_unique(param.game.game_area.rect, TiledBGMode::RANDOM)), game_logo_(std::make_unique(param.game.game_area.center_x, param.title.title_c_c_position)), mini_logo_sprite_(std::make_unique(Resource::get()->getTexture("logo_jailgames_mini.png"))), - state_(TitleState::LOGO_ANIMATING), + state_(State::LOGO_ANIMATING), num_controllers_(Input::get()->getNumGamepads()) { // Configura objetos tiled_bg_->setColor(param.title.bg_color); @@ -60,16 +60,16 @@ Title::Title() Section::attract_mode = IS_TITLE_TO_DEMO ? Section::AttractMode::TITLE_TO_LOGO : Section::AttractMode::TITLE_TO_DEMO; // Define los anclajes de los elementos - anchor_.mini_logo = (param.game.height / 5 * 4) + BLOCK; + anchor_.mini_logo = (param.game.height / MINI_LOGO_Y_DIVISOR * MINI_LOGO_Y_FACTOR) + BLOCK; mini_logo_sprite_->setY(anchor_.mini_logo); - anchor_.copyright_text = anchor_.mini_logo + mini_logo_sprite_->getHeight() + 3; + anchor_.copyright_text = anchor_.mini_logo + mini_logo_sprite_->getHeight() + COPYRIGHT_TEXT_SPACING; } // Destructor Title::~Title() { Audio::get()->stopAllSounds(); if (Section::name == Section::Name::LOGO) { - Audio::get()->fadeOutMusic(300); + Audio::get()->fadeOutMusic(MUSIC_FADE_OUT_SHORT_MS); } // Desregistra los jugadores de Options @@ -77,32 +77,17 @@ Title::~Title() { Options::gamepad_manager.clearPlayers(); } -// Actualiza las variables del objeto (frame-based) -void Title::update() { - if (SDL_GetTicks() - last_time_ > param.game.speed) { - last_time_ = SDL_GetTicks(); - Screen::get()->update(); - - updateFade(); - updateState(); - updateStartPrompt(); - updatePlayers(); - } - - Audio::update(); -} - -// Actualiza las variables del objeto (time-based) +// Actualiza las variables del objeto void Title::update(float deltaTime) { Screen::get()->update(); updateFade(); updateState(deltaTime); updateStartPrompt(); - + for (auto& player : players_) { - player->update(deltaTime); // deltaTime ya está en segundos + player->update(deltaTime); } - + Audio::update(); } @@ -292,7 +277,7 @@ void Title::handleStartButtonPress(const Options::Gamepad* controller) { } auto Title::canProcessStartButton() const -> bool { - return (state_ != TitleState::LOGO_ANIMATING || ALLOW_TITLE_ANIMATION_SKIP); + return (state_ != State::LOGO_ANIMATING || ALLOW_TITLE_ANIMATION_SKIP); } void Title::processPlayer1Start() { @@ -311,7 +296,7 @@ void Title::processPlayer2Start() { void Title::activatePlayerAndSetState(Player::Id player_id) { getPlayer(player_id)->setPlayingState(Player::State::TITLE_ANIMATION); - setState(TitleState::START_HAS_BEEN_PRESSED); + setState(State::START_HAS_BEEN_PRESSED); counter_time_ = 0.0f; } @@ -392,59 +377,22 @@ void Title::updateFade() { } // Actualiza el estado -void Title::updateState() { - // Establece la lógica según el estado - switch (state_) { - case TitleState::LOGO_ANIMATING: { - game_logo_->update(); // Mantener frame-based para consistencia del estado - if (game_logo_->hasFinished()) { - setState(TitleState::LOGO_FINISHED); - } - break; - } - case TitleState::LOGO_FINISHED: { - // Ya no se usa counter_ aquí, se usa updateState(deltaTime) - game_logo_->update(); - tiled_bg_->update(); - - // Esta lógica se movió a updateState(deltaTime) - break; - } - case TitleState::START_HAS_BEEN_PRESSED: { - // Ya no se usa counter_ aquí, se usa updateState(deltaTime) - game_logo_->update(); - tiled_bg_->update(); - - // Esta lógica se movió a updateState(deltaTime) - break; - } - - default: - break; - } -} - -// Actualiza el estado (time-based) void Title::updateState(float deltaTime) { - // deltaTime ya está en segundos desde calculateDeltaTime() game_logo_->update(deltaTime); tiled_bg_->update(deltaTime); // Establece la lógica según el estado switch (state_) { - case TitleState::LOGO_ANIMATING: { + case State::LOGO_ANIMATING: { if (game_logo_->hasFinished()) { - setState(TitleState::LOGO_FINISHED); + setState(State::LOGO_FINISHED); } break; } - case TitleState::LOGO_FINISHED: { - counter_time_ += deltaTime; // deltaTime está en milisegundos - - // param.title.title_duration está en frames (60fps), convertir a ms: frames * (1000ms/60fps) - float duration_ms = static_cast(param.title.title_duration) * (1000.0f / 60.0f); - - if (counter_time_ >= duration_ms) { + case State::LOGO_FINISHED: { + counter_time_ += deltaTime; + + if (counter_time_ >= param.title.title_duration) { // El menu ha hecho time out fade_->setPostDuration(0); fade_->activate(); @@ -452,11 +400,10 @@ void Title::updateState(float deltaTime) { } break; } - case TitleState::START_HAS_BEEN_PRESSED: { - counter_time_ += deltaTime; // deltaTime está en milisegundos - - // 100 frames a 60fps convertir a ms: 100 * (1000/60) = 1666.67 ms - if (counter_time_ >= (100.0f * 1000.0f / 60.0f)) { + case State::START_HAS_BEEN_PRESSED: { + counter_time_ += deltaTime; + + if (counter_time_ >= START_PRESSED_DELAY_MS) { fade_->activate(); } break; @@ -468,22 +415,16 @@ void Title::updateState(float deltaTime) { } void Title::updateStartPrompt() { - constexpr Uint32 LOGO_BLINK_PERIOD = 833; // milisegundos - constexpr Uint32 LOGO_BLINK_ON_TIME = 583; // 833 - 250 - - constexpr Uint32 START_BLINK_PERIOD = 167; - constexpr Uint32 START_BLINK_ON_TIME = 83; // 167 - 83 - Uint32 time_ms = SDL_GetTicks(); bool condition_met = false; switch (state_) { - case TitleState::LOGO_FINISHED: - condition_met = (time_ms % LOGO_BLINK_PERIOD) >= (LOGO_BLINK_PERIOD - LOGO_BLINK_ON_TIME); + case State::LOGO_FINISHED: + condition_met = (time_ms % LOGO_BLINK_PERIOD_MS) >= (LOGO_BLINK_PERIOD_MS - LOGO_BLINK_ON_TIME_MS); break; - case TitleState::START_HAS_BEEN_PRESSED: - condition_met = (time_ms % START_BLINK_PERIOD) >= (START_BLINK_PERIOD - START_BLINK_ON_TIME); + case State::START_HAS_BEEN_PRESSED: + condition_met = (time_ms % START_BLINK_PERIOD_MS) >= (START_BLINK_PERIOD_MS - START_BLINK_ON_TIME_MS); break; default: @@ -507,7 +448,7 @@ void Title::renderStartPrompt() { } void Title::renderCopyright() { - if (state_ != TitleState::LOGO_ANIMATING) { + if (state_ != State::LOGO_ANIMATING) { // Mini logo mini_logo_sprite_->render(); @@ -524,20 +465,20 @@ void Title::renderCopyright() { } // Cambia el estado -void Title::setState(TitleState state) { +void Title::setState(State state) { if (state_ == state) { return; } state_ = state; switch (state_) { - case TitleState::LOGO_ANIMATING: + case State::LOGO_ANIMATING: break; - case TitleState::LOGO_FINISHED: + case State::LOGO_FINISHED: Audio::get()->playMusic("title.ogg"); break; - case TitleState::START_HAS_BEEN_PRESSED: - Audio::get()->fadeOutMusic(1500); + case State::START_HAS_BEEN_PRESSED: + Audio::get()->fadeOutMusic(MUSIC_FADE_OUT_LONG_MS); break; } } @@ -606,13 +547,6 @@ void Title::initPlayers() { } } -// Actualiza los jugadores -void Title::updatePlayers() { - for (auto& player : players_) { - player->update(); - } -} - // Renderiza los jugadores void Title::renderPlayers() { for (auto const& player : players_) { diff --git a/source/sections/title.h b/source/sections/title.h index 8fdcb8c..5d80b0c 100644 --- a/source/sections/title.h +++ b/source/sections/title.h @@ -19,11 +19,22 @@ namespace Options { struct Gamepad; } // namespace Options -// --- Constantes --- -constexpr std::string_view TEXT_COPYRIGHT = "@2020,2025 JailDesigner"; // Texto de copyright -constexpr bool ALLOW_TITLE_ANIMATION_SKIP = false; // Permite saltar la animación del título - -// --- Clase Title: gestiona el estado de título/menú principal del juego --- +// --- Clase Title: pantalla de título y menú principal del juego --- +// +// Esta clase gestiona la pantalla de título del juego, incluyendo el menú principal +// y la transición entre diferentes modos de juego. +// +// Funcionalidades principales: +// • Logo animado: muestra y anima el logotipo principal del juego +// • Selección de jugadores: permite iniciar partidas de 1 o 2 jugadores +// • Modo attract: cicla automáticamente entre título y demo +// • Efectos visuales: parpadeos, transiciones y efectos de fondo +// • Gestión de controles: soporte para teclado y múltiples gamepads +// • Timeouts automáticos: transición automática si no hay interacción +// • Debug de colores: herramientas de depuración para ajustes visuales +// +// La clase utiliza un sistema de tiempo basado en milisegundos para garantizar +// comportamiento consistente independientemente del framerate. class Title { public: // --- Constructor y destructor --- @@ -34,8 +45,28 @@ class Title { void run(); private: + // --- Constantes de tiempo (en milisegundos) --- + static constexpr float START_PRESSED_DELAY_MS = 1666.67f; // Tiempo antes de fade tras pulsar start (100 frames a 60fps) + static constexpr int MUSIC_FADE_OUT_LONG_MS = 1500; // Fade out largo de música + static constexpr int MUSIC_FADE_OUT_SHORT_MS = 300; // Fade out corto de música + + // --- Constantes de parpadeo --- + static constexpr Uint32 LOGO_BLINK_PERIOD_MS = 833; // Período de parpadeo del logo + static constexpr Uint32 LOGO_BLINK_ON_TIME_MS = 583; // Tiempo encendido del logo (833-250) + static constexpr Uint32 START_BLINK_PERIOD_MS = 167; // Período de parpadeo del start + static constexpr Uint32 START_BLINK_ON_TIME_MS = 83; // Tiempo encendido del start (167-83) + + // --- Constantes de layout --- + static constexpr int MINI_LOGO_Y_DIVISOR = 5; // Divisor para posición Y del mini logo + static constexpr int MINI_LOGO_Y_FACTOR = 4; // Factor para posición Y del mini logo + static constexpr int COPYRIGHT_TEXT_SPACING = 3; // Espaciado del texto de copyright + + // --- Constantes de texto y configuración --- + static constexpr std::string_view TEXT_COPYRIGHT = "@2020,2025 JailDesigner"; // Texto de copyright + static constexpr bool ALLOW_TITLE_ANIMATION_SKIP = false; // Permite saltar la animación del título + // --- Enums --- - enum class TitleState { + enum class State { LOGO_ANIMATING, // El logo está animándose LOGO_FINISHED, // El logo ha terminado de animarse START_HAS_BEEN_PRESSED, // Se ha pulsado el botón de start @@ -59,7 +90,7 @@ class Title { Anchor anchor_; // Anclas para definir la posición de los elementos del título Section::Name next_section_; // Siguiente sección a cargar Section::Options selection_ = Section::Options::TITLE_TIME_OUT; // Opción elegida en el título - TitleState state_; // Estado actual de la sección + State state_; // Estado actual de la sección Uint64 last_time_ = 0; // Último timestamp para calcular delta-time float counter_time_ = 0.0f; // Temporizador para la pantalla de título (en milisegundos) int num_controllers_; // Número de mandos conectados @@ -68,13 +99,11 @@ class Title { bool player2_start_pressed_ = false; // Indica si se ha pulsado el botón de empezar para el jugador 2 // --- Ciclo de vida del título --- - void update(); // Actualiza las variables del objeto (frame-based) - void update(float deltaTime); // Actualiza las variables del objeto (time-based) - float calculateDeltaTime(); // Calcula el tiempo transcurrido desde el último frame - void updateState(); // Actualiza el estado actual del título (frame-based) - void updateState(float deltaTime); // Actualiza el estado actual del título (time-based) - void setState(TitleState state); // Cambia el estado del título - void resetCounter(); // Reinicia el contador interno + void update(float deltaTime); // Actualiza las variables del objeto + float calculateDeltaTime(); // Calcula el tiempo transcurrido desde el último frame + void updateState(float deltaTime); // Actualiza el estado actual del título + void setState(State state); // Cambia el estado del título + void resetCounter(); // Reinicia el contador interno // --- Entrada de usuario --- void checkEvents(); // Comprueba los eventos