From 473a52f98647791e615b6b0009fc06a7626ff029 Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Wed, 1 Oct 2025 18:05:00 +0200 Subject: [PATCH] treballant en la animacio alpixel del carrusel --- source/scoreboard.cpp | 146 ++++++++++++++++++++++++++++++--------- source/scoreboard.h | 10 ++- source/sections/game.cpp | 14 ++-- source/sections/game.h | 6 +- 4 files changed, 132 insertions(+), 44 deletions(-) diff --git a/source/scoreboard.cpp b/source/scoreboard.cpp index 9c2e8e4..bea3fc8 100644 --- a/source/scoreboard.cpp +++ b/source/scoreboard.cpp @@ -90,6 +90,40 @@ Scoreboard::~Scoreboard() { } } +// Establece el carrusel y detecta cambios para iniciar animación +void Scoreboard::setCarousel(Id id, const std::string& carousel) { + size_t idx = static_cast(id); + + if (carousel.empty()) { + carousel_.at(idx) = carousel; + return; + } + + // Calcular el índice central del nuevo carrusel + int new_center_index = carousel.size() / 2; + + // Si es la primera vez o cambió el índice, establecer nuevo target + if (carousel_.at(idx).empty()) { + // Primera inicialización: posicionar directamente sin animar + carousel_position_.at(idx) = static_cast(new_center_index); + carousel_target_.at(idx) = static_cast(new_center_index); + carousel_prev_index_.at(idx) = new_center_index; + } else { + // Calcular índice central del carrusel anterior + int prev_center_index = carousel_.at(idx).size() / 2; + + // Detectar si cambió (LEFT o RIGHT fue presionado) + if (new_center_index != prev_center_index) { + // Establecer nuevo target (la animación se hará en updateCarouselAnimation) + carousel_target_.at(idx) = static_cast(new_center_index); + } + + carousel_prev_index_.at(idx) = new_center_index; + } + + carousel_.at(idx) = carousel; +} + // Transforma un valor numérico en una cadena de 7 cifras auto Scoreboard::updateScoreText(int num) -> std::string { std::ostringstream oss; @@ -120,11 +154,41 @@ void Scoreboard::updateNameColorIndex() { animated_color_ = name_color_cycle_.at(name_color_index_ % name_color_cycle_.size()); } +// Actualiza la animación del carrusel +void Scoreboard::updateCarouselAnimation(float deltaTime) { + constexpr float CAROUSEL_SPEED = 8.0f; // Posiciones por segundo + + for (size_t i = 0; i < carousel_position_.size(); ++i) { + // Solo animar si no hemos llegado al target + if (std::abs(carousel_position_.at(i) - carousel_target_.at(i)) > 0.01f) { + // Determinar dirección + float direction = (carousel_target_.at(i) > carousel_position_.at(i)) ? 1.0f : -1.0f; + + // Calcular movimiento + float movement = CAROUSEL_SPEED * deltaTime * direction; + + // Mover, pero no sobrepasar el target + float new_position = carousel_position_.at(i) + movement; + + // Clamp para no sobrepasar + if (direction > 0) { + carousel_position_.at(i) = std::min(new_position, carousel_target_.at(i)); + } else { + carousel_position_.at(i) = std::max(new_position, carousel_target_.at(i)); + } + } else { + // Forzar al target exacto cuando estamos muy cerca + carousel_position_.at(i) = carousel_target_.at(i); + } + } +} + // Actualiza la lógica del marcador -void Scoreboard::update() { +void Scoreboard::update(float deltaTime) { fillBackgroundTexture(); updateTimeCounter(); updateNameColorIndex(); + updateCarouselAnimation(deltaTime); } // Pinta el marcador @@ -432,55 +496,75 @@ void Scoreboard::renderSeparator() { SDL_RenderLine(renderer_, 0, 0, rect_.w, 0); } -// Pinta el carrusel de caracteres con efecto de color LERP +// Pinta el carrusel de caracteres con efecto de color LERP y animación suave void Scoreboard::renderCarousel(size_t panel_index, int center_x, int y) { const std::string& carousel = carousel_.at(panel_index); if (carousel.empty()) { return; } - // Espacio extra entre letras (además del ancho natural de cada letra) + // Espacio extra entre letras constexpr int EXTRA_SPACING = 2; - // Índice de la letra central - const int CENTER_INDEX = carousel.size() / 2; + // Carrusel extendido: 9 letras visibles (4 izq + centro + 4 der) + constexpr int VISIBLE_LETTERS = 9; + constexpr int HALF_VISIBLE = VISIBLE_LETTERS / 2; // 4 - // Calcular el ancho acumulado antes de la letra central - int width_before_center = 0; - for (int i = 0; i < CENTER_INDEX; ++i) { - std::string ch(1, carousel[i]); - width_before_center += text_->length(ch, 1) + EXTRA_SPACING; - } + // Posición flotante actual del carrusel + const float carousel_pos = carousel_position_.at(panel_index); - // Calcular posición inicial para que la letra central esté centrada en center_x - std::string center_char(1, carousel[CENTER_INDEX]); - const int CENTER_CHAR_WIDTH = text_->length(center_char, 1); - const int CAROUSEL_START_X = center_x - width_before_center - (CENTER_CHAR_WIDTH / 2); + // Calcular ancho promedio de una letra (asumimos ancho uniforme para simplificar) + std::string sample_char(1, carousel[carousel.size() / 2]); + const int AVG_CHAR_WIDTH = text_->length(sample_char, 1); + const int CHAR_STEP = AVG_CHAR_WIDTH + EXTRA_SPACING; - // Pintar cada letra individualmente - int current_x = CAROUSEL_START_X; - for (size_t i = 0; i < carousel.size(); ++i) { - std::string single_char(1, carousel[i]); + // Calcular offset de píxeles basado en la parte fraccionaria de carousel_pos + const float fractional_offset = carousel_pos - std::floor(carousel_pos); + const int pixel_offset = static_cast(fractional_offset * CHAR_STEP); + // Índice base (centro del carrusel) + const int base_index = static_cast(std::floor(carousel_pos)); + + // Calcular posición X inicial (centrar el conjunto de 9 letras) + int start_x = center_x - (HALF_VISIBLE * CHAR_STEP) - (AVG_CHAR_WIDTH / 2) - pixel_offset; + + // Renderizar las 9 letras visibles + for (int i = -HALF_VISIBLE; i <= HALF_VISIBLE; ++i) { + // Índice real en la lista de caracteres (con wrap-around circular) + int char_index = base_index + i; + const int carousel_size = static_cast(carousel.size()); + + // Wrap-around circular + while (char_index < 0) { + char_index += carousel_size; + } + while (char_index >= carousel_size) { + char_index -= carousel_size; + } + + // Obtener el carácter + std::string single_char(1, carousel[char_index]); + + // Calcular distancia flotante al centro visual + const float distance_from_center = std::abs(static_cast(i) + fractional_offset); + + // Calcular color con LERP dinámico Color letter_color; - if (static_cast(i) == CENTER_INDEX) { - // Letra central: usa animated_color_ sin modificar + if (distance_from_center < 0.5f) { + // Letra central: animated_color_ letter_color = animated_color_; } else { - // Letras laterales: LERP desde text_color1_ hacia color_, pero sin llegar al 100% - const int DISTANCE = std::abs(static_cast(i) - CENTER_INDEX); - const float MAX_DISTANCE = static_cast(CENTER_INDEX); - // Limitar el fade al 70% para que las letras sigan siendo visibles - const float LERP_FACTOR = (MAX_DISTANCE > 0) ? ((DISTANCE / MAX_DISTANCE) * 0.7f) : 0.0f; - + // Letras laterales: LERP desde text_color1_ hacia color_ + // Factor: 0.0 cerca del centro, 0.85 en los extremos (85% fade) + const float LERP_FACTOR = std::min((distance_from_center - 0.5f) / (HALF_VISIBLE - 0.5f), 1.0f) * 0.85f; letter_color = text_color1_.LERP(color_, LERP_FACTOR); } - // Pintar la letra - text_->writeDX(Text::COLOR, current_x, y, single_char, 1, letter_color); + // Calcular posición X de esta letra + const int letter_x = start_x + (i + HALF_VISIBLE) * CHAR_STEP; - // Avanzar posición X: ancho del caracter + espacio extra - current_x += text_->length(single_char, 1) + EXTRA_SPACING; + // Pintar la letra + text_->writeDX(Text::COLOR, letter_x, y, single_char, 1, letter_color); } } \ No newline at end of file diff --git a/source/scoreboard.h b/source/scoreboard.h index e7e5041..39dc56f 100644 --- a/source/scoreboard.h +++ b/source/scoreboard.h @@ -50,8 +50,8 @@ class Scoreboard { static auto get() -> Scoreboard*; // Obtiene el puntero al objeto Scoreboard // --- Métodos principales --- - void update(); // Actualiza la lógica del marcador - void render(); // Pinta el marcador + void update(float deltaTime); // Actualiza la lógica del marcador + void render(); // Pinta el marcador // --- Setters --- void setColor(Color color); // Establece el color del marcador @@ -65,7 +65,7 @@ class Scoreboard { void setPower(float power) { power_ = power; } void setEnterName(Id id, const std::string& enter_name) { enter_name_.at(static_cast(id)) = enter_name; } void setCharacterSelected(Id id, const std::string& character_selected) { character_selected_.at(static_cast(id)) = character_selected; } - void setCarousel(Id id, const std::string& carousel) { carousel_.at(static_cast(id)) = carousel; } + void setCarousel(Id id, const std::string& carousel); // Implementación en .cpp para detectar cambios void setScore(Id id, int score) { score_.at(static_cast(id)) = score; } void setSelectorPos(Id id, int pos) { selector_pos_.at(static_cast(id)) = pos; } void setStage(int stage) { stage_ = stage; } @@ -84,6 +84,9 @@ class Scoreboard { std::array(Id::SIZE)> enter_name_ = {}; // Nombre introducido para la tabla de records std::array(Id::SIZE)> character_selected_ = {}; // Caracter seleccionado std::array(Id::SIZE)> carousel_ = {}; // Caracter seleccionado + std::array(Id::SIZE)> carousel_position_ = {}; // Posición actual del carrusel (animación suave) + std::array(Id::SIZE)> carousel_target_ = {}; // Posición objetivo del carrusel + std::array(Id::SIZE)> carousel_prev_index_ = {}; // Índice previo para detectar cambios std::array(Id::SIZE)> panel_ = {}; // Lista con todos los paneles del marcador Colors::Cycle name_color_cycle_; // Ciclo de colores para destacar el nombre una vez introducido Color animated_color_; // Color actual animado (ciclo automático cada 100ms) @@ -118,6 +121,7 @@ class Scoreboard { void fillBackgroundTexture(); // Rellena la textura de fondo void updateTimeCounter(); // Actualiza el contador void updateNameColorIndex(); // Actualiza el índice del color animado del nombre + void updateCarouselAnimation(float deltaTime); // Actualiza la animación del carrusel void renderSeparator(); // Dibuja la línea que separa la zona de juego del marcador void renderPanelContent(size_t panel_index); void renderScoreMode(size_t panel_index); diff --git a/source/sections/game.cpp b/source/sections/game.cpp index 09e5cd3..07f9c61 100644 --- a/source/sections/game.cpp +++ b/source/sections/game.cpp @@ -344,7 +344,7 @@ void Game::updateStage() { void Game::updateGameStateGameOver(float deltaTime) { fade_out_->update(); updatePlayers(deltaTime); - updateScoreboard(); + updateScoreboard(deltaTime); updateBackground(deltaTime); balloon_manager_->update(deltaTime); tabe_->update(deltaTime); @@ -380,7 +380,7 @@ void Game::updateGameStateGameOver(float deltaTime) { // Gestiona eventos para el estado del final del juego void Game::updateGameStateCompleted(float deltaTime) { updatePlayers(deltaTime); - updateScoreboard(); + updateScoreboard(deltaTime); updateBackground(deltaTime); balloon_manager_->update(deltaTime); tabe_->update(deltaTime); @@ -1145,7 +1145,7 @@ void Game::handleEvents() { } // Actualiza el marcador -void Game::updateScoreboard() { +void Game::updateScoreboard(float deltaTime) { for (const auto& player : players_) { scoreboard_->setScore(player->getScoreBoardPanel(), player->getScore()); scoreboard_->setMult(player->getScoreBoardPanel(), player->getScoreMultiplier()); @@ -1157,7 +1157,7 @@ void Game::updateScoreboard() { scoreboard_->setHiScore(hi_score_.score); scoreboard_->setHiScoreName(hi_score_.name); - scoreboard_->update(); + scoreboard_->update(deltaTime); } // Pone en el marcador el nombre del primer jugador de la tabla @@ -1743,7 +1743,7 @@ void Game::updateRecording(float deltaTime) { // Actualiza las variables durante dicho estado void Game::updateGameStateFadeIn(float deltaTime) { fade_in_->update(); - updateScoreboard(); + updateScoreboard(deltaTime); updateBackground(deltaTime); if (fade_in_->hasEnded()) { setState(State::ENTERING_PLAYER); @@ -1756,7 +1756,7 @@ void Game::updateGameStateFadeIn(float deltaTime) { void Game::updateGameStateEnteringPlayer(float deltaTime) { balloon_manager_->update(deltaTime); updatePlayers(deltaTime); - updateScoreboard(); + updateScoreboard(deltaTime); updateBackground(deltaTime); for (const auto& player : players_) { if (player->isPlaying()) { @@ -1790,7 +1790,7 @@ void Game::updateGameStatePlaying(float deltaTime) { #endif updatePlayers(deltaTime); checkPlayersStatusPlaying(); - updateScoreboard(); + updateScoreboard(deltaTime); updateBackground(deltaTime); balloon_manager_->update(deltaTime); tabe_->update(deltaTime); diff --git a/source/sections/game.h b/source/sections/game.h index edcb6a4..a35e21a 100644 --- a/source/sections/game.h +++ b/source/sections/game.h @@ -316,9 +316,9 @@ class Game { void setMenace(); // Calcula y establece amenaza según globos activos // --- Puntuación y marcador --- - void updateHiScore(); // Actualiza el récord máximo si es necesario - void updateScoreboard(); // Actualiza la visualización del marcador - void updateHiScoreName(); // Pone en el marcador el nombre del primer jugador de la tabla + void updateHiScore(); // Actualiza el récord máximo si es necesario + void updateScoreboard(float deltaTime); // Actualiza la visualización del marcador + void updateHiScoreName(); // Pone en el marcador el nombre del primer jugador de la tabla void initScoreboard(); // Inicializa el sistema de puntuación // --- Modo demostración ---