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) <noreply@anthropic.com>
This commit is contained in:
2026-05-20 07:54:02 +02:00
parent 816bc02d9d
commit a4942fcbae
4 changed files with 160 additions and 154 deletions
+11 -146
View File
@@ -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<int>(zona.x);
int y1 = static_cast<int>(zona.y);
int x2 = static_cast<int>(zona.x + zona.w);
int y2 = static_cast<int>(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<int>(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<int>(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<int>(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<int>(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<int>(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<float>(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;