From d70edb29e7de0c19a7f68db27c04cd76ef62156a Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Wed, 8 Apr 2026 08:56:00 +0200 Subject: [PATCH] revisat motor de animacions --- data/player/player.yaml | 5 +- .../core/rendering/sprite/animated_sprite.cpp | 264 +++++------------- .../core/rendering/sprite/animated_sprite.hpp | 16 +- 3 files changed, 84 insertions(+), 201 deletions(-) diff --git a/data/player/player.yaml b/data/player/player.yaml index 65ef1aa..b88c2f8 100644 --- a/data/player/player.yaml +++ b/data/player/player.yaml @@ -13,7 +13,7 @@ animations: frames: [2, 3, 4, 1] - name: turn_walk - speed: [0.15, 0.07, 0.07, 0.07, 0.07] + speed: [0.1, 0.07, 0.07, 0.07, 0.07] loopFrom: 1 frames: [0, 2, 3, 4, 1] @@ -22,6 +22,3 @@ animations: - name: jump_peak frames: [5] - - - name: turn - frames: [0] diff --git a/source/core/rendering/sprite/animated_sprite.cpp b/source/core/rendering/sprite/animated_sprite.cpp index e3e267b..8269189 100644 --- a/source/core/rendering/sprite/animated_sprite.cpp +++ b/source/core/rendering/sprite/animated_sprite.cpp @@ -31,201 +31,95 @@ auto convertYAMLFramesToRects(const fkyaml::node& frames_node, float frame_width return frames; } -// Carga las animaciones desde un fichero YAML -auto AnimatedSprite::loadAnimationsFromYAML(const std::string& file_path, std::shared_ptr& surface, float& frame_width, float& frame_height) -> std::vector { // NOLINT(readability-convert-member-functions-to-static) - std::vector animations; +// Helper: parsea el array de animaciones de un nodo YAML +static auto parseAnimations(const fkyaml::node& yaml, float frame_width, float frame_height, int frames_per_row, int max_tiles) -> std::vector { + std::vector animations; + if (!yaml.contains("animations") || !yaml["animations"].is_sequence()) { + return animations; + } - // Extract filename for logging - const std::string FILE_NAME = file_path.substr(file_path.find_last_of("\\/") + 1); + for (const auto& anim_node : yaml["animations"]) { + AnimatedSprite::AnimationData animation; - try { - // Load YAML file using ResourceHelper (supports both filesystem and pack) - auto file_data = Resource::Helper::loadFile(file_path); - - if (file_data.empty()) { - std::cerr << "Error: Unable to load animation file " << FILE_NAME << '\n'; - throw std::runtime_error("Animation file not found: " + file_path); + if (anim_node.contains("name")) { + animation.name = anim_node["name"].get_value(); } - printWithDots("Animation : ", FILE_NAME, "[ LOADED ]"); - - // Parse YAML from string - std::string yaml_content(file_data.begin(), file_data.end()); - auto yaml = fkyaml::node::deserialize(yaml_content); - - // --- Parse global configuration --- - if (yaml.contains("tileSetFile")) { - auto tile_set_file = yaml["tileSetFile"].get_value(); - surface = Resource::Cache::get()->getSurface(tile_set_file); + if (anim_node.contains("frames") && anim_node["frames"].is_sequence()) { + animation.frames = convertYAMLFramesToRects( + anim_node["frames"], + frame_width, + frame_height, + frames_per_row, + max_tiles); } - if (yaml.contains("frameWidth")) { - frame_width = static_cast(yaml["frameWidth"].get_value()); - } - - if (yaml.contains("frameHeight")) { - frame_height = static_cast(yaml["frameHeight"].get_value()); - } - - // Calculate sprite sheet parameters - int frames_per_row = 1; - int max_tiles = 1; - if (surface) { - frames_per_row = surface->getWidth() / static_cast(frame_width); - const int W = surface->getWidth() / static_cast(frame_width); - const int H = surface->getHeight() / static_cast(frame_height); - max_tiles = W * H; - } - - // --- Parse animations array --- - if (yaml.contains("animations") && yaml["animations"].is_sequence()) { - const auto& animations_node = yaml["animations"]; - - for (const auto& anim_node : animations_node) { - AnimationData animation; - - // Parse animation name - if (anim_node.contains("name")) { - animation.name = anim_node["name"].get_value(); + if (anim_node.contains("speed")) { + const auto& speed_node = anim_node["speed"]; + if (speed_node.is_sequence()) { + for (const auto& s : speed_node) { + animation.speeds.push_back(s.get_value()); } - - // Parse frames array (antes de speeds, para saber cuántos frames hay) - if (anim_node.contains("frames") && anim_node["frames"].is_sequence()) { - animation.frames = convertYAMLFramesToRects( - anim_node["frames"], - frame_width, - frame_height, - frames_per_row, - max_tiles); + } else { + float spd = speed_node.get_value(); + if (spd > 0.0F) { + animation.speeds.assign(animation.frames.size(), spd); } - - // Parse speed: escalar (uniforme) o array (por frame) - if (anim_node.contains("speed")) { - const auto& speed_node = anim_node["speed"]; - if (speed_node.is_sequence()) { - for (const auto& s : speed_node) { - animation.speeds.push_back(s.get_value()); - } - } else { - float spd = speed_node.get_value(); - if (spd > 0.0F) { - animation.speeds.assign(animation.frames.size(), spd); - } - } - } - - // Parse loopFrom - if (anim_node.contains("loopFrom")) { - animation.loop_from = anim_node["loopFrom"].get_value(); - } - - animations.push_back(animation); } } - } catch (const fkyaml::exception& e) { - std::cerr << "YAML parsing error in " << FILE_NAME << ": " << e.what() << '\n'; - throw; - } catch (const std::exception& e) { - std::cerr << "Error loading animation " << FILE_NAME << ": " << e.what() << '\n'; - throw; - } + if (anim_node.contains("loopFrom")) { + animation.loop_from = anim_node["loopFrom"].get_value(); + } + animations.push_back(animation); + } return animations; } -// Constructor con bytes YAML del cache (parsing lazy) +// Helper: parsea la configuración global (tileSetFile, frameWidth, frameHeight) y calcula parámetros del spritesheet +struct SheetParams { + float frame_width{0.0F}; + float frame_height{0.0F}; + int frames_per_row{1}; + int max_tiles{1}; +}; + +static auto parseGlobalConfig(const fkyaml::node& yaml, std::shared_ptr& surface) -> SheetParams { + SheetParams params; + if (yaml.contains("tileSetFile")) { + surface = Resource::Cache::get()->getSurface(yaml["tileSetFile"].get_value()); + } + if (yaml.contains("frameWidth")) { + params.frame_width = static_cast(yaml["frameWidth"].get_value()); + } + if (yaml.contains("frameHeight")) { + params.frame_height = static_cast(yaml["frameHeight"].get_value()); + } + if (surface && params.frame_width > 0.0F && params.frame_height > 0.0F) { + params.frames_per_row = surface->getWidth() / static_cast(params.frame_width); + const int H = surface->getHeight() / static_cast(params.frame_height); + params.max_tiles = params.frames_per_row * H; + } + return params; +} + +// Constructor con bytes YAML del cache AnimatedSprite::AnimatedSprite(const AnimationResource& cached_data) { - // Parsear YAML desde los bytes cargados en cache std::string yaml_content(cached_data.yaml_data.begin(), cached_data.yaml_data.end()); try { auto yaml = fkyaml::node::deserialize(yaml_content); + auto params = parseGlobalConfig(yaml, surface_); + animations_ = parseAnimations(yaml, params.frame_width, params.frame_height, params.frames_per_row, params.max_tiles); + buildNameIndex(); - // Variables para almacenar configuración global - float frame_width = 0.0F; - float frame_height = 0.0F; + setWidth(params.frame_width); + setHeight(params.frame_height); - // --- Parse global configuration --- - if (yaml.contains("tileSetFile")) { - auto tile_set_file = yaml["tileSetFile"].get_value(); - // Ahora SÍ podemos acceder al cache (ya está completamente cargado) - surface_ = Resource::Cache::get()->getSurface(tile_set_file); - } - - if (yaml.contains("frameWidth")) { - frame_width = static_cast(yaml["frameWidth"].get_value()); - } - - if (yaml.contains("frameHeight")) { - frame_height = static_cast(yaml["frameHeight"].get_value()); - } - - // Calculate sprite sheet parameters - int frames_per_row = 1; - int max_tiles = 1; - if (surface_) { - frames_per_row = surface_->getWidth() / static_cast(frame_width); - const int W = surface_->getWidth() / static_cast(frame_width); - const int H = surface_->getHeight() / static_cast(frame_height); - max_tiles = W * H; - } - - // --- Parse animations array --- - if (yaml.contains("animations") && yaml["animations"].is_sequence()) { - const auto& animations_node = yaml["animations"]; - - for (const auto& anim_node : animations_node) { - AnimationData animation; - - // Parse animation name - if (anim_node.contains("name")) { - animation.name = anim_node["name"].get_value(); - } - - // Parse frames array (antes de speeds, para saber cuántos frames hay) - if (anim_node.contains("frames") && anim_node["frames"].is_sequence()) { - animation.frames = convertYAMLFramesToRects( - anim_node["frames"], - frame_width, - frame_height, - frames_per_row, - max_tiles); - } - - // Parse speed: escalar (uniforme) o array (por frame) - if (anim_node.contains("speed")) { - const auto& speed_node = anim_node["speed"]; - if (speed_node.is_sequence()) { - for (const auto& s : speed_node) { - animation.speeds.push_back(s.get_value()); - } - } else { - float spd = speed_node.get_value(); - if (spd > 0.0F) { - animation.speeds.assign(animation.frames.size(), spd); - } - } - } - - // Parse loopFrom - if (anim_node.contains("loopFrom")) { - animation.loop_from = anim_node["loopFrom"].get_value(); - } - - animations_.push_back(animation); - } - } - - // Set dimensions - setWidth(frame_width); - setHeight(frame_height); - - // Inicializar con la primera animación si existe if (!animations_.empty() && !animations_[0].frames.empty()) { setClip(animations_[0].frames[0]); } - } catch (const fkyaml::exception& e) { std::cerr << "YAML parsing error in animation " << cached_data.name << ": " << e.what() << '\n'; throw; @@ -244,18 +138,12 @@ AnimatedSprite::AnimatedSprite(std::shared_ptr surface, SDL_FRect pos) } } -// Obtiene el indice de la animación a partir del nombre -auto AnimatedSprite::getIndex(const std::string& name) -> int { // NOLINT(readability-convert-member-functions-to-static) - auto index = -1; - - for (const auto& a : animations_) { - index++; - if (a.name == name) { - return index; - } +// Construye el mapa nombre→índice para búsqueda O(1) +void AnimatedSprite::buildNameIndex() { + animation_index_.clear(); + for (int i = 0; i < static_cast(animations_.size()); ++i) { + animation_index_[animations_[i].name] = i; } - std::cout << "** Warning: could not find \"" << name.c_str() << "\" animation" << '\n'; - return -1; } // Calcula el frame correspondiente a la animación (time-based) @@ -307,16 +195,14 @@ auto AnimatedSprite::animationIsCompleted() -> bool { return animations_[current_animation_].completed; } -// Establece la animacion actual +// Establece la animacion actual por nombre void AnimatedSprite::setCurrentAnimation(const std::string& name) { - const auto NEW_ANIMATION = getIndex(name); - if (current_animation_ != NEW_ANIMATION) { - current_animation_ = NEW_ANIMATION; - animations_[current_animation_].current_frame = 0; - animations_[current_animation_].accumulated_time = 0.0F; - animations_[current_animation_].completed = false; - setClip(animations_[current_animation_].frames[animations_[current_animation_].current_frame]); + auto it = animation_index_.find(name); + if (it == animation_index_.end()) { + std::cout << "** Warning: could not find \"" << name << "\" animation" << '\n'; + return; } + setCurrentAnimation(it->second); } // Establece la animacion actual diff --git a/source/core/rendering/sprite/animated_sprite.hpp b/source/core/rendering/sprite/animated_sprite.hpp index 665c403..1b4b948 100644 --- a/source/core/rendering/sprite/animated_sprite.hpp +++ b/source/core/rendering/sprite/animated_sprite.hpp @@ -2,8 +2,9 @@ #include -#include // Para shared_ptr -#include // Para string +#include // Para shared_ptr +#include // Para string +#include // Para unordered_map #include #include // Para vector @@ -27,9 +28,6 @@ class AnimatedSprite : public MovingSprite { float accumulated_time{0.0F}; // Tiempo acumulado para las animaciones (time-based) }; - // Métodos estáticos - static auto loadAnimationsFromYAML(const std::string& file_path, std::shared_ptr& surface, float& frame_width, float& frame_height) -> std::vector; // Carga las animaciones desde fichero YAML - // Constructores explicit AnimatedSprite(const AnimationResource& cached_data); // Constructor con datos pre-cargados del cache @@ -39,7 +37,6 @@ class AnimatedSprite : public MovingSprite { // Consultas de estado auto animationIsCompleted() -> bool; // Comprueba si ha terminado la animación - auto getIndex(const std::string& name) -> int; // Obtiene el índice de la animación por nombre auto getCurrentAnimationSize() -> int { return static_cast(animations_[current_animation_].frames.size()); } // Número de frames de la animación actual // Modificadores de animación @@ -54,7 +51,10 @@ class AnimatedSprite : public MovingSprite { AnimatedSprite(std::shared_ptr surface, SDL_FRect pos); private: + void buildNameIndex(); // Construye el mapa nombre→índice + // Variables miembro - std::vector animations_; // Vector con las diferentes animaciones - int current_animation_{0}; // Animación activa + std::vector animations_; // Vector con las diferentes animaciones + std::unordered_map animation_index_; // Mapa nombre→índice para búsqueda O(1) + int current_animation_{0}; // Animación activa }; \ No newline at end of file