reestructuració

This commit is contained in:
2026-04-14 13:26:22 +02:00
parent 4ac34b8583
commit 4429cd92c1
143 changed files with 0 additions and 0 deletions
@@ -0,0 +1,304 @@
#include "animated_sprite.hpp"
#include <SDL3/SDL.h> // Para SDL_LogWarn, SDL_LogCategory, SDL_LogError, SDL_FRect
#include <algorithm> // Para min
#include <cstddef> // Para size_t
#include <fstream> // Para basic_istream, basic_ifstream, istream, basic_ios, ifstream, istringstream, stringstream
#include <iostream> // Para std::cout
#include <sstream> // Para basic_istringstream, basic_stringstream
#include <stdexcept> // Para runtime_error
#include <utility> // Para move, pair
#include "resource_helper.hpp" // Para loadFile
#include "texture.hpp" // Para Texture
// Carga las animaciones en un vector(Animations) desde un fichero
auto loadAnimationsFromFile(const std::string& file_path) -> AnimationsFileBuffer {
// Intentar cargar desde ResourceHelper primero
auto resource_data = ResourceHelper::loadFile(file_path);
std::istringstream stream;
bool using_resource_data = false;
if (!resource_data.empty()) {
std::string content(resource_data.begin(), resource_data.end());
stream.str(content);
using_resource_data = true;
}
// Fallback a archivo directo
std::ifstream file;
if (!using_resource_data) {
file.open(file_path);
if (!file) {
std::cout << "Error: Fichero no encontrado " << file_path << '\n';
throw std::runtime_error("Fichero no encontrado: " + file_path);
}
}
std::istream& input_stream = using_resource_data ? stream : static_cast<std::istream&>(file);
std::vector<std::string> buffer;
std::string line;
while (std::getline(input_stream, line)) {
// Eliminar caracteres de retorno de carro (\r) al final de la línea
if (!line.empty() && line.back() == '\r') {
line.pop_back();
}
if (!line.empty()) {
buffer.push_back(line);
}
}
return buffer;
}
// Constructor
AnimatedSprite::AnimatedSprite(std::shared_ptr<Texture> texture, const std::string& file_path)
: MovingSprite(std::move(texture)) {
// Carga las animaciones
if (!file_path.empty()) {
auto buffer = loadAnimationsFromFile(file_path);
loadFromAnimationsFileBuffer(buffer);
}
}
// Constructor
AnimatedSprite::AnimatedSprite(std::shared_ptr<Texture> texture, const AnimationsFileBuffer& animations)
: MovingSprite(std::move(texture)) {
if (!animations.empty()) {
loadFromAnimationsFileBuffer(animations);
}
}
// Obtiene el índice de la animación a partir del nombre
auto AnimatedSprite::getAnimationIndex(const std::string& name) -> int {
auto iterator = animation_indices_.find(name);
if (iterator != animation_indices_.end()) {
// Si se encuentra la animación en el mapa, devuelve su índice
return iterator->second;
}
// Si no se encuentra, muestra una advertencia y devuelve -1
std::cout << "** Warning: could not find \"" << name << "\" animation" << '\n';
return -1;
}
// Calcula el frame correspondiente a la animación (time-based)
void AnimatedSprite::animate(float delta_time) {
if (animations_[current_animation_].speed == 0 || animations_[current_animation_].paused) {
return;
}
// Acumular tiempo transcurrido
animations_[current_animation_].time_accumulator += delta_time;
// Verificar si es momento de cambiar frame
if (animations_[current_animation_].time_accumulator >= animations_[current_animation_].speed) {
animations_[current_animation_].time_accumulator -= animations_[current_animation_].speed;
animations_[current_animation_].current_frame++;
// Si alcanza el final de la animación
if (animations_[current_animation_].current_frame >= animations_[current_animation_].frames.size()) {
if (animations_[current_animation_].loop == -1) { // Si no hay loop, deja el último frame
animations_[current_animation_].current_frame = animations_[current_animation_].frames.size() - 1;
animations_[current_animation_].completed = true;
} else { // Si hay loop, vuelve al frame indicado
animations_[current_animation_].time_accumulator = 0.0F;
animations_[current_animation_].current_frame = animations_[current_animation_].loop;
}
}
// Actualizar el sprite clip
updateSpriteClip();
}
}
// Comprueba si ha terminado la animación
auto AnimatedSprite::animationIsCompleted() -> bool {
return animations_[current_animation_].completed;
}
// Establece la animacion actual
void AnimatedSprite::setCurrentAnimation(const std::string& name, bool reset) {
const auto NEW_ANIMATION = getAnimationIndex(name);
if (current_animation_ != NEW_ANIMATION) {
const auto OLD_ANIMATION = current_animation_;
current_animation_ = NEW_ANIMATION;
if (reset) {
animations_[current_animation_].current_frame = 0;
animations_[current_animation_].time_accumulator = 0.0F;
animations_[current_animation_].completed = false;
} else {
animations_[current_animation_].current_frame = std::min(animations_[OLD_ANIMATION].current_frame, animations_[current_animation_].frames.size() - 1);
animations_[current_animation_].time_accumulator = animations_[OLD_ANIMATION].time_accumulator;
animations_[current_animation_].completed = animations_[OLD_ANIMATION].completed;
}
updateSpriteClip();
}
}
// Establece la animacion actual
void AnimatedSprite::setCurrentAnimation(int index, bool reset) {
const auto NEW_ANIMATION = index;
if (current_animation_ != NEW_ANIMATION) {
const auto OLD_ANIMATION = current_animation_;
current_animation_ = NEW_ANIMATION;
if (reset) {
animations_[current_animation_].current_frame = 0;
animations_[current_animation_].time_accumulator = 0.0F;
animations_[current_animation_].completed = false;
} else {
animations_[current_animation_].current_frame = std::min(animations_[OLD_ANIMATION].current_frame, animations_[current_animation_].frames.size());
animations_[current_animation_].time_accumulator = animations_[OLD_ANIMATION].time_accumulator;
animations_[current_animation_].completed = animations_[OLD_ANIMATION].completed;
}
updateSpriteClip();
}
}
// Actualiza las variables del objeto (time-based)
void AnimatedSprite::update(float delta_time) {
animate(delta_time);
MovingSprite::update(delta_time);
}
// Reinicia la animación
void AnimatedSprite::resetAnimation() {
animations_[current_animation_].current_frame = 0;
animations_[current_animation_].time_accumulator = 0.0F;
animations_[current_animation_].completed = false;
animations_[current_animation_].paused = false;
updateSpriteClip();
}
// Carga la animación desde un vector de cadenas
void AnimatedSprite::loadFromAnimationsFileBuffer(const AnimationsFileBuffer& source) {
AnimationConfig config;
size_t index = 0;
while (index < source.size()) {
const std::string& line = source.at(index);
if (line == "[animation]") {
index = processAnimationBlock(source, index, config);
} else {
processConfigLine(line, config);
}
index++;
}
// Pone un valor por defecto
setWidth(config.frame_width);
setHeight(config.frame_height);
// Establece el primer frame inmediatamente si hay animaciones
if (!animations_.empty()) {
current_animation_ = 0;
updateSpriteClip();
}
}
// Procesa una línea de configuración
void AnimatedSprite::processConfigLine(const std::string& line, AnimationConfig& config) {
size_t pos = line.find('=');
if (pos == std::string::npos) {
return;
}
std::string key = line.substr(0, pos);
int value = std::stoi(line.substr(pos + 1));
if (key == "frame_width") {
config.frame_width = value;
updateFrameCalculations(config);
} else if (key == "frame_height") {
config.frame_height = value;
updateFrameCalculations(config);
} else {
std::cout << "Warning: unknown parameter " << key << '\n';
}
}
// Actualiza los cálculos basados en las dimensiones del frame
void AnimatedSprite::updateFrameCalculations(AnimationConfig& config) {
config.frames_per_row = getTexture()->getWidth() / config.frame_width;
const int WIDTH = getTexture()->getWidth() / config.frame_width;
const int HEIGHT = getTexture()->getHeight() / config.frame_height;
config.max_tiles = WIDTH * HEIGHT;
}
// Procesa un bloque completo de animación
auto AnimatedSprite::processAnimationBlock(const AnimationsFileBuffer& source, size_t start_index, const AnimationConfig& config) -> size_t {
Animation animation;
size_t index = start_index + 1; // Salta la línea "[animation]"
while (index < source.size()) {
const std::string& line = source.at(index);
if (line == "[/animation]") {
break;
}
processAnimationParameter(line, animation, config);
index++;
}
// Añade la animación al vector de animaciones
animations_.emplace_back(animation);
// Rellena el mapa con el nombre y el nuevo índice
animation_indices_[animation.name] = animations_.size() - 1;
return index;
}
// Procesa un parámetro individual de animación
void AnimatedSprite::processAnimationParameter(const std::string& line, Animation& animation, const AnimationConfig& config) {
size_t pos = line.find('=');
if (pos == std::string::npos) {
return;
}
std::string key = line.substr(0, pos);
std::string value = line.substr(pos + 1);
if (key == "name") {
animation.name = value;
} else if (key == "speed") {
animation.speed = std::stof(value);
} else if (key == "loop") {
animation.loop = std::stoi(value);
} else if (key == "frames") {
parseFramesParameter(value, animation, config);
} else {
std::cout << "Warning: unknown parameter " << key << '\n';
}
}
// Parsea el parámetro de frames (lista separada por comas)
void AnimatedSprite::parseFramesParameter(const std::string& frames_str, Animation& animation, const AnimationConfig& config) {
std::stringstream stream(frames_str);
std::string tmp;
SDL_FRect rect = {.x = 0, .y = 0, .w = config.frame_width, .h = config.frame_height};
while (getline(stream, tmp, ',')) {
const int NUM_TILE = std::stoi(tmp);
if (NUM_TILE <= config.max_tiles) {
rect.x = (NUM_TILE % config.frames_per_row) * config.frame_width;
rect.y = (NUM_TILE / config.frames_per_row) * config.frame_height;
animation.frames.emplace_back(rect);
}
}
}
// Establece la velocidad de la animación
void AnimatedSprite::setAnimationSpeed(float value) {
animations_[current_animation_].speed = value;
}
// Actualiza el spriteClip con el frame correspondiente
void AnimatedSprite::updateSpriteClip() {
setSpriteClip(animations_[current_animation_].frames[animations_[current_animation_].current_frame]);
}
@@ -0,0 +1,88 @@
#pragma once
#include <SDL3/SDL.h> // Para SDL_FRect
#include <cstddef> // Para size_t
#include <memory> // Para shared_ptr
#include <string> // Para basic_string, string, hash
#include <unordered_map> // Para unordered_map
#include <utility> // Para move
#include <vector> // Para vector
#include "moving_sprite.hpp" // for MovingSprite
// Declaración adelantada
class Texture;
// --- Estructuras ---
struct Animation {
static constexpr float DEFAULT_SPEED = 80.0F;
std::string name; // Nombre de la animación
std::vector<SDL_FRect> frames; // Frames que componen la animación
float speed{DEFAULT_SPEED}; // Velocidad de reproducción (ms entre frames)
int loop{0}; // Frame de vuelta al terminar (-1 para no repetir)
bool completed{false}; // Indica si la animación ha finalizado
size_t current_frame{0}; // Frame actual en reproducción
float time_accumulator{0.0F}; // Acumulador de tiempo para animaciones time-based
bool paused{false}; // La animación no avanza
Animation() = default;
};
struct AnimationConfig {
float frame_width = 1.0F;
float frame_height = 1.0F;
int frames_per_row = 1;
int max_tiles = 1;
};
// --- Tipos ---
using AnimationsFileBuffer = std::vector<std::string>; // Buffer de animaciones
// --- Funciones ---
auto loadAnimationsFromFile(const std::string& file_path) -> AnimationsFileBuffer; // Carga las animaciones desde un fichero
// --- Clase AnimatedSprite: sprite animado que hereda de MovingSprite ---
class AnimatedSprite : public MovingSprite {
public:
// --- Constructores y destructor ---
AnimatedSprite(std::shared_ptr<Texture> texture, const std::string& file_path);
AnimatedSprite(std::shared_ptr<Texture> texture, const AnimationsFileBuffer& animations);
explicit AnimatedSprite(std::shared_ptr<Texture> texture)
: MovingSprite(std::move(texture)) {}
~AnimatedSprite() override = default;
// --- Métodos principales ---
void update(float delta_time) override; // Actualiza la animación (time-based)
// --- Control de animaciones ---
void setCurrentAnimation(const std::string& name = "default", bool reset = true); // Establece la animación por nombre
void setCurrentAnimation(int index = 0, bool reset = true); // Establece la animación por índice
void resetAnimation(); // Reinicia la animación actual
void setAnimationSpeed(float value); // Establece la velocidad de la animación
[[nodiscard]] auto getAnimationSpeed() const -> float { return animations_[current_animation_].speed; } // Obtiene la velocidad de la animación actual
void animtionPause() { animations_[current_animation_].paused = true; } // Detiene la animación
void animationResume() { animations_[current_animation_].paused = false; } // Reanuda la animación
[[nodiscard]] auto getCurrentAnimationFrame() const -> size_t { return animations_[current_animation_].current_frame; } // Obtiene el numero de frame de la animación actual
// --- Consultas ---
auto animationIsCompleted() -> bool; // Comprueba si la animación ha terminado
auto getAnimationIndex(const std::string& name) -> int; // Obtiene el índice de una animación por nombre
protected:
// --- Variables de estado ---
std::vector<Animation> animations_; // Vector de animaciones disponibles
std::unordered_map<std::string, int> animation_indices_; // Mapa para búsqueda rápida por nombre
int current_animation_ = 0; // Índice de la animación activa
// --- Métodos internos ---
void animate(float delta_time); // Calcula el frame correspondiente a la animación (time-based)
void loadFromAnimationsFileBuffer(const AnimationsFileBuffer& source); // Carga la animación desde un vector de cadenas
void processConfigLine(const std::string& line, AnimationConfig& config); // Procesa una línea de configuración
void updateFrameCalculations(AnimationConfig& config); // Actualiza los cálculos basados en las dimensiones del frame
auto processAnimationBlock(const AnimationsFileBuffer& source, size_t start_index, const AnimationConfig& config) -> size_t; // Procesa un bloque completo de animación
static void processAnimationParameter(const std::string& line, Animation& animation, const AnimationConfig& config); // Procesa un parámetro individual de animación
static void parseFramesParameter(const std::string& frames_str, Animation& animation, const AnimationConfig& config); // Parsea el parámetro de frames (lista separada por comas)
void updateSpriteClip(); // Actualiza el spriteClip con el frame correspondiente
};
@@ -0,0 +1,255 @@
#include "card_sprite.hpp"
#include <algorithm> // Para std::clamp
#include <functional> // Para function
#include <utility> // Para move
#include "texture.hpp" // Para Texture
#include "utils.hpp" // Para easeOutBounce, easeOutCubic
// Constructor
CardSprite::CardSprite(std::shared_ptr<Texture> texture)
: MovingSprite(std::move(texture)),
entry_easing_(easeOutBounce) {}
// Inicia la animación de entrada (solo si está en IDLE)
auto CardSprite::enable() -> bool {
if (state_ != CardState::IDLE) {
return false;
}
state_ = CardState::ENTERING;
entry_elapsed_ = 0.0F;
first_touch_ = false;
// 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_;
vertical_zoom_ = start_zoom_;
// Ángulo inicial
rotate_.angle = start_angle_;
rotate_.center = {pos_.w / 2.0F, pos_.h / 2.0F};
shadow_visible_ = true;
return true;
}
// Inicia la animación de salida (solo si está en LANDED)
void CardSprite::startExit() {
if (state_ != CardState::LANDED) {
return;
}
state_ = CardState::EXITING;
shadow_visible_ = true;
// Velocidad y aceleración de salida
vx_ = exit_vx_;
vy_ = exit_vy_;
ax_ = exit_ax_;
ay_ = exit_ay_;
// Rotación continua
rotate_.enabled = true;
rotate_.amount = exit_rotate_amount_;
rotate_.center = {pos_.w / 2.0F, pos_.h / 2.0F};
}
// Actualiza según el estado
void CardSprite::update(float delta_time) {
switch (state_) {
case CardState::ENTERING:
updateEntering(delta_time);
break;
case CardState::EXITING:
updateExiting(delta_time);
break;
default:
break;
}
}
// Animación de entrada: interpola posición, zoom y ángulo
void CardSprite::updateEntering(float delta_time) {
entry_elapsed_ += delta_time;
float progress = std::clamp(entry_elapsed_ / entry_duration_s_, 0.0F, 1.0F);
double eased = entry_easing_(static_cast<double>(progress));
// Zoom: de start_zoom_ a 1.0 con rebote
auto current_zoom = static_cast<float>(start_zoom_ + (1.0 - start_zoom_) * eased);
horizontal_zoom_ = current_zoom;
vertical_zoom_ = current_zoom;
// Ángulo: de start_angle_ a 0 con rebote
rotate_.angle = start_angle_ * (1.0 - eased);
// 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<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);
// 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;
}
}
// Animación de salida: movimiento + rotación continua + zoom opcional
void CardSprite::updateExiting(float delta_time) {
move(delta_time);
rotate(delta_time);
// Ganar altura gradualmente (zoom hacia el objetivo)
if (exit_zoom_speed_ > 0.0F && horizontal_zoom_ < exit_target_zoom_) {
float new_zoom = horizontal_zoom_ + exit_zoom_speed_ * delta_time;
if (new_zoom > exit_target_zoom_) {
new_zoom = exit_target_zoom_;
}
horizontal_zoom_ = new_zoom;
vertical_zoom_ = new_zoom;
}
if (isOffScreen()) {
state_ = CardState::FINISHED;
}
}
// Renderiza el sprite y su sombra
void CardSprite::render() {
if (state_ == CardState::IDLE || state_ == CardState::FINISHED) {
return;
}
// Sombra primero (debajo de la tarjeta)
if (shadow_visible_ && shadow_texture_) {
renderShadow();
}
// Tarjeta
MovingSprite::render();
}
// Renderiza la sombra con efecto de perspectiva 2D→3D (efecto helicóptero)
//
// Fuente de luz en la esquina superior izquierda (0,0).
// La sombra se mueve con la tarjeta pero desplazada en dirección opuesta a la luz
// (abajo-derecha a 45°). Cuanto más alta la tarjeta (zoom > 1.0):
// - Más separada de la tarjeta (offset grande)
// - Más pequeña (proyección lejana)
// Cuando la tarjeta está en la mesa (zoom=1.0):
// - Sombra pegada con offset base
// - Tamaño real
void CardSprite::renderShadow() {
// Altura sobre la mesa: 0.0 = en la mesa, 0.8 = alta (zoom 1.8)
float height = horizontal_zoom_ - 1.0F;
// Escala: más pequeña cuanto más alta
float shadow_zoom = 1.0F / horizontal_zoom_;
// Offset respecto a la tarjeta: base + extra proporcional a la altura
// La sombra se aleja en diagonal abajo-derecha (opuesta a la luz en 0,0)
float offset_x = shadow_offset_x_ + height * SHADOW_HEIGHT_MULTIPLIER;
float offset_y = shadow_offset_y_ + height * SHADOW_HEIGHT_MULTIPLIER;
shadow_texture_->render(
pos_.x + offset_x,
pos_.y + offset_y,
&sprite_clip_,
shadow_zoom,
shadow_zoom,
rotate_.angle,
&rotate_.center,
flip_);
}
// Comprueba si el sprite está fuera de pantalla
auto CardSprite::isOffScreen() const -> bool {
float effective_width = pos_.w * horizontal_zoom_;
float effective_height = pos_.h * vertical_zoom_;
return (pos_.x + effective_width < -OFF_SCREEN_MARGIN ||
pos_.x > screen_width_ + OFF_SCREEN_MARGIN ||
pos_.y + effective_height < -OFF_SCREEN_MARGIN ||
pos_.y > screen_height_ + OFF_SCREEN_MARGIN);
}
// --- Consultas de estado ---
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;
}
auto CardSprite::isExiting() const -> bool {
return state_ == CardState::EXITING;
}
auto CardSprite::getState() const -> CardState {
return state_;
}
// --- Configuración ---
void CardSprite::setEntryParams(float start_zoom, double start_angle, float duration_s, std::function<double(double)> easing) {
start_zoom_ = start_zoom;
start_angle_ = start_angle;
entry_duration_s_ = duration_s;
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;
}
void CardSprite::setExitParams(float vx, float vy, float ax, float ay, double rotate_amount) {
exit_vx_ = vx;
exit_vy_ = vy;
exit_ax_ = ax;
exit_ay_ = ay;
exit_rotate_amount_ = rotate_amount;
}
void CardSprite::setExitLift(float target_zoom, float zoom_speed) {
exit_target_zoom_ = target_zoom;
exit_zoom_speed_ = zoom_speed;
}
void CardSprite::setShadowTexture(std::shared_ptr<Texture> texture) {
shadow_texture_ = std::move(texture);
}
void CardSprite::setShadowOffset(float offset_x, float offset_y) {
shadow_offset_x_ = offset_x;
shadow_offset_y_ = offset_y;
}
void CardSprite::setScreenBounds(float width, float height) {
screen_width_ = width;
screen_height_ = height;
}
@@ -0,0 +1,109 @@
#pragma once
#include <SDL3/SDL.h> // Para SDL_FPoint
#include <functional> // Para function
#include <memory> // Para shared_ptr
#include "moving_sprite.hpp" // Para MovingSprite
class Texture;
// --- Estados de la tarjeta ---
enum class CardState {
IDLE, // No activada todavía
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
};
// --- Clase CardSprite: tarjeta animada con zoom, rotación y sombra integrada ---
//
// 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:
explicit CardSprite(std::shared_ptr<Texture> texture);
~CardSprite() override = default;
// --- Ciclo principal ---
void update(float delta_time) override;
void render() override;
// --- Control de estado ---
auto enable() -> bool; // Inicia la animación de entrada (true si se activó)
void startExit(); // Inicia la animación de salida
// --- Consultas de estado ---
[[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<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
// --- Configuración de salida ---
void setExitParams(float vx, float vy, float ax, float ay, double rotate_amount);
void setExitLift(float target_zoom, float zoom_speed); // Ganar altura al salir (zoom > 1.0)
// --- Sombra ---
void setShadowTexture(std::shared_ptr<Texture> texture);
void setShadowOffset(float offset_x, float offset_y);
// --- Limites de pantalla (para detectar salida) ---
void setScreenBounds(float width, float height);
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;
double start_angle_ = 15.0;
float entry_duration_s_ = 1.5F;
float entry_elapsed_ = 0.0F;
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_y_ = 0.0F;
// --- Parámetros de salida ---
float exit_vx_ = 0.0F;
float exit_vy_ = 0.0F;
float exit_ax_ = 0.0F;
float exit_ay_ = 0.0F;
double exit_rotate_amount_ = 0.0;
float exit_target_zoom_ = 1.0F; // Zoom objetivo al salir (>1.0 = se eleva)
float exit_zoom_speed_ = 0.0F; // Velocidad de cambio de zoom por segundo
// --- Sombra ---
std::shared_ptr<Texture> shadow_texture_;
float shadow_offset_x_ = 8.0F;
float shadow_offset_y_ = 8.0F;
bool shadow_visible_ = true;
// --- Límites de pantalla ---
float screen_width_ = 320.0F;
float screen_height_ = 240.0F;
// --- Constantes ---
static constexpr float OFF_SCREEN_MARGIN = 50.0F; // Margen fuera de pantalla para considerar FINISHED
static constexpr float SHADOW_HEIGHT_MULTIPLIER = 400.0F; // Pixels de separación de sombra por unidad de altura
// --- Métodos internos ---
void updateEntering(float delta_time);
void updateExiting(float delta_time);
void renderShadow();
[[nodiscard]] auto isOffScreen() const -> bool;
};
@@ -0,0 +1,136 @@
#include "moving_sprite.hpp"
#include <cmath> // Para std::abs
#include <utility>
#include "texture.hpp" // Para Texture
// Constructor
MovingSprite::MovingSprite(std::shared_ptr<Texture> texture, SDL_FRect pos, Rotate rotate, float horizontal_zoom, float vertical_zoom, SDL_FlipMode flip)
: Sprite(std::move(texture), pos),
rotate_(rotate),
flip_(flip),
x_(pos.x),
y_(pos.y),
horizontal_zoom_(horizontal_zoom),
vertical_zoom_(vertical_zoom) {}
MovingSprite::MovingSprite(std::shared_ptr<Texture> texture, SDL_FRect pos)
: Sprite(std::move(texture), pos),
flip_(SDL_FLIP_NONE),
x_(pos.x),
y_(pos.y),
horizontal_zoom_(1.0F),
vertical_zoom_(1.0F) {}
MovingSprite::MovingSprite(std::shared_ptr<Texture> texture)
: Sprite(std::move(texture)),
flip_(SDL_FLIP_NONE),
horizontal_zoom_(1.0F),
vertical_zoom_(1.0F) { Sprite::clear(); }
// Reinicia todas las variables
void MovingSprite::clear() {
stop();
Sprite::clear();
}
// Elimina el movimiento del sprite
void MovingSprite::stop() {
x_ = 0.0F; // Posición en el eje X
y_ = 0.0F; // Posición en el eje Y
vx_ = 0.0F; // Velocidad en el eje X. Cantidad de pixeles a desplazarse
vy_ = 0.0F; // Velocidad en el eje Y. Cantidad de pixeles a desplazarse
ax_ = 0.0F; // Aceleración en el eje X. Variación de la velocidad
ay_ = 0.0F; // Aceleración en el eje Y. Variación de la velocidad
rotate_ = Rotate(); // Inicializa la estructura
horizontal_zoom_ = 1.0F; // Zoom aplicado a la anchura
vertical_zoom_ = 1.0F; // Zoom aplicado a la altura
flip_ = SDL_FLIP_NONE; // Establece como se ha de voltear el sprite
}
// Mueve el sprite (time-based)
void MovingSprite::move(float delta_time) {
// DeltaTime puro: velocidad (pixels/ms) * tiempo (ms)
x_ += vx_ * delta_time;
y_ += vy_ * delta_time;
// Aceleración (pixels/ms²) * tiempo (ms)
vx_ += ax_ * delta_time;
vy_ += ay_ * delta_time;
pos_.x = static_cast<int>(x_);
pos_.y = static_cast<int>(y_);
}
// Actualiza las variables internas del objeto (time-based)
void MovingSprite::update(float delta_time) {
move(delta_time);
rotate(delta_time);
}
// Muestra el sprite por pantalla
void MovingSprite::render() {
getTexture()->render(pos_.x, pos_.y, &sprite_clip_, horizontal_zoom_, vertical_zoom_, rotate_.angle, &rotate_.center, flip_);
}
// Establece la rotacion (time-based)
void MovingSprite::rotate(float delta_time) {
if (rotate_.enabled) {
rotate_.angle += rotate_.amount * delta_time;
}
}
// Activa o desactiva el efecto de rotación
void MovingSprite::setRotate(bool enable) {
rotate_.enabled = enable;
}
// Habilita la rotación y establece el centro en el centro del sprite
void MovingSprite::startRotate() {
rotate_.enabled = true;
rotate_.center.x = pos_.w / 2.0F;
rotate_.center.y = pos_.h / 2.0F;
}
// Detiene la rotación y resetea el ángulo a cero
void MovingSprite::stopRotate(float threshold) {
if (threshold == 0.0F || std::abs(rotate_.amount) <= threshold) {
rotate_.enabled = false;
rotate_.angle = 0.0;
}
}
// Establece la posición y_ el tamaño del objeto
void MovingSprite::setPos(SDL_FRect rect) {
x_ = rect.x;
y_ = rect.y;
pos_ = rect;
}
// Establece el valor de las variables
void MovingSprite::setPos(float pos_x, float pos_y) {
x_ = pos_x;
y_ = pos_y;
pos_.x = static_cast<int>(x_);
pos_.y = static_cast<int>(y_);
}
// Establece el valor de la variable
void MovingSprite::setPosX(float pos_x) {
x_ = pos_x;
pos_.x = static_cast<int>(x_);
}
// Establece el valor de la variable
void MovingSprite::setPosY(float pos_y) {
y_ = pos_y;
pos_.y = static_cast<int>(y_);
}
@@ -0,0 +1,83 @@
#pragma once
#include <SDL3/SDL.h> // Para SDL_FlipMode, SDL_FPoint, SDL_FRect
#include <memory> // Para shared_ptr
#include "sprite.hpp" // for Sprite
class Texture;
// --- Clase MovingSprite: añade movimiento y efectos de rotación, zoom y flip al sprite ---
class MovingSprite : public Sprite {
public:
// --- Estructuras ---
struct Rotate {
bool enabled{false}; // Indica si ha de rotar
double angle{0.0}; // Ángulo para dibujarlo
float amount{0.0F}; // Cantidad de grados a girar en cada iteración
SDL_FPoint center{.x = 0.0F, .y = 0.0F}; // Centro de rotación
};
// --- Constructores y destructor ---
MovingSprite(std::shared_ptr<Texture> texture, SDL_FRect pos, MovingSprite::Rotate rotate, float horizontal_zoom, float vertical_zoom, SDL_FlipMode flip);
MovingSprite(std::shared_ptr<Texture> texture, SDL_FRect pos);
explicit MovingSprite(std::shared_ptr<Texture> texture);
~MovingSprite() override = default;
// --- Métodos principales ---
virtual void update(float delta_time); // Actualiza las variables internas del objeto (time-based)
void clear() override; // Reinicia todas las variables a cero
void stop(); // Elimina el movimiento del sprite
void render() override; // Muestra el sprite por pantalla
// --- Configuración ---
void setPos(SDL_FRect rect); // Establece la posición y el tamaño del objeto
void setPos(float pos_x, float pos_y); // Establece la posición del objeto
void setPosX(float pos_x); // Establece la posición X
void setPosY(float pos_y); // Establece la posición Y
void setVelX(float value) { vx_ = value; } // Establece la velocidad X
void setVelY(float value) { vy_ = value; } // Establece la velocidad Y
void setAccelX(float value) { ax_ = value; } // Establece la aceleración X
void setAccelY(float value) { ay_ = value; } // Establece la aceleración Y
void setHorizontalZoom(float value) { horizontal_zoom_ = value; } // Establece el zoom horizontal
void setVerticalZoom(float value) { vertical_zoom_ = value; } // Establece el zoom vertical
void setAngle(double value) { rotate_.angle = value; } // Establece el ángulo
void setRotatingCenter(SDL_FPoint point) { rotate_.center = point; } // Establece el centro de rotación
void setRotate(bool enable); // Activa o desactiva el efecto de rotación
void startRotate(); // Habilita la rotación con centro automático
void stopRotate(float threshold = 0.0F); // Detiene la rotación y resetea ángulo
void setRotateAmount(double value) { rotate_.amount = value; } // Establece la velocidad de rotación
void scaleRotateAmount(float value) { rotate_.amount *= value; } // Modifica la velocidad de rotacion
void switchRotate() { rotate_.amount *= -1; } // Cambia el sentido de la rotación
void setFlip(SDL_FlipMode flip) { flip_ = flip; } // Establece el flip
void flip() { flip_ = (flip_ == SDL_FLIP_HORIZONTAL) ? SDL_FLIP_NONE : SDL_FLIP_HORIZONTAL; } // Cambia el flip
// --- Getters ---
[[nodiscard]] auto getPosX() const -> float { return x_; } // Obtiene la posición X
[[nodiscard]] auto getPosY() const -> float { return y_; } // Obtiene la posición Y
[[nodiscard]] auto getVelX() const -> float { return vx_; } // Obtiene la velocidad X
[[nodiscard]] auto getVelY() const -> float { return vy_; } // Obtiene la velocidad Y
[[nodiscard]] auto getAccelX() const -> float { return ax_; } // Obtiene la aceleración X
[[nodiscard]] auto getAccelY() const -> float { return ay_; } // Obtiene la aceleración Y
[[nodiscard]] auto isRotating() const -> bool { return rotate_.enabled; } // Verifica si está rotando
auto getFlip() -> SDL_FlipMode { return flip_; } // Obtiene el flip
protected:
// --- Variables de estado ---
Rotate rotate_; // Variables usadas para controlar la rotación del sprite
SDL_FlipMode flip_; // Indica cómo se voltea el sprite
float x_ = 0.0F; // Posición en el eje X
float y_ = 0.0F; // Posición en el eje Y
float vx_ = 0.0F; // Velocidad en el eje X
float vy_ = 0.0F; // Velocidad en el eje Y
float ax_ = 0.0F; // Aceleración en el eje X
float ay_ = 0.0F; // Aceleración en el eje Y
float horizontal_zoom_; // Zoom aplicado a la anchura
float vertical_zoom_; // Zoom aplicado a la altura
// --- Métodos internos ---
void updateAngle() { rotate_.angle += rotate_.amount; } // Incrementa el valor del ángulo
void move(float delta_time); // Mueve el sprite según velocidad y aceleración (time-based)
void rotate(float delta_time); // Rota el sprite según los parámetros de rotación (time-based)
};
@@ -0,0 +1,233 @@
// IWYU pragma: no_include <bits/std_abs.h>
#include "path_sprite.hpp"
#include <cmath> // Para abs
#include <functional> // Para function
#include <utility> // Para move
// Constructor para paths por puntos (convertido a segundos)
Path::Path(const std::vector<SDL_FPoint>& spots_init, float waiting_time_s_init)
: spots(spots_init),
is_point_path(true) {
waiting_time_s = waiting_time_s_init;
}
// Devuelve un vector con los puntos que conforman la ruta
auto createPath(float start, float end, PathType type, float fixed_pos, int steps, const std::function<double(double)>& easing_function) -> std::vector<SDL_FPoint> {
std::vector<SDL_FPoint> v;
v.reserve(steps);
for (int i = 0; i < steps; ++i) {
double t = static_cast<double>(i) / (steps - 1);
double value = start + ((end - start) * easing_function(t));
if ((start > 0 && end < 0) || (start < 0 && end > 0)) {
value = start + ((end > 0 ? 1 : -1) * std::abs(end - start) * easing_function(t));
}
switch (type) {
case PathType::HORIZONTAL:
v.emplace_back(SDL_FPoint{.x = static_cast<float>(value), .y = fixed_pos});
break;
case PathType::VERTICAL:
v.emplace_back(SDL_FPoint{.x = fixed_pos, .y = static_cast<float>(value)});
break;
default:
break;
}
}
return v;
}
// Actualiza la posición y comprueba si ha llegado a su destino
void PathSprite::update(float delta_time) {
if (enabled_ && !has_finished_) {
moveThroughCurrentPath(delta_time);
goToNextPathOrDie();
}
}
// Muestra el sprite por pantalla
void PathSprite::render() {
if (enabled_) {
Sprite::render();
}
}
// Determina el tipo de centrado basado en el tipo de path
auto PathSprite::determineCenteringType(const Path& path, bool centered) -> PathCentered {
if (!centered) {
return PathCentered::NONE;
}
if (path.is_point_path) {
// Lógica de centrado para paths por PUNTOS
if (!path.spots.empty()) {
// Si X es constante, es un path Vertical, centramos en X
return (path.spots.back().x == path.spots.front().x) ? PathCentered::ON_X : PathCentered::ON_Y;
}
return PathCentered::NONE;
}
// Lógica de centrado para paths GENERADOS
// Si el tipo es Vertical, centramos en X
return (path.type == PathType::VERTICAL) ? PathCentered::ON_X : PathCentered::ON_Y;
}
// Aplica centrado en el eje X (para paths verticales)
void PathSprite::centerPathOnX(Path& path, float offset) {
if (path.is_point_path) {
const float X_BASE = !path.spots.empty() ? path.spots.front().x : 0.0F;
const float X = X_BASE - offset;
for (auto& spot : path.spots) {
spot.x = X;
}
} else {
// Es un path generado, ajustamos la posición fija (que es X)
path.fixed_pos -= offset;
}
}
// Aplica centrado en el eje Y (para paths horizontales)
void PathSprite::centerPathOnY(Path& path, float offset) {
if (path.is_point_path) {
const float Y_BASE = !path.spots.empty() ? path.spots.front().y : 0.0F;
const float Y = Y_BASE - offset;
for (auto& spot : path.spots) {
spot.y = Y;
}
} else {
// Es un path generado, ajustamos la posición fija (que es Y)
path.fixed_pos -= offset;
}
}
// Añade un recorrido
void PathSprite::addPath(Path path, bool centered) {
PathCentered path_centered = determineCenteringType(path, centered);
switch (path_centered) {
case PathCentered::ON_X:
centerPathOnX(path, pos_.w / 2.0F);
break;
case PathCentered::ON_Y:
centerPathOnY(path, pos_.h / 2.0F);
break;
case PathCentered::NONE:
break;
}
paths_.emplace_back(std::move(path));
}
// Añade un recorrido generado (en segundos)
void PathSprite::addPath(float start, float end, PathType type, float fixed_pos, float duration_s, const std::function<double(double)>& easing_function, float waiting_time_s) {
paths_.emplace_back(start, end, type, fixed_pos, duration_s, waiting_time_s, easing_function);
}
// Añade un recorrido por puntos (en segundos)
void PathSprite::addPath(const std::vector<SDL_FPoint>& spots, float waiting_time_s) {
paths_.emplace_back(spots, waiting_time_s);
}
// Habilita el objeto
void PathSprite::enable() {
if (paths_.empty() || enabled_) {
return;
}
enabled_ = true;
// Establece la posición inicial
auto& path = paths_.at(current_path_);
if (path.is_point_path) {
const auto& p = path.spots.at(path.counter);
setPosition(p);
} else {
// Para paths generados, establecer posición inicial
SDL_FPoint initial_pos;
if (path.type == PathType::HORIZONTAL) {
initial_pos = {.x = path.start_pos, .y = path.fixed_pos};
} else {
initial_pos = {.x = path.fixed_pos, .y = path.start_pos};
}
setPosition(initial_pos);
}
}
// Coloca el sprite en los diferentes puntos del recorrido
void PathSprite::moveThroughCurrentPath(float delta_time) {
auto& path = paths_.at(current_path_);
if (path.is_point_path) {
// Lógica para paths por puntos (compatibilidad)
const auto& p = path.spots.at(path.counter);
setPosition(p);
if (!path.on_destination) {
++path.counter;
if (std::cmp_greater_equal(path.counter, path.spots.size())) {
path.on_destination = true;
path.counter = static_cast<int>(path.spots.size()) - 1;
}
}
if (path.on_destination) {
path.waiting_elapsed += delta_time;
if (path.waiting_elapsed >= path.waiting_time_s) {
path.finished = true;
}
}
} else {
// Lógica para paths generados en tiempo real
if (!path.on_destination) {
path.elapsed_time += delta_time;
// Calcular progreso (0.0 a 1.0)
float progress = path.elapsed_time / path.duration_s;
if (progress >= 1.0F) {
progress = 1.0F;
path.on_destination = true;
}
// Aplicar función de easing
double eased_progress = path.easing_function(progress);
// Calcular posición actual
float current_pos = path.start_pos + ((path.end_pos - path.start_pos) * static_cast<float>(eased_progress));
// Establecer posición según el tipo
SDL_FPoint position;
if (path.type == PathType::HORIZONTAL) {
position = {.x = current_pos, .y = path.fixed_pos};
} else {
position = {.x = path.fixed_pos, .y = current_pos};
}
setPosition(position);
} else {
// Esperar en destino
path.waiting_elapsed += delta_time;
if (path.waiting_elapsed >= path.waiting_time_s) {
path.finished = true;
}
}
}
}
// Cambia de recorrido o finaliza
void PathSprite::goToNextPathOrDie() {
// Comprueba si ha terminado el recorrdo actual
if (paths_.at(current_path_).finished) {
++current_path_;
}
// Comprueba si quedan mas recorridos
if (std::cmp_greater_equal(current_path_, paths_.size())) {
has_finished_ = true;
current_path_ = 0;
}
}
// Indica si ha terminado todos los recorridos
auto PathSprite::hasFinished() const -> bool { return has_finished_; }
@@ -0,0 +1,101 @@
#pragma once
#include <SDL3/SDL.h> // Para SDL_FPoint
#include <functional> // Para std::function
#include <memory> // Para shared_ptr
#include <utility>
#include <vector> // Para vector
#include "sprite.hpp" // Para Sprite
class Texture;
// --- Enums ---
enum class PathType { // Tipos de recorrido
VERTICAL,
HORIZONTAL,
};
enum class PathCentered { // Centrado del recorrido
ON_X,
ON_Y,
NONE,
};
// --- Estructuras ---
struct Path { // Define un recorrido para el sprite
float start_pos; // Posición inicial
float end_pos; // Posición final
PathType type; // Tipo de movimiento (horizontal/vertical)
float fixed_pos; // Posición fija en el eje contrario
float duration_s; // Duración de la animación en segundos
float waiting_time_s; // Tiempo de espera una vez en el destino
std::function<double(double)> easing_function; // Función de easing
float elapsed_time = 0.0F; // Tiempo transcurrido
float waiting_elapsed = 0.0F; // Tiempo de espera transcurrido
bool on_destination = false; // Indica si ha llegado al destino
bool finished = false; // Indica si ha terminado de esperarse
// Constructor para paths generados
Path(float start, float end, PathType path_type, float fixed, float duration, float waiting, std::function<double(double)> easing)
: start_pos(start),
end_pos(end),
type(path_type),
fixed_pos(fixed),
duration_s(duration),
waiting_time_s(waiting),
easing_function(std::move(easing)) {}
// Constructor para paths por puntos (convertido a segundos)
Path(const std::vector<SDL_FPoint>& spots_init, float waiting_time_s_init);
// Variables para paths por puntos
std::vector<SDL_FPoint> spots; // Solo para paths por puntos
int counter = 0; // Solo para paths por puntos
bool is_point_path = false; // Indica si es un path por puntos
};
// --- Funciones ---
auto createPath(float start, float end, PathType type, float fixed_pos, int steps, const std::function<double(double)>& easing_function) -> std::vector<SDL_FPoint>; // Devuelve un vector con los puntos que conforman la ruta
// --- Clase PathSprite: Sprite que sigue uno o varios recorridos ---
class PathSprite : public Sprite {
public:
// --- Constructor y destructor ---
explicit PathSprite(std::shared_ptr<Texture> texture)
: Sprite(std::move(texture)) {}
~PathSprite() override = default;
// --- Métodos principales ---
void update(float delta_time); // Actualiza la posición del sprite según el recorrido (delta_time en segundos)
void render() override; // Muestra el sprite por pantalla
// --- Gestión de recorridos ---
void addPath(Path path, bool centered = false); // Añade un recorrido (Path)
void addPath(const std::vector<SDL_FPoint>& spots, float waiting_time_s = 0.0F); // Añade un recorrido a partir de puntos
void addPath(float start, float end, PathType type, float fixed_pos, float duration_s, const std::function<double(double)>& easing_function, float waiting_time_s = 0.0F); // Añade un recorrido generado
// --- Estado y control ---
void enable(); // Habilita el objeto
[[nodiscard]] auto hasFinished() const -> bool; // Indica si ha terminado todos los recorridos
// --- Getters ---
[[nodiscard]] auto getCurrentPath() const -> int { return current_path_; } // Devuelve el índice del recorrido actual
private:
// --- Variables internas ---
bool enabled_ = false; // Indica si el objeto está habilitado
bool has_finished_ = false; // Indica si el objeto ha finalizado el recorrido
int current_path_ = 0; // Recorrido que se está recorriendo actualmente
std::vector<Path> paths_; // Caminos a recorrer por el sprite
// --- Métodos internos ---
void moveThroughCurrentPath(float delta_time); // Coloca el sprite en los diferentes puntos del recorrido
void goToNextPathOrDie(); // Cambia de recorrido o finaliza
// --- Métodos auxiliares para addPath ---
[[nodiscard]] static auto determineCenteringType(const Path& path, bool centered) -> PathCentered; // Determina el tipo de centrado
static void centerPathOnX(Path& path, float offset); // Aplica centrado en el eje X
static void centerPathOnY(Path& path, float offset); // Aplica centrado en el eje Y
};
@@ -0,0 +1,89 @@
#include "smart_sprite.hpp"
#include "moving_sprite.hpp" // Para MovingSprite
// Actualiza la posición y comprueba si ha llegado a su destino (time-based)
void SmartSprite::update(float delta_time) {
if (enabled_) {
MovingSprite::update(delta_time);
checkMove();
checkFinished(delta_time);
}
}
// Dibuja el sprite
void SmartSprite::render() {
if (enabled_) {
MovingSprite::render();
}
}
// Comprueba el movimiento
void SmartSprite::checkMove() {
// Comprueba si se desplaza en el eje X hacia la derecha
if (getAccelX() > 0 || getVelX() > 0) {
// Comprueba si ha llegado al destino
if (getPosX() > dest_x_) {
// Lo coloca en posición
setPosX(dest_x_);
// Lo detiene
setVelX(0.0F);
setAccelX(0.0F);
}
}
// Comprueba si se desplaza en el eje X hacia la izquierda
else if (getAccelX() < 0 || getVelX() < 0) {
// Comprueba si ha llegado al destino
if (getPosX() < dest_x_) {
// Lo coloca en posición
setPosX(dest_x_);
// Lo detiene
setVelX(0.0F);
setAccelX(0.0F);
}
}
// Comprueba si se desplaza en el eje Y hacia abajo
if (getAccelY() > 0 || getVelY() > 0) {
// Comprueba si ha llegado al destino
if (getPosY() > dest_y_) {
// Lo coloca en posición
setPosY(dest_y_);
// Lo detiene
setVelY(0.0F);
setAccelY(0.0F);
}
}
// Comprueba si se desplaza en el eje Y hacia arriba
else if (getAccelY() < 0 || getVelY() < 0) {
// Comprueba si ha llegado al destino
if (getPosY() < dest_y_) {
// Lo coloca en posición
setPosY(dest_y_);
// Lo detiene
setVelY(0.0F);
setAccelY(0.0F);
}
}
}
// Comprueba si ha terminado (time-based)
void SmartSprite::checkFinished(float delta_time) {
// Comprueba si ha llegado a su destino
on_destination_ = (getPosX() == dest_x_ && getPosY() == dest_y_);
if (on_destination_) {
if (finished_delay_ms_ == 0.0F) {
finished_ = true;
} else {
finished_timer_ += delta_time;
if (finished_timer_ >= finished_delay_ms_) {
finished_ = true;
}
}
}
}
@@ -0,0 +1,47 @@
#pragma once
#include <memory> // Para shared_ptr
#include <utility>
#include "animated_sprite.hpp" // Para AnimatedSprite
class Texture;
// --- Clase SmartSprite: sprite animado que se mueve hacia un destino y puede deshabilitarse automáticamente ---
class SmartSprite : public AnimatedSprite {
public:
// --- Constructor y destructor ---
explicit SmartSprite(std::shared_ptr<Texture> texture)
: AnimatedSprite(std::move(texture)) {}
~SmartSprite() override = default;
// --- Métodos principales ---
void update(float delta_time) override; // Actualiza la posición y comprueba si ha llegado a su destino (time-based)
void render() override; // Dibuja el sprite
// --- Getters ---
[[nodiscard]] auto getDestX() const -> int { return dest_x_; } // Obtiene la posición de destino en X
[[nodiscard]] auto getDestY() const -> int { return dest_y_; } // Obtiene la posición de destino en Y
[[nodiscard]] auto isOnDestination() const -> bool { return on_destination_; } // Indica si está en el destino
[[nodiscard]] auto hasFinished() const -> bool { return finished_; } // Indica si ya ha terminado
// --- Setters ---
void setFinishedDelay(float value) { finished_delay_ms_ = value; } // Establece el retraso para deshabilitarlo (ms)
void setDestX(int x) { dest_x_ = x; } // Establece la posición de destino en X
void setDestY(int y) { dest_y_ = y; } // Establece la posición de destino en Y
void setEnabled(bool value) { enabled_ = value; } // Habilita o deshabilita el objeto
private:
// --- Variables de estado ---
int dest_x_ = 0; // Posición de destino en el eje X
int dest_y_ = 0; // Posición de destino en el eje Y
float finished_delay_ms_ = 0.0F; // Retraso para deshabilitarlo (ms)
float finished_timer_ = 0.0F; // Timer acumulado (ms)
bool on_destination_ = false; // Indica si está en el destino
bool finished_ = false; // Indica si ya ha terminado
bool enabled_ = false; // Indica si el objeto está habilitado
// --- Métodos internos ---
void checkFinished(float delta_time); // Comprueba si ha terminado (time-based)
void checkMove(); // Comprueba el movimiento
};
+54
View File
@@ -0,0 +1,54 @@
#include "sprite.hpp"
#include <utility>
#include <vector> // Para vector
#include "texture.hpp" // Para Texture
// Constructor
Sprite::Sprite(std::shared_ptr<Texture> texture, float pos_x, float pos_y, float width, float height)
: textures_{std::move(texture)},
pos_((SDL_FRect){.x = pos_x, .y = pos_y, .w = width, .h = height}),
sprite_clip_((SDL_FRect){.x = 0, .y = 0, .w = pos_.w, .h = pos_.h}) {}
Sprite::Sprite(std::shared_ptr<Texture> texture, SDL_FRect rect)
: textures_{std::move(texture)},
pos_(rect),
sprite_clip_((SDL_FRect){.x = 0, .y = 0, .w = pos_.w, .h = pos_.h}) {}
Sprite::Sprite(std::shared_ptr<Texture> texture)
: textures_{std::move(texture)},
pos_(SDL_FRect{.x = 0, .y = 0, .w = static_cast<float>(textures_.at(texture_index_)->getWidth()), .h = static_cast<float>(textures_.at(texture_index_)->getHeight())}),
sprite_clip_(pos_) {}
// Muestra el sprite por pantalla
void Sprite::render() {
textures_.at(texture_index_)->render(pos_.x, pos_.y, &sprite_clip_, zoom_, zoom_);
}
// Establece la posición del objeto
void Sprite::setPosition(float pos_x, float pos_y) {
pos_.x = pos_x;
pos_.y = pos_y;
}
// Establece la posición del objeto
void Sprite::setPosition(SDL_FPoint point) {
pos_.x = point.x;
pos_.y = point.y;
}
// Reinicia las variables a cero
void Sprite::clear() {
pos_ = {.x = 0, .y = 0, .w = 0, .h = 0};
sprite_clip_ = {.x = 0, .y = 0, .w = 0, .h = 0};
}
// Cambia la textura activa por índice
auto Sprite::setActiveTexture(size_t index) -> bool {
if (index < textures_.size()) {
texture_index_ = index;
}
return index < textures_.size();
}
+72
View File
@@ -0,0 +1,72 @@
#pragma once
#include <SDL3/SDL.h> // Para SDL_FRect, SDL_FPoint
#include <cstddef> // Para size_t
#include <memory> // Para shared_ptr
#include <utility>
#include <vector> // Para vector
class Texture;
// --- Clase Sprite: representa un objeto gráfico básico con posición, tamaño y textura ---
class Sprite {
public:
// --- Constructores y destructor ---
Sprite(std::shared_ptr<Texture> texture, float pos_x, float pos_y, float width, float height);
Sprite(std::shared_ptr<Texture> texture, SDL_FRect rect);
explicit Sprite(std::shared_ptr<Texture> texture);
virtual ~Sprite() = default;
// --- Renderizado y control ---
virtual void render(); // Muestra el sprite por pantalla
virtual void clear(); // Reinicia las variables a cero
// --- Getters de posición y tamaño ---
[[nodiscard]] auto getX() const -> float { return pos_.x; }
[[nodiscard]] auto getY() const -> float { return pos_.y; }
[[nodiscard]] auto getWidth() const -> float { return pos_.w; }
[[nodiscard]] auto getHeight() const -> float { return pos_.h; }
[[nodiscard]] auto getPosition() const -> SDL_FRect { return pos_; }
auto getRect() -> SDL_FRect& { return pos_; }
// --- Setters de posición y tamaño ---
void setX(float pos_x) { pos_.x = pos_x; }
void setY(float pos_y) { pos_.y = pos_y; }
void setWidth(float width) { pos_.w = width; }
void setHeight(float height) { pos_.h = height; }
void setPosition(float pos_x, float pos_y);
void setPosition(SDL_FPoint point);
void setPosition(SDL_FRect rect) { pos_ = rect; }
// --- Zoom ---
void setZoom(float zoom) { zoom_ = zoom; }
// --- Modificación de posición ---
void incX(float value) { pos_.x += value; }
void incY(float value) { pos_.y += value; }
// --- Sprite clip ---
[[nodiscard]] auto getSpriteClip() const -> SDL_FRect { return sprite_clip_; }
void setSpriteClip(SDL_FRect rect) { sprite_clip_ = rect; }
void setSpriteClip(float pos_x, float pos_y, float width, float height) { sprite_clip_ = SDL_FRect{.x = pos_x, .y = pos_y, .w = width, .h = height}; }
// --- Textura ---
[[nodiscard]] auto getTexture() const -> std::shared_ptr<Texture> { return textures_.at(texture_index_); }
void setTexture(std::shared_ptr<Texture> texture) { textures_.at(texture_index_) = std::move(texture); }
void addTexture(const std::shared_ptr<Texture>& texture) { textures_.push_back(texture); }
auto setActiveTexture(size_t index) -> bool; // Cambia la textura activa por índice
[[nodiscard]] auto getActiveTexture() const -> size_t { return texture_index_; } // Alias para getActiveTextureIndex
[[nodiscard]] auto getTextureCount() const -> size_t { return textures_.size(); } // Obtiene el número total de texturas
protected:
// --- Métodos internos ---
auto getTextureRef() -> std::shared_ptr<Texture>& { return textures_.at(texture_index_); } // Obtiene referencia a la textura activa
// --- Variables internas ---
std::vector<std::shared_ptr<Texture>> textures_; // Lista de texturas
size_t texture_index_ = 0; // Índice de la textura activa
SDL_FRect pos_; // Posición y tamaño donde dibujar el sprite
SDL_FRect sprite_clip_; // Rectángulo de origen de la textura que se dibujará en pantalla
double zoom_ = 1.0F; // Zoom aplicado a la textura
};