aproximant-se

This commit is contained in:
2026-04-03 18:24:58 +02:00
parent 43de2c0b35
commit 6f31751d42
4 changed files with 100 additions and 53 deletions

View File

@@ -5,7 +5,7 @@
#include <utility> // Para move #include <utility> // Para move
#include "texture.hpp" // Para Texture #include "texture.hpp" // Para Texture
#include "utils.hpp" // Para easeOutBounce #include "utils.hpp" // Para easeOutBounce, easeOutCubic
// Constructor // Constructor
CardSprite::CardSprite(std::shared_ptr<Texture> texture) CardSprite::CardSprite(std::shared_ptr<Texture> texture)
@@ -20,9 +20,10 @@ void CardSprite::enable() {
state_ = CardState::ENTERING; state_ = CardState::ENTERING;
entry_elapsed_ = 0.0F; entry_elapsed_ = 0.0F;
first_touch_ = false;
// Posición fija en el punto de aterrizaje // Posición inicial (borde de pantalla)
setPos(landing_x_, landing_y_); setPos(entry_start_x_, entry_start_y_);
// Zoom inicial grande (como si estuviera cerca de la cámara) // Zoom inicial grande (como si estuviera cerca de la cámara)
horizontal_zoom_ = start_zoom_; 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) { void CardSprite::updateEntering(float delta_time) {
entry_elapsed_ += delta_time; entry_elapsed_ += delta_time;
@@ -85,15 +86,26 @@ void CardSprite::updateEntering(float delta_time) {
// Ángulo: de start_angle_ a 0 con rebote // Ángulo: de start_angle_ a 0 con rebote
rotate_.angle = start_angle_ * (1.0 - eased); rotate_.angle = start_angle_ * (1.0 - eased);
// Offset de sombra escalado con el zoom (perspectiva) // Posición: de entry_start a landing con easing suave (sin rebote)
// (se aplica en renderShadow) // Usamos easeOutCubic para que el desplazamiento sea fluido
double pos_eased = easeOutCubic(static_cast<double>(progress));
auto current_x = static_cast<float>(entry_start_x_ + (landing_x_ - entry_start_x_) * pos_eased);
auto current_y = static_cast<float>(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) { if (progress >= 1.0F) {
horizontal_zoom_ = 1.0F; horizontal_zoom_ = 1.0F;
vertical_zoom_ = 1.0F; vertical_zoom_ = 1.0F;
rotate_.angle = 0.0; rotate_.angle = 0.0;
setPos(landing_x_, landing_y_);
state_ = CardState::LANDED; state_ = CardState::LANDED;
first_touch_ = true;
} }
} }
@@ -122,18 +134,23 @@ void CardSprite::render() {
MovingSprite::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() { 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_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( shadow_texture_->render(
pos_.x + scaled_offset_x, pos_.x + scaled_offset_x,
pos_.y + scaled_offset_y, pos_.y + scaled_offset_y,
&sprite_clip_, &sprite_clip_,
horizontal_zoom_, shadow_zoom,
vertical_zoom_, shadow_zoom,
rotate_.angle, rotate_.angle,
&rotate_.center, &rotate_.center,
flip_); flip_);
@@ -141,7 +158,6 @@ void CardSprite::renderShadow() {
// Comprueba si el sprite está fuera de pantalla // Comprueba si el sprite está fuera de pantalla
auto CardSprite::isOffScreen() const -> bool { 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_width = pos_.w * horizontal_zoom_;
float effective_height = pos_.h * vertical_zoom_; float effective_height = pos_.h * vertical_zoom_;
return (pos_.x + effective_width < -OFF_SCREEN_MARGIN || 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; return state_ == CardState::LANDED || state_ == CardState::EXITING || state_ == CardState::FINISHED;
} }
auto CardSprite::hasFirstTouch() const -> bool {
return first_touch_;
}
auto CardSprite::hasFinished() const -> bool { auto CardSprite::hasFinished() const -> bool {
return state_ == CardState::FINISHED; return state_ == CardState::FINISHED;
} }
@@ -175,6 +195,11 @@ void CardSprite::setEntryParams(float start_zoom, double start_angle, float dura
entry_easing_ = std::move(easing); 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) { void CardSprite::setLandingPosition(float x, float y) {
landing_x_ = x; landing_x_ = x;
landing_y_ = y; landing_y_ = y;

View File

@@ -12,7 +12,7 @@ class Texture;
// --- Estados de la tarjeta --- // --- Estados de la tarjeta ---
enum class CardState { enum class CardState {
IDLE, // No activada todavía 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 LANDED, // En reposo sobre la mesa
EXITING, // Saliendo de pantalla girando EXITING, // Saliendo de pantalla girando
FINISHED, // Fuera de pantalla FINISHED, // Fuera de pantalla
@@ -20,8 +20,8 @@ enum class CardState {
// --- Clase CardSprite: tarjeta animada con zoom, rotación y sombra integrada --- // --- Clase CardSprite: tarjeta animada con zoom, rotación y sombra integrada ---
// //
// Simula una tarjeta lanzada sobre una mesa desde arriba (eje Z). // Simula una tarjeta lanzada sobre una mesa desde un borde de la pantalla.
// Durante la entrada, interpola zoom y rotación con easing (rebote). // Durante la entrada, interpola posición, zoom y rotación con easing (rebote).
// Durante la salida, se desplaza fuera de pantalla girando, sin sombra. // Durante la salida, se desplaza fuera de pantalla girando, sin sombra.
class CardSprite : public MovingSprite { class CardSprite : public MovingSprite {
public: public:
@@ -37,13 +37,15 @@ class CardSprite : public MovingSprite {
void startExit(); // Inicia la animación de salida void startExit(); // Inicia la animación de salida
// --- Consultas de estado --- // --- 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 hasFinished() const -> bool; // ¿Ha terminado completamente?
[[nodiscard]] auto isExiting() const -> bool; // ¿Está saliendo de pantalla? [[nodiscard]] auto isExiting() const -> bool; // ¿Está saliendo de pantalla?
[[nodiscard]] auto getState() const -> CardState; // Estado actual [[nodiscard]] auto getState() const -> CardState; // Estado actual
// --- Configuración de entrada --- // --- Configuración de entrada ---
void setEntryParams(float start_zoom, double start_angle, float duration_s, std::function<double(double)> easing); void setEntryParams(float start_zoom, double start_angle, float duration_s, std::function<double(double)> easing);
void setEntryPosition(float start_x, float start_y); // Posición inicial (borde de pantalla)
void setLandingPosition(float x, float y); // Posición final centrada void setLandingPosition(float x, float y); // Posición final centrada
// --- Configuración de salida --- // --- Configuración de salida ---
@@ -59,6 +61,10 @@ class CardSprite : public MovingSprite {
private: private:
// --- Estado --- // --- Estado ---
CardState state_ = CardState::IDLE; 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 --- // --- Parámetros de entrada ---
float start_zoom_ = 1.8F; float start_zoom_ = 1.8F;
@@ -66,6 +72,8 @@ class CardSprite : public MovingSprite {
float entry_duration_s_ = 1.5F; float entry_duration_s_ = 1.5F;
float entry_elapsed_ = 0.0F; float entry_elapsed_ = 0.0F;
std::function<double(double)> entry_easing_; std::function<double(double)> 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_x_ = 0.0F;
float landing_y_ = 0.0F; float landing_y_ = 0.0F;

View File

@@ -57,6 +57,11 @@ void Intro::checkInput() {
// Actualiza las escenas de la intro // Actualiza las escenas de la intro
void Intro::updateScenes() { 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_) { switch (scene_) {
case 0: case 0:
updateScene0(); updateScene0();
@@ -103,7 +108,7 @@ void Intro::updateScene0() {
// Fin de la primera escena: la tarjeta sale despedida // Fin de la primera escena: la tarjeta sale despedida
if (texts_.at(2)->hasFinished()) { if (texts_.at(2)->hasFinished()) {
texts_.at(2)->setEnabled(false); texts_.at(2)->setEnabled(false);
startCardExitAndAdvance(); scene_++;
} }
} }
@@ -119,7 +124,7 @@ void Intro::updateScene1() {
// Fin de la segunda escena // Fin de la segunda escena
if (texts_.at(3)->hasFinished()) { if (texts_.at(3)->hasFinished()) {
texts_.at(3)->setEnabled(false); texts_.at(3)->setEnabled(false);
startCardExitAndAdvance(); scene_++;
} }
} }
@@ -133,7 +138,7 @@ void Intro::updateScene2() {
// Fin de la tercera escena // Fin de la tercera escena
if (card_sprites_.at(2)->hasLanded() && texts_.at(4)->hasFinished()) { if (card_sprites_.at(2)->hasLanded() && texts_.at(4)->hasFinished()) {
texts_.at(4)->setEnabled(false); texts_.at(4)->setEnabled(false);
startCardExitAndAdvance(); scene_++;
} }
} }
@@ -153,7 +158,7 @@ void Intro::updateScene3() {
// Fin de la cuarta escena // Fin de la cuarta escena
if (card_sprites_.at(3)->hasLanded() && texts_.at(6)->hasFinished()) { if (card_sprites_.at(3)->hasLanded() && texts_.at(6)->hasFinished()) {
texts_.at(6)->setEnabled(false); texts_.at(6)->setEnabled(false);
startCardExitAndAdvance(); scene_++;
} }
} }
@@ -168,7 +173,7 @@ void Intro::updateScene4() {
// Fin de la quinta escena // Fin de la quinta escena
if (card_sprites_.at(4)->hasLanded() && texts_.at(7)->hasFinished()) { if (card_sprites_.at(4)->hasLanded() && texts_.at(7)->hasFinished()) {
texts_.at(7)->setEnabled(false); 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) { void Intro::switchText(int from_index, int to_index) {
texts_.at(from_index)->setEnabled(false); texts_.at(from_index)->setEnabled(false);
texts_.at(to_index)->setEnabled(true); 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 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); const float Y_DEST = param.game.game_area.first_quarter_y - (CARD_HEIGHT / 4);
// Ángulos de entrada por tarjeta (variedad visual) // Configuración por tarjeta: posición de entrada, ángulo, salida
const double ENTRY_ANGLES[] = {CARD_ANGLE_0, CARD_ANGLE_1, CARD_ANGLE_2, CARD_ANGLE_3, CARD_ANGLE_4, CARD_ANGLE_5}; // Cada tarjeta viene de un borde diferente (gente alrededor de una mesa lanzando cartas al centro)
struct CardConfig {
// Direcciones de salida: cada tarjeta sale en una dirección diferente float entry_x; // Posición inicial X
// {vx, vy, ax, ay, rotate_amount} float entry_y; // Posición inicial Y
struct ExitConfig { double entry_angle; // Ángulo de entrada
float vx; float exit_vx; // Velocidad de salida X
float vy; float exit_vy; // Velocidad de salida Y
float ax; float exit_ax; // Aceleración de salida X
float ay; float exit_ay; // Aceleración de salida Y
double rotate_amount; double exit_rotation; // Velocidad de rotación de salida
}; };
const ExitConfig EXIT_CONFIGS[] = { const float W = param.game.width;
{ CARD_EXIT_SPEED, -CARD_EXIT_SPEED * 0.15F, CARD_EXIT_ACCEL, 0.0F, CARD_EXIT_ROTATION}, // 0: Derecha + leve arriba const float H = param.game.height;
{-CARD_EXIT_SPEED, CARD_EXIT_SPEED * 0.25F, -CARD_EXIT_ACCEL, CARD_EXIT_ACCEL * 0.2F, -CARD_EXIT_ROTATION * 1.1}, // 1: Izquierda + abajo const float S = CARD_EXIT_SPEED;
{ CARD_EXIT_SPEED, -CARD_EXIT_SPEED * 0.4F, CARD_EXIT_ACCEL, -CARD_EXIT_ACCEL * 0.3F, CARD_EXIT_ROTATION * 0.8}, // 2: Derecha + arriba const float A = CARD_EXIT_ACCEL;
{-CARD_EXIT_SPEED, -CARD_EXIT_SPEED * 0.2F, -CARD_EXIT_ACCEL, 0.0F, -CARD_EXIT_ROTATION}, // 3: Izquierda + leve arriba const double R = CARD_EXIT_ROTATION;
{ 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 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 // Inicializa los CardSprites
@@ -376,15 +387,19 @@ void Intro::initSprites() {
card->setHeight(CARD_HEIGHT); card->setHeight(CARD_HEIGHT);
card->setSpriteClip(0, 0, CARD_WIDTH, 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); 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 // 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 // Parámetros de salida
const auto& exit = EXIT_CONFIGS[i]; card->setExitParams(cfg.exit_vx, cfg.exit_vy, cfg.exit_ax, cfg.exit_ay, cfg.exit_rotation);
card->setExitParams(exit.vx, exit.vy, exit.ax, exit.ay, exit.rotate_amount);
// Sombra // Sombra
card->setShadowTexture(shadow_texture); card->setShadowTexture(shadow_texture);

View File

@@ -59,7 +59,7 @@ class Intro {
static constexpr float CARD_START_ZOOM = 1.8F; // Zoom inicial (como si estuviera cerca) 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_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 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) --- // --- Ángulos iniciales de entrada por tarjeta (grados) ---
static constexpr double CARD_ANGLE_0 = 12.0; static constexpr double CARD_ANGLE_0 = 12.0;
@@ -119,5 +119,4 @@ class Intro {
// --- Métodos auxiliares --- // --- Métodos auxiliares ---
void switchText(int from_index, int to_index); void switchText(int from_index, int to_index);
void startCardExitAndAdvance(); // Inicia la salida de la tarjeta actual y avanza a la siguiente escena
}; };