From a4942fcbae9e1c6769a2898e4d4eb72b99ade401 Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Wed, 20 May 2026 07:54:02 +0200 Subject: [PATCH] Fase 9c: extraer InitHudAnimator de GameScene GameScene::dibuixar_marges_animat, dibuixar_marcador_animat, calcular_posicio_nau_init_hud y calcular_progress_rango (4 funciones, ~135 LOC) salen a Systems::InitHud en source/game/systems/init_hud_animator.{hpp,cpp}. Las funciones son puras (sin estado interno propio). API libre en namespace: - computeRangeProgress(global, init, end): normalizacion de la ventana de progreso de un elemento dentro del global 0..1. - computeShipPosition(progress, final_position): interpola Y desde fuera de pantalla con ease_out_quad. - drawBordersAnimated(renderer, progress): efecto pincel en 3 fases. - drawScoreboardAnimated(text, scoreboard_text, progress): texto subiendo desde abajo. GameScene inyecta lo que cada funcion necesita por parametro (spawn point desde obtenir_punt_spawn, scoreboard desde buildScoreboard). Sin estado mutable compartido. GameScene.cpp acumulado tras 9a/9b/9c: 1429 -> 1043 LOC. Smoke test xvfb OK. Co-Authored-By: Claude Opus 4.7 (1M context) --- source/game/scenes/game_scene.cpp | 157 ++-------------------- source/game/scenes/game_scene.hpp | 8 -- source/game/systems/init_hud_animator.cpp | 100 ++++++++++++++ source/game/systems/init_hud_animator.hpp | 49 +++++++ 4 files changed, 160 insertions(+), 154 deletions(-) create mode 100644 source/game/systems/init_hud_animator.cpp create mode 100644 source/game/systems/init_hud_animator.hpp diff --git a/source/game/scenes/game_scene.cpp b/source/game/scenes/game_scene.cpp index e043a9c..a14828a 100644 --- a/source/game/scenes/game_scene.cpp +++ b/source/game/scenes/game_scene.cpp @@ -23,6 +23,7 @@ #include "game/stage_system/stage_loader.hpp" #include "game/systems/collision_system.hpp" #include "game/systems/continue_system.hpp" +#include "game/systems/init_hud_animator.hpp" // Using declarations per simplificar el codi using SceneManager::SceneContext; @@ -429,24 +430,24 @@ void GameScene::update(float delta_time) { global_progress = std::min(1.0F, global_progress); // [NEW] Calcular progress independiente para cada nave - float ship1_progress = calcular_progress_rango( + float ship1_progress = Systems::InitHud::computeRangeProgress( global_progress, Defaults::Game::INIT_HUD_SHIP1_RATIO_INIT, Defaults::Game::INIT_HUD_SHIP1_RATIO_END); - float ship2_progress = calcular_progress_rango( + float ship2_progress = Systems::InitHud::computeRangeProgress( global_progress, Defaults::Game::INIT_HUD_SHIP2_RATIO_INIT, Defaults::Game::INIT_HUD_SHIP2_RATIO_END); // [MODIFICAT] Animar AMBAS naves con sus progress respectivos if (match_config_.jugador1_actiu && ship1_progress < 1.0F) { - Vec2 pos_p1 = calcular_posicio_nau_init_hud(ship1_progress, 0); + Vec2 pos_p1 = Systems::InitHud::computeShipPosition(ship1_progress, obtenir_punt_spawn(0)); ships_[0].setCenter(pos_p1); } if (match_config_.jugador2_actiu && ship2_progress < 1.0F) { - Vec2 pos_p2 = calcular_posicio_nau_init_hud(ship2_progress, 1); + Vec2 pos_p2 = Systems::InitHud::computeShipPosition(ship2_progress, obtenir_punt_spawn(1)); ships_[1].setCenter(pos_p2); } @@ -630,22 +631,22 @@ void GameScene::draw() { float global_progress = 1.0F - (timer / total_time); // [NEW] Calcular progress independiente para cada elemento - float rect_progress = calcular_progress_rango( + float rect_progress = Systems::InitHud::computeRangeProgress( global_progress, Defaults::Game::INIT_HUD_RECT_RATIO_INIT, Defaults::Game::INIT_HUD_RECT_RATIO_END); - float score_progress = calcular_progress_rango( + float score_progress = Systems::InitHud::computeRangeProgress( global_progress, Defaults::Game::INIT_HUD_SCORE_RATIO_INIT, Defaults::Game::INIT_HUD_SCORE_RATIO_END); - float ship1_progress = calcular_progress_rango( + float ship1_progress = Systems::InitHud::computeRangeProgress( global_progress, Defaults::Game::INIT_HUD_SHIP1_RATIO_INIT, Defaults::Game::INIT_HUD_SHIP1_RATIO_END); - float ship2_progress = calcular_progress_rango( + float ship2_progress = Systems::InitHud::computeRangeProgress( global_progress, Defaults::Game::INIT_HUD_SHIP2_RATIO_INIT, Defaults::Game::INIT_HUD_SHIP2_RATIO_END); @@ -658,11 +659,11 @@ void GameScene::draw() { init_hud_rect_sound_played_ = true; } - dibuixar_marges_animat(rect_progress); + Systems::InitHud::drawBordersAnimated(sdl_.getRenderer(), rect_progress); } if (score_progress > 0.0F) { - dibuixar_marcador_animat(score_progress); + Systems::InitHud::drawScoreboardAnimated(text_, buildScoreboard(), score_progress); } // [MODIFICAT] Dibuixar naves con progress independent @@ -824,142 +825,6 @@ void GameScene::dibuixar_marcador() { text_.renderCentered(text, {.x = centre_x, .y = centre_y}, scale, spacing); } -void GameScene::dibuixar_marges_animat(float progress) const { - // Animación seqüencial del rectangle con efecte de "pinzell" - // Dos pinzells comencen al centro superior i baixen por los laterals - - const SDL_FRect& zona = Defaults::Zones::PLAYAREA; - - // Aplicar easing al progrés global - float eased_progress = Easing::ease_out_quad(progress); - - // Coordenades del rectangle complet - int x1 = static_cast(zona.x); - int y1 = static_cast(zona.y); - int x2 = static_cast(zona.x + zona.w); - int y2 = static_cast(zona.y + zona.h); - int cx = (x1 + x2) / 2; - - // Dividir en 3 fases de 33% cada una - constexpr float PHASE_1_END = 0.33F; - constexpr float PHASE_2_END = 0.66F; - - // --- FASE 1: Línies horitzontals superiors (0-33%) --- - if (eased_progress > 0.0F) { - float phase1_progress = std::min(eased_progress / PHASE_1_END, 1.0F); - - // Línia izquierda: creix des del centro hacia l'izquierda - int x1_phase1 = static_cast(cx - ((cx - x1) * phase1_progress)); - Rendering::linea(sdl_.getRenderer(), cx, y1, x1_phase1, y1); - - // Línia derecha: creix des del centro hacia la derecha - int x2_phase1 = static_cast(cx + ((x2 - cx) * phase1_progress)); - Rendering::linea(sdl_.getRenderer(), cx, y1, x2_phase1, y1); - } - - // --- FASE 2: Línies verticals laterals (33-66%) --- - if (eased_progress > PHASE_1_END) { - float phase2_progress = std::min((eased_progress - PHASE_1_END) / (PHASE_2_END - PHASE_1_END), 1.0F); - - // Línia izquierda: creix desde dalt hacia baix - int y2_phase2 = static_cast(y1 + ((y2 - y1) * phase2_progress)); - Rendering::linea(sdl_.getRenderer(), x1, y1, x1, y2_phase2); - - // Línia derecha: creix desde dalt hacia baix - Rendering::linea(sdl_.getRenderer(), x2, y1, x2, y2_phase2); - } - - // --- FASE 3: Línies horitzontals inferiors (66-100%) --- - if (eased_progress > PHASE_2_END) { - float phase3_progress = (eased_progress - PHASE_2_END) / (1.0F - PHASE_2_END); - - // Línia izquierda: creix desde l'izquierda hacia el centro - int x_left_phase3 = static_cast(x1 + ((cx - x1) * phase3_progress)); - Rendering::linea(sdl_.getRenderer(), x1, y2, x_left_phase3, y2); - - // Línia derecha: creix desde la derecha hacia el centro - int x_right_phase3 = static_cast(x2 - ((x2 - cx) * phase3_progress)); - Rendering::linea(sdl_.getRenderer(), x2, y2, x_right_phase3, y2); - } -} - -void GameScene::dibuixar_marcador_animat(float progress) { - // Animación del marcador pujant desde baix con easing - - // Calcular progrés con easing - float eased_progress = Easing::ease_out_quad(progress); - - // Construir text - std::string text = buildScoreboard(); - - // Parámetros - const float scale = 0.85F; - const float spacing = 0.0F; - - // Calcular centro de la zona del marcador - const SDL_FRect& scoreboard = Defaults::Zones::SCOREBOARD; - float centre_x = scoreboard.w / 2.0F; - float centre_y_final = scoreboard.y + (scoreboard.h / 2.0F); - - // Posición Y inicial (offscreen, sota de la pantalla) - auto centre_y_inicial = static_cast(Defaults::Game::HEIGHT); - - // Interpolació con easing - float centre_y_animada = centre_y_inicial + ((centre_y_final - centre_y_inicial) * eased_progress); - - // Renderizar centrat en posición animada - text_.renderCentered(text, {.x = centre_x, .y = centre_y_animada}, scale, spacing); -} - -Vec2 GameScene::calcular_posicio_nau_init_hud(float progress, uint8_t player_id) const { - // Animación de la ship pujant desde baix con easing - // [MODIFICAT] Ambas naves usan ease_out_quad (desfase temporal via INIT/END) - - // Aplicar easing (uniforme para ambas naves) - float eased_progress = Easing::ease_out_quad(progress); - - const SDL_FRect& zona = Defaults::Zones::PLAYAREA; - - // Calcular posición final segons player (reutilitza obtenir_punt_spawn) - Vec2 spawn_final = obtenir_punt_spawn(player_id); - float x_final = spawn_final.x; - float y_final = spawn_final.y; - - // Y inicial: offscreen, 50px sota la zona de juego - float y_inicial = zona.y + zona.h + 50.0F; - - // X no canvia (destí segons player_id) - // Y interpola con easing - float y_animada = y_inicial + ((y_final - y_inicial) * eased_progress); - - return {.x = x_final, .y = y_animada}; -} - -float GameScene::calcular_progress_rango(float global_progress, float ratio_init, float ratio_end) const { - // Convierte global_progress (0.0→1.0) a element_progress usando ventana [INIT, END] - // - // Casos: - // - global_progress < INIT → 0.0 (no ha empezado) - // - global_progress > END → 1.0 (completado) - // - INIT ≤ global_progress ≤ END → interpola linealmente 0.0→1.0 - - // Validación de parámetros (evita división por cero) - if (ratio_init >= ratio_end) { - return (global_progress >= ratio_end) ? 1.0F : 0.0F; - } - - if (global_progress < ratio_init) { - return 0.0F; - } - - if (global_progress > ratio_end) { - return 1.0F; - } - - // Normalizar rango [INIT, END] a [0.0, 1.0] - return (global_progress - ratio_init) / (ratio_end - ratio_init); -} - std::string GameScene::buildScoreboard() const { // Puntuación P1 (6 dígits) - mostrar zeros si inactiu std::string score_p1; diff --git a/source/game/scenes/game_scene.hpp b/source/game/scenes/game_scene.hpp index e4ae658..e58a4a0 100644 --- a/source/game/scenes/game_scene.hpp +++ b/source/game/scenes/game_scene.hpp @@ -94,14 +94,6 @@ class GameScene { // [NEW] Stage system helpers void dibuixar_missatge_stage(const std::string& message); - // [NEW] Funciones de animación per INIT_HUD - void dibuixar_marges_animat(float progress) const; // Rectangle con creixement uniforme - void dibuixar_marcador_animat(float progress); // Marcador que puja desde baix - [[nodiscard]] Vec2 calcular_posicio_nau_init_hud(float progress, uint8_t player_id) const; // Posición animada de la ship - - // [NEW] Función helper del sistema de animación INIT_HUD - [[nodiscard]] float calcular_progress_rango(float global_progress, float ratio_init, float ratio_end) const; - // [NEW] Función helper del marcador [[nodiscard]] std::string buildScoreboard() const; }; diff --git a/source/game/systems/init_hud_animator.cpp b/source/game/systems/init_hud_animator.cpp new file mode 100644 index 0000000..fb4fe11 --- /dev/null +++ b/source/game/systems/init_hud_animator.cpp @@ -0,0 +1,100 @@ +// init_hud_animator.cpp - Implementación de la animación inicial del HUD + +#include "game/systems/init_hud_animator.hpp" + +#include + +#include +#include +#include + +#include "core/defaults.hpp" +#include "core/math/easing.hpp" +#include "core/rendering/line_renderer.hpp" + +namespace Systems::InitHud { + +auto computeRangeProgress(float global_progress, + float ratio_init, + float ratio_end) -> float { + if (ratio_init >= ratio_end) { + return (global_progress >= ratio_end) ? 1.0F : 0.0F; + } + if (global_progress < ratio_init) { + return 0.0F; + } + if (global_progress > ratio_end) { + return 1.0F; + } + return (global_progress - ratio_init) / (ratio_end - ratio_init); +} + +auto computeShipPosition(float progress, const Vec2& final_position) -> Vec2 { + const float EASED = Easing::ease_out_quad(progress); + const SDL_FRect& ZONA = Defaults::Zones::PLAYAREA; + // Y inicial: 50 px bajo la zona de juego. + const float Y_INI = ZONA.y + ZONA.h + 50.0F; + const float Y_ANIM = Y_INI + ((final_position.y - Y_INI) * EASED); + return Vec2{.x = final_position.x, .y = Y_ANIM}; +} + +void drawBordersAnimated(Rendering::Renderer* renderer, float progress) { + const SDL_FRect& ZONA = Defaults::Zones::PLAYAREA; + const float EASED = Easing::ease_out_quad(progress); + + const int X1 = static_cast(ZONA.x); + const int Y1 = static_cast(ZONA.y); + const int X2 = static_cast(ZONA.x + ZONA.w); + const int Y2 = static_cast(ZONA.y + ZONA.h); + const int CX = (X1 + X2) / 2; + + constexpr float PHASE_1_END = 0.33F; + constexpr float PHASE_2_END = 0.66F; + + // Fase 1: línea superior crece desde el centro hacia los lados. + if (EASED > 0.0F) { + const float P = std::min(EASED / PHASE_1_END, 1.0F); + const int X_LEFT = static_cast(CX - ((CX - X1) * P)); + const int X_RIGHT = static_cast(CX + ((X2 - CX) * P)); + Rendering::linea(renderer, CX, Y1, X_LEFT, Y1); + Rendering::linea(renderer, CX, Y1, X_RIGHT, Y1); + } + + // Fase 2: laterales bajan. + if (EASED > PHASE_1_END) { + const float P = std::min((EASED - PHASE_1_END) / (PHASE_2_END - PHASE_1_END), 1.0F); + const int Y_BOTTOM = static_cast(Y1 + ((Y2 - Y1) * P)); + Rendering::linea(renderer, X1, Y1, X1, Y_BOTTOM); + Rendering::linea(renderer, X2, Y1, X2, Y_BOTTOM); + } + + // Fase 3: línea inferior crece desde los lados hacia el centro. + if (EASED > PHASE_2_END) { + const float P = (EASED - PHASE_2_END) / (1.0F - PHASE_2_END); + const int X_LEFT = static_cast(X1 + ((CX - X1) * P)); + const int X_RIGHT = static_cast(X2 - ((X2 - CX) * P)); + Rendering::linea(renderer, X1, Y2, X_LEFT, Y2); + Rendering::linea(renderer, X2, Y2, X_RIGHT, Y2); + } +} + +void drawScoreboardAnimated(Graphics::VectorText& text, + const std::string& scoreboard_text, + float progress) { + const float EASED = Easing::ease_out_quad(progress); + + constexpr float SCALE = 0.85F; + constexpr float SPACING = 0.0F; + const SDL_FRect& SCOREBOARD = Defaults::Zones::SCOREBOARD; + const float CENTRE_X = SCOREBOARD.w / 2.0F; + const float Y_FINAL = SCOREBOARD.y + (SCOREBOARD.h / 2.0F); + // Posición inicial: fuera de la pantalla por debajo. + const auto Y_INI = static_cast(Defaults::Game::HEIGHT); + const float Y_ANIM = Y_INI + ((Y_FINAL - Y_INI) * EASED); + + text.renderCentered(scoreboard_text, + Vec2{.x = CENTRE_X, .y = Y_ANIM}, + SCALE, SPACING); +} + +} // namespace Systems::InitHud diff --git a/source/game/systems/init_hud_animator.hpp b/source/game/systems/init_hud_animator.hpp new file mode 100644 index 0000000..681f584 --- /dev/null +++ b/source/game/systems/init_hud_animator.hpp @@ -0,0 +1,49 @@ +// init_hud_animator.hpp - Animación inicial del HUD del juego +// © 2025 Orni Attack +// +// Cubre la animación INIT_HUD del comienzo de cada partida/stage: +// 1. Crecimiento de los marcos del PLAYAREA con efecto pincel en 3 fases. +// 2. Marcador subiendo desde abajo. +// 3. Naves entrando desde la zona inferior hacia su spawn. +// +// Todas las funciones son puras (sin estado interno propio). GameScene aporta +// el contexto que necesitan: posiciones finales, texto del scoreboard y el +// renderer/VectorText. El timing global (progress 0..1) lo gestiona +// StageManager. + +#pragma once + +#include + +#include "core/graphics/vector_text.hpp" +#include "core/rendering/render_context.hpp" +#include "core/types.hpp" + +namespace Systems::InitHud { + +// Convierte un progreso global 0..1 al sub-progreso de un elemento que solo +// se anima en la ventana [ratio_init, ratio_end]. +// < ratio_init → 0.0 (no empezó) +// > ratio_end → 1.0 (terminó) +// en rango → interpolación lineal 0..1 +[[nodiscard]] auto computeRangeProgress(float global_progress, + float ratio_init, + float ratio_end) -> float; + +// Calcula posición Y animada de una nave durante INIT_HUD. La nave sube +// desde 50 px bajo el PLAYAREA hasta `final_position` con easing. +[[nodiscard]] auto computeShipPosition(float progress, const Vec2& final_position) -> Vec2; + +// Dibuja los 4 lados del PLAYAREA con efecto pincel en 3 fases: +// 0..33% → línea superior crece desde el centro hacia los lados. +// 33..66% → líneas verticales bajan por los laterales. +// 66..100% → línea inferior crece desde los lados hacia el centro. +void drawBordersAnimated(Rendering::Renderer* renderer, float progress); + +// Dibuja el scoreboard centrado, subiendo desde fuera de la pantalla +// hasta su posición final con easing. +void drawScoreboardAnimated(Graphics::VectorText& text, + const std::string& scoreboard_text, + float progress); + +} // namespace Systems::InitHud