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:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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 <SDL3/SDL.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#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<int>(ZONA.x);
|
||||
const int Y1 = static_cast<int>(ZONA.y);
|
||||
const int X2 = static_cast<int>(ZONA.x + ZONA.w);
|
||||
const int Y2 = static_cast<int>(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<int>(CX - ((CX - X1) * P));
|
||||
const int X_RIGHT = static_cast<int>(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<int>(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<int>(X1 + ((CX - X1) * P));
|
||||
const int X_RIGHT = static_cast<int>(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<float>(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
|
||||
@@ -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 <string>
|
||||
|
||||
#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
|
||||
Reference in New Issue
Block a user