From 85d34fb9077cdf04ae3cd834a0d520d823418b75 Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Sat, 8 Nov 2025 00:10:16 +0100 Subject: [PATCH] style: afegides varies animacions amb easing per al Logo --- source/core/system/director.cpp | 2 +- source/game/scene_manager.hpp | 2 +- source/game/scenes/logo.cpp | 73 ++++---- source/game/scenes/logo.hpp | 15 +- source/utils/easing_functions.hpp | 273 ++++++++++++++++++++++++++++++ 5 files changed, 327 insertions(+), 38 deletions(-) create mode 100644 source/utils/easing_functions.hpp diff --git a/source/core/system/director.cpp b/source/core/system/director.cpp index 5c3cb04..b6e77fd 100644 --- a/source/core/system/director.cpp +++ b/source/core/system/director.cpp @@ -334,7 +334,7 @@ auto Director::run() -> int { break; case SceneManager::Scene::LOADING_SCREEN: - runLoadingScreen(); + runLogo(); break; case SceneManager::Scene::TITLE: diff --git a/source/game/scene_manager.hpp b/source/game/scene_manager.hpp index 9ee627a..f489c53 100644 --- a/source/game/scene_manager.hpp +++ b/source/game/scene_manager.hpp @@ -34,7 +34,7 @@ enum class Options { // --- Variables de estado globales --- #ifdef _DEBUG -inline Scene current = Scene::GAME; // Escena actual +inline Scene current = Scene::LOGO; // Escena actual inline Options options = Options::LOGO_TO_LOADING_SCREEN; // Opciones de la escena actual #else inline Scene current = Scene::LOGO; // Escena actual diff --git a/source/game/scenes/logo.cpp b/source/game/scenes/logo.cpp index 7882421..7c07405 100644 --- a/source/game/scenes/logo.cpp +++ b/source/game/scenes/logo.cpp @@ -3,6 +3,8 @@ #include #include // Para std::clamp +#include // Para std::array +#include // Para generador aleatorio #include "core/audio/audio.hpp" // Para Audio #include "core/input/global_inputs.hpp" // Para check @@ -15,6 +17,7 @@ #include "game/options.hpp" // Para Options, SectionState, options, Section #include "game/scene_manager.hpp" // Para SceneManager #include "utils/delta_timer.hpp" // Para DeltaTimer +#include "utils/easing_functions.hpp" // Para funciones de suavizado #include "utils/utils.hpp" // Para PaletteColor // Constructor @@ -34,6 +37,19 @@ Logo::Logo() initSprites(); // Crea los sprites de cada linea initColors(); // Inicializa el vector de colores + // Seleccionar función de easing aleatoria para la animación del logo + // Usamos lambdas para funciones con parámetros opcionales + static const std::array EASING_OPTIONS = { + [](float t) { return Easing::backOut(t); }, // Overshoot retro + [](float t) { return Easing::elasticOut(t); }, // Rebote múltiple con oscilación + Easing::bounceOut, // Rebote físico decreciente + Easing::cubicOut // Suavizado sin overshoot (para variedad) + }; + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution dist(0, EASING_OPTIONS.size() - 1); + easing_function_ = EASING_OPTIONS[dist(gen)]; + // Cambia el color del borde Screen::get()->setBorderColor(static_cast(PaletteColor::BLACK)); } @@ -59,41 +75,24 @@ void Logo::updateJAILGAMES(float delta_time) { return; } - // Calcular el desplazamiento basado en velocidad y delta time - const float DISPLACEMENT = JAILGAMES_SLIDE_SPEED * delta_time; + // Calcular el progreso de la animación (0.0 a 1.0) + const float progress = std::clamp(state_time_ / JAILGAMES_SLIDE_DURATION, 0.0F, 1.0F); - // Actualizar cada línea del sprite JAILGAMES + // Aplicar función de suavizado seleccionada aleatoriamente (permite overshoot para efecto de rebote) + // La posición final exacta se garantiza en updateState() antes de transicionar + const float eased_progress = easing_function_(progress); + + // Actualizar cada línea del sprite JAILGAMES interpolando con easing for (size_t i = 0; i < jailgames_sprite_.size(); ++i) { - const float CURRENT_X = jailgames_sprite_[i]->getX(); + // Interpolar entre posición inicial y destino usando el progreso suavizado + const float initial_x = static_cast(jailgames_initial_x_[i]); + const float dest_x = static_cast(JAILGAMES_DEST_X); + const float new_x = initial_x + (dest_x - initial_x) * eased_progress; - // Las líneas pares se mueven desde la derecha, las impares desde la izquierda - if (i % 2 == 0) { - // Mover hacia la izquierda - if (CURRENT_X > JAILGAMES_DEST_X) { - const float NEW_X = CURRENT_X - DISPLACEMENT; - jailgames_sprite_[i]->setX(NEW_X < JAILGAMES_DEST_X ? JAILGAMES_DEST_X : NEW_X); - } - } else { - // Mover hacia la derecha - if (CURRENT_X < JAILGAMES_DEST_X) { - const float NEW_X = CURRENT_X + DISPLACEMENT; - jailgames_sprite_[i]->setX(NEW_X > JAILGAMES_DEST_X ? JAILGAMES_DEST_X : NEW_X); - } - } + jailgames_sprite_[i]->setX(new_x); } } -// Verifica si todas las líneas están en su posición destino -auto Logo::allJailgamesLinesInPosition() const -> bool { - // Iterar por todas las líneas (empezando desde 1, como en updateJAILGAMES) - for (size_t i = 0; i < jailgames_sprite_.size(); ++i) { - if (jailgames_sprite_[i]->getX() != JAILGAMES_DEST_X) { - return false; // Al menos una línea no ha llegado - } - } - return true; // Todas las líneas están en posición -} - // Calcula el índice de color según el progreso (0.0-1.0) auto Logo::getColorIndex(float progress) const -> int { // Asegurar que progress esté en el rango [0.0, 1.0] @@ -157,7 +156,12 @@ void Logo::updateState(float delta_time) { break; case State::JAILGAMES_SLIDE_IN: - if (allJailgamesLinesInPosition()) { + if (state_time_ >= JAILGAMES_SLIDE_DURATION) { + // Garantizar que todas las líneas estén exactamente en la posición final + // antes de transicionar (previene race condition con updateJAILGAMES) + for (auto& sprite : jailgames_sprite_) { + sprite->setX(JAILGAMES_DEST_X); + } transitionToState(State::SINCE_1998_FADE_IN); } break; @@ -263,10 +267,17 @@ void Logo::initColors() { // Crea los sprites de cada linea void Logo::initSprites() { const float WIDTH = jailgames_surface_->getWidth(); + jailgames_initial_x_.reserve(jailgames_surface_->getHeight()); + for (int i = 0; i < jailgames_surface_->getHeight(); ++i) { jailgames_sprite_.push_back(std::make_shared(jailgames_surface_, 0, i, jailgames_surface_->getWidth(), 1)); jailgames_sprite_.back()->setClip(0, i, jailgames_surface_->getWidth(), 1); - jailgames_sprite_.at(i)->setX((i % 2 == 0) ? (256 + (i * 3)) : (-WIDTH - (i * 3))); + + // Calcular posición inicial (alternando entre derecha e izquierda) + const int initial_x = (i % 2 == 0) ? (256 + (i * 3)) : (static_cast(-WIDTH) - (i * 3)); + jailgames_initial_x_.push_back(initial_x); + + jailgames_sprite_.at(i)->setX(initial_x); jailgames_sprite_.at(i)->setY(83 + i); } } \ No newline at end of file diff --git a/source/game/scenes/logo.hpp b/source/game/scenes/logo.hpp index a92b0eb..d39d2b0 100644 --- a/source/game/scenes/logo.hpp +++ b/source/game/scenes/logo.hpp @@ -2,8 +2,9 @@ #include -#include // Para shared_ptr -#include // Para vector +#include // Para std::function +#include // Para shared_ptr +#include // Para vector #include "utils/delta_timer.hpp" // Para DeltaTimer class SurfaceSprite; // Forward declaration @@ -11,6 +12,9 @@ class Surface; // Forward declaration class Logo { public: + // --- Tipos --- + using EasingFunction = std::function; // Función de easing (permite lambdas) + // --- Enumeraciones --- enum class State { INITIAL, // Espera inicial @@ -36,13 +40,14 @@ class Logo { static constexpr float FADE_OUT_DURATION = 0.5F; // Duración del fade-out final // --- Constantes de animación --- - static constexpr float JAILGAMES_SLIDE_SPEED = 350.0F; // Velocidad de slide-in (pixels/segundo) - static constexpr int JAILGAMES_DEST_X = 37; // Posición X de destino para JAILGAMES + static constexpr float JAILGAMES_SLIDE_DURATION = 0.8F; // Duración de la animación de slide-in (segundos) + static constexpr int JAILGAMES_DEST_X = 37; // Posición X de destino para JAILGAMES // --- Objetos y punteros --- std::shared_ptr jailgames_surface_ = nullptr; // Textura con los graficos "JAILGAMES" std::shared_ptr since_1998_surface_ = nullptr; // Textura con los graficos "Since 1998" std::vector> jailgames_sprite_; // Vector con los sprites de cada linea que forman el bitmap JAILGAMES + std::vector jailgames_initial_x_; // Posiciones X iniciales de cada línea (para interpolación con easing) std::shared_ptr since_1998_sprite_ = nullptr; // SSprite para manejar la textura2 Uint8 jailgames_color_ = 0; // Color para el sprite de "JAILGAMES" Uint8 since_1998_color_ = 0; // Color para el sprite de "Since 1998" @@ -52,6 +57,7 @@ class Logo { std::unique_ptr delta_timer_ = nullptr; // Timer para delta time State state_ = State::INITIAL; // Estado actual de la secuencia float state_time_ = 0.0F; // Tiempo acumulado en el estado actual + EasingFunction easing_function_; // Función de easing para la animación del logo // --- Funciones --- void update(); // Actualiza las variables @@ -63,7 +69,6 @@ class Logo { void updateState(float delta_time); // Actualiza el estado actual void transitionToState(State new_state); // Transiciona a un nuevo estado [[nodiscard]] auto getColorIndex(float progress) const -> int; // Calcula el índice de color según el progreso (0.0-1.0) - [[nodiscard]] auto allJailgamesLinesInPosition() const -> bool; // Verifica si todas las líneas están en su posición destino static void endSection(); // Termina la sección void initColors(); // Inicializa el vector de colores void initSprites(); // Crea los sprites de cada linea diff --git a/source/utils/easing_functions.hpp b/source/utils/easing_functions.hpp new file mode 100644 index 0000000..5707fa9 --- /dev/null +++ b/source/utils/easing_functions.hpp @@ -0,0 +1,273 @@ +/** + * @file easing_functions.hpp + * @brief Colección de funciones de suavizado (easing) para animaciones + * + * Todas las funciones toman un parámetro t (0.0 a 1.0) que representa + * el progreso de la animación y retornan el valor suavizado. + * + * Convenciones: + * - In: Aceleración (slow -> fast) + * - Out: Desaceleración (fast -> slow) + * - InOut: Aceleración + Desaceleración (slow -> fast -> slow) + */ + +#ifndef EASING_FUNCTIONS_HPP +#define EASING_FUNCTIONS_HPP + +#include + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +namespace Easing { + +// ============================================================================ +// LINEAR +// ============================================================================ + +inline float linear(float t) { + return t; +} + +// ============================================================================ +// QUAD (Cuadrática: t^2) +// ============================================================================ + +inline float quadIn(float t) { + return t * t; +} + +inline float quadOut(float t) { + return t * (2.0F - t); +} + +inline float quadInOut(float t) { + if (t < 0.5F) { + return 2.0F * t * t; + } + return -1.0F + (4.0F - 2.0F * t) * t; +} + +// ============================================================================ +// CUBIC (Cúbica: t^3) +// ============================================================================ + +inline float cubicIn(float t) { + return t * t * t; +} + +inline float cubicOut(float t) { + const float f = t - 1.0F; + return f * f * f + 1.0F; +} + +inline float cubicInOut(float t) { + if (t < 0.5F) { + return 4.0F * t * t * t; + } + const float f = (2.0F * t - 2.0F); + return 0.5F * f * f * f + 1.0F; +} + +// ============================================================================ +// QUART (Cuártica: t^4) +// ============================================================================ + +inline float quartIn(float t) { + return t * t * t * t; +} + +inline float quartOut(float t) { + const float f = t - 1.0F; + return 1.0F - f * f * f * f; +} + +inline float quartInOut(float t) { + if (t < 0.5F) { + return 8.0F * t * t * t * t; + } + const float f = t - 1.0F; + return 1.0F - 8.0F * f * f * f * f; +} + +// ============================================================================ +// QUINT (Quíntica: t^5) +// ============================================================================ + +inline float quintIn(float t) { + return t * t * t * t * t; +} + +inline float quintOut(float t) { + const float f = t - 1.0F; + return f * f * f * f * f + 1.0F; +} + +inline float quintInOut(float t) { + if (t < 0.5F) { + return 16.0F * t * t * t * t * t; + } + const float f = (2.0F * t - 2.0F); + return 0.5F * f * f * f * f * f + 1.0F; +} + +// ============================================================================ +// SINE (Sinusoidal) +// ============================================================================ + +inline float sineIn(float t) { + return 1.0F - std::cos(t * static_cast(M_PI) * 0.5F); +} + +inline float sineOut(float t) { + return std::sin(t * static_cast(M_PI) * 0.5F); +} + +inline float sineInOut(float t) { + return 0.5F * (1.0F - std::cos(static_cast(M_PI) * t)); +} + +// ============================================================================ +// EXPO (Exponencial) +// ============================================================================ + +inline float expoIn(float t) { + if (t == 0.0F) return 0.0F; + return std::pow(2.0F, 10.0F * (t - 1.0F)); +} + +inline float expoOut(float t) { + if (t == 1.0F) return 1.0F; + return 1.0F - std::pow(2.0F, -10.0F * t); +} + +inline float expoInOut(float t) { + if (t == 0.0F || t == 1.0F) return t; + + if (t < 0.5F) { + return 0.5F * std::pow(2.0F, (20.0F * t) - 10.0F); + } + return 0.5F * (2.0F - std::pow(2.0F, -20.0F * t + 10.0F)); +} + +// ============================================================================ +// CIRC (Circular) +// ============================================================================ + +inline float circIn(float t) { + return 1.0F - std::sqrt(1.0F - t * t); +} + +inline float circOut(float t) { + const float f = t - 1.0F; + return std::sqrt(1.0F - f * f); +} + +inline float circInOut(float t) { + if (t < 0.5F) { + return 0.5F * (1.0F - std::sqrt(1.0F - 4.0F * t * t)); + } + const float f = 2.0F * t - 2.0F; + return 0.5F * (std::sqrt(1.0F - f * f) + 1.0F); +} + +// ============================================================================ +// BACK (Overshoot - retrocede antes de avanzar) +// ============================================================================ + +inline float backIn(float t, float overshoot = 1.70158F) { + return t * t * ((overshoot + 1.0F) * t - overshoot); +} + +inline float backOut(float t, float overshoot = 1.70158F) { + const float f = t - 1.0F; + return f * f * ((overshoot + 1.0F) * f + overshoot) + 1.0F; +} + +inline float backInOut(float t, float overshoot = 1.70158F) { + const float s = overshoot * 1.525F; + + if (t < 0.5F) { + const float f = 2.0F * t; + return 0.5F * (f * f * ((s + 1.0F) * f - s)); + } + + const float f = 2.0F * t - 2.0F; + return 0.5F * (f * f * ((s + 1.0F) * f + s) + 2.0F); +} + +// ============================================================================ +// ELASTIC (Oscilación elástica - efecto de resorte) +// ============================================================================ + +inline float elasticIn(float t, float amplitude = 1.0F, float period = 0.3F) { + if (t == 0.0F || t == 1.0F) return t; + + const float s = period / (2.0F * static_cast(M_PI)) * std::asin(1.0F / amplitude); + const float f = t - 1.0F; + return -(amplitude * std::pow(2.0F, 10.0F * f) * + std::sin((f - s) * (2.0F * static_cast(M_PI)) / period)); +} + +inline float elasticOut(float t, float amplitude = 1.0F, float period = 0.3F) { + if (t == 0.0F || t == 1.0F) return t; + + const float s = period / (2.0F * static_cast(M_PI)) * std::asin(1.0F / amplitude); + return amplitude * std::pow(2.0F, -10.0F * t) * + std::sin((t - s) * (2.0F * static_cast(M_PI)) / period) + 1.0F; +} + +inline float elasticInOut(float t, float amplitude = 1.0F, float period = 0.3F) { + if (t == 0.0F || t == 1.0F) return t; + + const float s = period / (2.0F * static_cast(M_PI)) * std::asin(1.0F / amplitude); + + if (t < 0.5F) { + const float f = 2.0F * t - 1.0F; + return -0.5F * (amplitude * std::pow(2.0F, 10.0F * f) * + std::sin((f - s) * (2.0F * static_cast(M_PI)) / period)); + } + + const float f = 2.0F * t - 1.0F; + return 0.5F * amplitude * std::pow(2.0F, -10.0F * f) * + std::sin((f - s) * (2.0F * static_cast(M_PI)) / period) + 1.0F; +} + +// ============================================================================ +// BOUNCE (Rebote - simula física de rebote) +// ============================================================================ + +inline float bounceOut(float t) { + const float n1 = 7.5625F; + const float d1 = 2.75F; + + if (t < 1.0F / d1) { + return n1 * t * t; + } + if (t < 2.0F / d1) { + const float f = t - 1.5F / d1; + return n1 * f * f + 0.75F; + } + if (t < 2.5F / d1) { + const float f = t - 2.25F / d1; + return n1 * f * f + 0.9375F; + } + const float f = t - 2.625F / d1; + return n1 * f * f + 0.984375F; +} + +inline float bounceIn(float t) { + return 1.0F - bounceOut(1.0F - t); +} + +inline float bounceInOut(float t) { + if (t < 0.5F) { + return 0.5F * bounceIn(2.0F * t); + } + return 0.5F * bounceOut(2.0F * t - 1.0F) + 0.5F; +} + +} // namespace Easing + +#endif // EASING_FUNCTIONS_HPP