From 6f31751d42d4bb4ae249290d0f32eb0735576ec1 Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Fri, 3 Apr 2026 18:24:58 +0200 Subject: [PATCH] aproximant-se --- source/card_sprite.cpp | 51 +++++++++++++++++------- source/card_sprite.hpp | 18 ++++++--- source/sections/intro.cpp | 81 +++++++++++++++++++++++---------------- source/sections/intro.hpp | 3 +- 4 files changed, 100 insertions(+), 53 deletions(-) diff --git a/source/card_sprite.cpp b/source/card_sprite.cpp index 12326d5..c01e0e9 100644 --- a/source/card_sprite.cpp +++ b/source/card_sprite.cpp @@ -5,7 +5,7 @@ #include // Para move #include "texture.hpp" // Para Texture -#include "utils.hpp" // Para easeOutBounce +#include "utils.hpp" // Para easeOutBounce, easeOutCubic // Constructor CardSprite::CardSprite(std::shared_ptr texture) @@ -20,9 +20,10 @@ void CardSprite::enable() { state_ = CardState::ENTERING; entry_elapsed_ = 0.0F; + first_touch_ = false; - // Posición fija en el punto de aterrizaje - setPos(landing_x_, landing_y_); + // Posición inicial (borde de pantalla) + setPos(entry_start_x_, entry_start_y_); // Zoom inicial grande (como si estuviera cerca de la cámara) horizontal_zoom_ = start_zoom_; @@ -70,7 +71,7 @@ void CardSprite::update(float delta_time) { } } -// Animación de entrada: interpola zoom y ángulo con easing +// Animación de entrada: interpola posición, zoom y ángulo void CardSprite::updateEntering(float delta_time) { entry_elapsed_ += delta_time; @@ -85,15 +86,26 @@ void CardSprite::updateEntering(float delta_time) { // Ángulo: de start_angle_ a 0 con rebote rotate_.angle = start_angle_ * (1.0 - eased); - // Offset de sombra escalado con el zoom (perspectiva) - // (se aplica en renderShadow) + // Posición: de entry_start a landing con easing suave (sin rebote) + // Usamos easeOutCubic para que el desplazamiento sea fluido + double pos_eased = easeOutCubic(static_cast(progress)); + auto current_x = static_cast(entry_start_x_ + (landing_x_ - entry_start_x_) * pos_eased); + auto current_y = static_cast(entry_start_y_ + (landing_y_ - entry_start_y_) * pos_eased); + setPos(current_x, current_y); - // Transición a LANDED cuando termina la animación + // Detecta el primer toque (cuando el easing alcanza ~1.0 por primera vez) + if (!first_touch_ && eased >= FIRST_TOUCH_THRESHOLD) { + first_touch_ = true; + } + + // Transición a LANDED cuando termina la animación completa if (progress >= 1.0F) { horizontal_zoom_ = 1.0F; vertical_zoom_ = 1.0F; rotate_.angle = 0.0; + setPos(landing_x_, landing_y_); state_ = CardState::LANDED; + first_touch_ = true; } } @@ -122,18 +134,23 @@ void CardSprite::render() { MovingSprite::render(); } -// Renderiza la sombra con el mismo zoom y rotación que la tarjeta +// Renderiza la sombra con efecto de perspectiva +// Cuanto más alta la tarjeta (zoom grande), la sombra es más pequeña y más separada. +// Cuando la tarjeta está en la mesa (zoom=1.0), la sombra tiene tamaño real y offset base. void CardSprite::renderShadow() { - // Offset de sombra escalado con el zoom para efecto de perspectiva + // La sombra siempre está en la mesa: su escala es inversamente proporcional al zoom + float shadow_zoom = 1.0F / horizontal_zoom_; + + // El offset aumenta con la altura (más lejos de la tarjeta cuanto más alta) float scaled_offset_x = shadow_offset_x_ * horizontal_zoom_; - float scaled_offset_y = shadow_offset_y_ * vertical_zoom_; + float scaled_offset_y = shadow_offset_y_ * horizontal_zoom_; shadow_texture_->render( pos_.x + scaled_offset_x, pos_.y + scaled_offset_y, &sprite_clip_, - horizontal_zoom_, - vertical_zoom_, + shadow_zoom, + shadow_zoom, rotate_.angle, &rotate_.center, flip_); @@ -141,7 +158,6 @@ void CardSprite::renderShadow() { // Comprueba si el sprite está fuera de pantalla auto CardSprite::isOffScreen() const -> bool { - // Considerar el zoom: el sprite puede ser más grande de lo que indica pos_ float effective_width = pos_.w * horizontal_zoom_; float effective_height = pos_.h * vertical_zoom_; return (pos_.x + effective_width < -OFF_SCREEN_MARGIN || @@ -155,6 +171,10 @@ auto CardSprite::hasLanded() const -> bool { return state_ == CardState::LANDED || state_ == CardState::EXITING || state_ == CardState::FINISHED; } +auto CardSprite::hasFirstTouch() const -> bool { + return first_touch_; +} + auto CardSprite::hasFinished() const -> bool { return state_ == CardState::FINISHED; } @@ -175,6 +195,11 @@ void CardSprite::setEntryParams(float start_zoom, double start_angle, float dura entry_easing_ = std::move(easing); } +void CardSprite::setEntryPosition(float start_x, float start_y) { + entry_start_x_ = start_x; + entry_start_y_ = start_y; +} + void CardSprite::setLandingPosition(float x, float y) { landing_x_ = x; landing_y_ = y; diff --git a/source/card_sprite.hpp b/source/card_sprite.hpp index 1e3a85c..fff894d 100644 --- a/source/card_sprite.hpp +++ b/source/card_sprite.hpp @@ -12,7 +12,7 @@ class Texture; // --- Estados de la tarjeta --- enum class CardState { IDLE, // No activada todavía - ENTERING, // Animación de entrada (zoom + rotación con rebote) + ENTERING, // Animación de entrada (zoom + rotación + desplazamiento con rebote) LANDED, // En reposo sobre la mesa EXITING, // Saliendo de pantalla girando FINISHED, // Fuera de pantalla @@ -20,8 +20,8 @@ enum class CardState { // --- Clase CardSprite: tarjeta animada con zoom, rotación y sombra integrada --- // -// Simula una tarjeta lanzada sobre una mesa desde arriba (eje Z). -// Durante la entrada, interpola zoom y rotación con easing (rebote). +// Simula una tarjeta lanzada sobre una mesa desde un borde de la pantalla. +// Durante la entrada, interpola posición, zoom y rotación con easing (rebote). // Durante la salida, se desplaza fuera de pantalla girando, sin sombra. class CardSprite : public MovingSprite { public: @@ -37,14 +37,16 @@ class CardSprite : public MovingSprite { void startExit(); // Inicia la animación de salida // --- Consultas de estado --- - [[nodiscard]] auto hasLanded() const -> bool; // ¿Ha aterrizado en la mesa? + [[nodiscard]] auto hasLanded() const -> bool; // ¿Ha aterrizado definitivamente? + [[nodiscard]] auto hasFirstTouch() const -> bool; // ¿Ha tocado la mesa por primera vez? (primer rebote) [[nodiscard]] auto hasFinished() const -> bool; // ¿Ha terminado completamente? [[nodiscard]] auto isExiting() const -> bool; // ¿Está saliendo de pantalla? [[nodiscard]] auto getState() const -> CardState; // Estado actual // --- Configuración de entrada --- void setEntryParams(float start_zoom, double start_angle, float duration_s, std::function easing); - void setLandingPosition(float x, float y); // Posición final centrada + void setEntryPosition(float start_x, float start_y); // Posición inicial (borde de pantalla) + void setLandingPosition(float x, float y); // Posición final centrada // --- Configuración de salida --- void setExitParams(float vx, float vy, float ax, float ay, double rotate_amount); @@ -59,6 +61,10 @@ class CardSprite : public MovingSprite { private: // --- Estado --- CardState state_ = CardState::IDLE; + bool first_touch_ = false; // Primer contacto con la mesa (eased >= umbral) + + // --- Umbral para detectar el primer toque --- + static constexpr double FIRST_TOUCH_THRESHOLD = 0.98; // --- Parámetros de entrada --- float start_zoom_ = 1.8F; @@ -66,6 +72,8 @@ class CardSprite : public MovingSprite { float entry_duration_s_ = 1.5F; float entry_elapsed_ = 0.0F; std::function entry_easing_; + float entry_start_x_ = 0.0F; // Posición inicial X (borde) + float entry_start_y_ = 0.0F; // Posición inicial Y (borde) float landing_x_ = 0.0F; float landing_y_ = 0.0F; diff --git a/source/sections/intro.cpp b/source/sections/intro.cpp index e9e9a3e..11fb9ef 100644 --- a/source/sections/intro.cpp +++ b/source/sections/intro.cpp @@ -57,6 +57,11 @@ void Intro::checkInput() { // Actualiza las escenas de la intro void Intro::updateScenes() { + // Cuando la tarjeta actual toca la mesa por primera vez, la anterior sale despedida + if (scene_ > 0 && card_sprites_.at(scene_)->hasFirstTouch()) { + card_sprites_.at(scene_ - 1)->startExit(); + } + switch (scene_) { case 0: updateScene0(); @@ -103,7 +108,7 @@ void Intro::updateScene0() { // Fin de la primera escena: la tarjeta sale despedida if (texts_.at(2)->hasFinished()) { texts_.at(2)->setEnabled(false); - startCardExitAndAdvance(); + scene_++; } } @@ -119,7 +124,7 @@ void Intro::updateScene1() { // Fin de la segunda escena if (texts_.at(3)->hasFinished()) { texts_.at(3)->setEnabled(false); - startCardExitAndAdvance(); + scene_++; } } @@ -133,7 +138,7 @@ void Intro::updateScene2() { // Fin de la tercera escena if (card_sprites_.at(2)->hasLanded() && texts_.at(4)->hasFinished()) { texts_.at(4)->setEnabled(false); - startCardExitAndAdvance(); + scene_++; } } @@ -153,7 +158,7 @@ void Intro::updateScene3() { // Fin de la cuarta escena if (card_sprites_.at(3)->hasLanded() && texts_.at(6)->hasFinished()) { texts_.at(6)->setEnabled(false); - startCardExitAndAdvance(); + scene_++; } } @@ -168,7 +173,7 @@ void Intro::updateScene4() { // Fin de la quinta escena if (card_sprites_.at(4)->hasLanded() && texts_.at(7)->hasFinished()) { texts_.at(7)->setEnabled(false); - startCardExitAndAdvance(); + scene_++; } } @@ -193,12 +198,6 @@ void Intro::updateScene5() { } } -// Inicia la salida de la tarjeta actual y avanza a la siguiente escena -void Intro::startCardExitAndAdvance() { - card_sprites_.at(scene_)->startExit(); - scene_++; -} - void Intro::switchText(int from_index, int to_index) { texts_.at(from_index)->setEnabled(false); texts_.at(to_index)->setEnabled(true); @@ -347,26 +346,38 @@ void Intro::initSprites() { const float X_DEST = param.game.game_area.center_x - (CARD_WIDTH / 2); const float Y_DEST = param.game.game_area.first_quarter_y - (CARD_HEIGHT / 4); - // Ángulos de entrada por tarjeta (variedad visual) - const double ENTRY_ANGLES[] = {CARD_ANGLE_0, CARD_ANGLE_1, CARD_ANGLE_2, CARD_ANGLE_3, CARD_ANGLE_4, CARD_ANGLE_5}; - - // Direcciones de salida: cada tarjeta sale en una dirección diferente - // {vx, vy, ax, ay, rotate_amount} - struct ExitConfig { - float vx; - float vy; - float ax; - float ay; - double rotate_amount; + // Configuración por tarjeta: posición de entrada, ángulo, salida + // Cada tarjeta viene de un borde diferente (gente alrededor de una mesa lanzando cartas al centro) + struct CardConfig { + float entry_x; // Posición inicial X + float entry_y; // Posición inicial Y + double entry_angle; // Ángulo de entrada + float exit_vx; // Velocidad de salida X + float exit_vy; // Velocidad de salida Y + float exit_ax; // Aceleración de salida X + float exit_ay; // Aceleración de salida Y + double exit_rotation; // Velocidad de rotación de salida }; - const ExitConfig EXIT_CONFIGS[] = { - { CARD_EXIT_SPEED, -CARD_EXIT_SPEED * 0.15F, CARD_EXIT_ACCEL, 0.0F, CARD_EXIT_ROTATION}, // 0: Derecha + leve arriba - {-CARD_EXIT_SPEED, CARD_EXIT_SPEED * 0.25F, -CARD_EXIT_ACCEL, CARD_EXIT_ACCEL * 0.2F, -CARD_EXIT_ROTATION * 1.1}, // 1: Izquierda + abajo - { CARD_EXIT_SPEED, -CARD_EXIT_SPEED * 0.4F, CARD_EXIT_ACCEL, -CARD_EXIT_ACCEL * 0.3F, CARD_EXIT_ROTATION * 0.8}, // 2: Derecha + arriba - {-CARD_EXIT_SPEED, -CARD_EXIT_SPEED * 0.2F, -CARD_EXIT_ACCEL, 0.0F, -CARD_EXIT_ROTATION}, // 3: Izquierda + leve arriba - { CARD_EXIT_SPEED * 0.2F, CARD_EXIT_SPEED, 0.0F, CARD_EXIT_ACCEL, CARD_EXIT_ROTATION * 1.2}, // 4: Abajo + leve derecha - {-CARD_EXIT_SPEED * 0.6F, -CARD_EXIT_SPEED * 0.1F, -CARD_EXIT_ACCEL * 0.5F, 0.0F, -CARD_EXIT_ROTATION * 0.7}, // 5: Izquierda suave (viento) + const float W = param.game.width; + const float H = param.game.height; + const float S = CARD_EXIT_SPEED; + const float A = CARD_EXIT_ACCEL; + const double R = CARD_EXIT_ROTATION; + + const CardConfig CARD_CONFIGS[] = { + // 0: Entra desde la izquierda. La 1 entra desde la derecha → sale empujada hacia la izquierda + {-CARD_WIDTH, Y_DEST - 20.0F, CARD_ANGLE_0, -S, S * 0.1F, -A, 0.0F, -R}, + // 1: Entra desde la derecha. La 2 entra desde arriba → sale empujada hacia abajo + {W + CARD_WIDTH, Y_DEST + 15.0F, CARD_ANGLE_1, S * 0.15F, S, 0.0F, A, R * 1.1}, + // 2: Entra desde arriba. La 3 entra desde abajo → sale empujada hacia arriba + {X_DEST + 30.0F, -CARD_HEIGHT, CARD_ANGLE_2, -S * 0.15F, -S, 0.0F, -A, -R * 0.9}, + // 3: Entra desde abajo. La 4 entra desde arriba-izquierda → sale empujada hacia abajo-derecha + {X_DEST - 25.0F, H + CARD_HEIGHT, CARD_ANGLE_3, S * 0.8F, S * 0.6F, A * 0.5F, A * 0.4F, R}, + // 4: Entra desde arriba-izquierda. La 5 entra desde derecha-abajo → sale empujada hacia arriba-izquierda + {-CARD_WIDTH * 0.5F, -CARD_HEIGHT, CARD_ANGLE_4, -S * 0.7F, -S * 0.5F, -A * 0.5F, -A * 0.3F, -R * 1.2}, + // 5: Entra desde la derecha-abajo. Última: sale hacia la izquierda suave (viento) + {W + CARD_WIDTH, H * 0.6F, CARD_ANGLE_5, -S * 0.6F, -S * 0.1F, -A * 0.5F, 0.0F, -R * 0.7}, }; // Inicializa los CardSprites @@ -376,15 +387,19 @@ void Intro::initSprites() { card->setHeight(CARD_HEIGHT); card->setSpriteClip(0, 0, CARD_WIDTH, CARD_HEIGHT); - // Posición de aterrizaje + const auto& cfg = CARD_CONFIGS[i]; + + // Posición de aterrizaje (centro) card->setLandingPosition(X_DEST, Y_DEST); + // Posición de entrada (borde de pantalla) + card->setEntryPosition(cfg.entry_x, cfg.entry_y); + // Parámetros de entrada: zoom, ángulo, duración, easing - card->setEntryParams(CARD_START_ZOOM, ENTRY_ANGLES[i], CARD_ENTRY_DURATION_S, easeOutBounce); + card->setEntryParams(CARD_START_ZOOM, cfg.entry_angle, CARD_ENTRY_DURATION_S, easeOutBounce); // Parámetros de salida - const auto& exit = EXIT_CONFIGS[i]; - card->setExitParams(exit.vx, exit.vy, exit.ax, exit.ay, exit.rotate_amount); + card->setExitParams(cfg.exit_vx, cfg.exit_vy, cfg.exit_ax, cfg.exit_ay, cfg.exit_rotation); // Sombra card->setShadowTexture(shadow_texture); diff --git a/source/sections/intro.hpp b/source/sections/intro.hpp index 48822e7..92f7ede 100644 --- a/source/sections/intro.hpp +++ b/source/sections/intro.hpp @@ -59,7 +59,7 @@ class Intro { static constexpr float CARD_START_ZOOM = 1.8F; // Zoom inicial (como si estuviera cerca) static constexpr float CARD_EXIT_SPEED = 400.0F; // Velocidad base de salida (pixels/s) static constexpr float CARD_EXIT_ACCEL = 200.0F; // Aceleración de salida (pixels/s²) - static constexpr double CARD_EXIT_ROTATION = 180.0; // Velocidad de rotación en salida (grados/s) + static constexpr double CARD_EXIT_ROTATION = 450.0; // Velocidad de rotación en salida (grados/s) // --- Ángulos iniciales de entrada por tarjeta (grados) --- static constexpr double CARD_ANGLE_0 = 12.0; @@ -119,5 +119,4 @@ class Intro { // --- Métodos auxiliares --- void switchText(int from_index, int to_index); - void startCardExitAndAdvance(); // Inicia la salida de la tarjeta actual y avanza a la siguiente escena };