revisat motor de animacions

This commit is contained in:
2026-04-08 08:56:00 +02:00
parent 947bcceaf9
commit d70edb29e7
3 changed files with 84 additions and 201 deletions

View File

@@ -13,7 +13,7 @@ animations:
frames: [2, 3, 4, 1] frames: [2, 3, 4, 1]
- name: turn_walk - 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 loopFrom: 1
frames: [0, 2, 3, 4, 1] frames: [0, 2, 3, 4, 1]
@@ -22,6 +22,3 @@ animations:
- name: jump_peak - name: jump_peak
frames: [5] frames: [5]
- name: turn
frames: [0]

View File

@@ -31,65 +31,20 @@ auto convertYAMLFramesToRects(const fkyaml::node& frames_node, float frame_width
return frames; return frames;
} }
// Carga las animaciones desde un fichero YAML // Helper: parsea el array de animaciones de un nodo YAML
auto AnimatedSprite::loadAnimationsFromYAML(const std::string& file_path, std::shared_ptr<Surface>& surface, float& frame_width, float& frame_height) -> std::vector<AnimationData> { // NOLINT(readability-convert-member-functions-to-static) static auto parseAnimations(const fkyaml::node& yaml, float frame_width, float frame_height, int frames_per_row, int max_tiles) -> std::vector<AnimatedSprite::AnimationData> {
std::vector<AnimationData> animations; std::vector<AnimatedSprite::AnimationData> animations;
if (!yaml.contains("animations") || !yaml["animations"].is_sequence()) {
// Extract filename for logging return animations;
const std::string FILE_NAME = file_path.substr(file_path.find_last_of("\\/") + 1);
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);
} }
printWithDots("Animation : ", FILE_NAME, "[ LOADED ]"); for (const auto& anim_node : yaml["animations"]) {
AnimatedSprite::AnimationData animation;
// 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<std::string>();
surface = Resource::Cache::get()->getSurface(tile_set_file);
}
if (yaml.contains("frameWidth")) {
frame_width = static_cast<float>(yaml["frameWidth"].get_value<int>());
}
if (yaml.contains("frameHeight")) {
frame_height = static_cast<float>(yaml["frameHeight"].get_value<int>());
}
// Calculate sprite sheet parameters
int frames_per_row = 1;
int max_tiles = 1;
if (surface) {
frames_per_row = surface->getWidth() / static_cast<int>(frame_width);
const int W = surface->getWidth() / static_cast<int>(frame_width);
const int H = surface->getHeight() / static_cast<int>(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")) { if (anim_node.contains("name")) {
animation.name = anim_node["name"].get_value<std::string>(); animation.name = anim_node["name"].get_value<std::string>();
} }
// Parse frames array (antes de speeds, para saber cuántos frames hay)
if (anim_node.contains("frames") && anim_node["frames"].is_sequence()) { if (anim_node.contains("frames") && anim_node["frames"].is_sequence()) {
animation.frames = convertYAMLFramesToRects( animation.frames = convertYAMLFramesToRects(
anim_node["frames"], anim_node["frames"],
@@ -99,7 +54,6 @@ auto AnimatedSprite::loadAnimationsFromYAML(const std::string& file_path, std::s
max_tiles); max_tiles);
} }
// Parse speed: escalar (uniforme) o array (por frame)
if (anim_node.contains("speed")) { if (anim_node.contains("speed")) {
const auto& speed_node = anim_node["speed"]; const auto& speed_node = anim_node["speed"];
if (speed_node.is_sequence()) { if (speed_node.is_sequence()) {
@@ -114,118 +68,58 @@ auto AnimatedSprite::loadAnimationsFromYAML(const std::string& file_path, std::s
} }
} }
// Parse loopFrom
if (anim_node.contains("loopFrom")) { if (anim_node.contains("loopFrom")) {
animation.loop_from = anim_node["loopFrom"].get_value<int>(); animation.loop_from = anim_node["loopFrom"].get_value<int>();
} }
animations.push_back(animation); 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;
}
return animations; 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>& surface) -> SheetParams {
SheetParams params;
if (yaml.contains("tileSetFile")) {
surface = Resource::Cache::get()->getSurface(yaml["tileSetFile"].get_value<std::string>());
}
if (yaml.contains("frameWidth")) {
params.frame_width = static_cast<float>(yaml["frameWidth"].get_value<int>());
}
if (yaml.contains("frameHeight")) {
params.frame_height = static_cast<float>(yaml["frameHeight"].get_value<int>());
}
if (surface && params.frame_width > 0.0F && params.frame_height > 0.0F) {
params.frames_per_row = surface->getWidth() / static_cast<int>(params.frame_width);
const int H = surface->getHeight() / static_cast<int>(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) { 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()); std::string yaml_content(cached_data.yaml_data.begin(), cached_data.yaml_data.end());
try { try {
auto yaml = fkyaml::node::deserialize(yaml_content); 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 setWidth(params.frame_width);
float frame_width = 0.0F; setHeight(params.frame_height);
float frame_height = 0.0F;
// --- Parse global configuration ---
if (yaml.contains("tileSetFile")) {
auto tile_set_file = yaml["tileSetFile"].get_value<std::string>();
// 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<float>(yaml["frameWidth"].get_value<int>());
}
if (yaml.contains("frameHeight")) {
frame_height = static_cast<float>(yaml["frameHeight"].get_value<int>());
}
// Calculate sprite sheet parameters
int frames_per_row = 1;
int max_tiles = 1;
if (surface_) {
frames_per_row = surface_->getWidth() / static_cast<int>(frame_width);
const int W = surface_->getWidth() / static_cast<int>(frame_width);
const int H = surface_->getHeight() / static_cast<int>(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<std::string>();
}
// 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<float>());
}
} else {
float spd = speed_node.get_value<float>();
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<int>();
}
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()) { if (!animations_.empty() && !animations_[0].frames.empty()) {
setClip(animations_[0].frames[0]); setClip(animations_[0].frames[0]);
} }
} catch (const fkyaml::exception& e) { } catch (const fkyaml::exception& e) {
std::cerr << "YAML parsing error in animation " << cached_data.name << ": " << e.what() << '\n'; std::cerr << "YAML parsing error in animation " << cached_data.name << ": " << e.what() << '\n';
throw; throw;
@@ -244,18 +138,12 @@ AnimatedSprite::AnimatedSprite(std::shared_ptr<Surface> surface, SDL_FRect pos)
} }
} }
// Obtiene el indice de la animación a partir del nombre // Construye el mapa nombre→índice para búsqueda O(1)
auto AnimatedSprite::getIndex(const std::string& name) -> int { // NOLINT(readability-convert-member-functions-to-static) void AnimatedSprite::buildNameIndex() {
auto index = -1; animation_index_.clear();
for (int i = 0; i < static_cast<int>(animations_.size()); ++i) {
for (const auto& a : animations_) { animation_index_[animations_[i].name] = i;
index++;
if (a.name == name) {
return index;
} }
}
std::cout << "** Warning: could not find \"" << name.c_str() << "\" animation" << '\n';
return -1;
} }
// Calcula el frame correspondiente a la animación (time-based) // Calcula el frame correspondiente a la animación (time-based)
@@ -307,16 +195,14 @@ auto AnimatedSprite::animationIsCompleted() -> bool {
return animations_[current_animation_].completed; return animations_[current_animation_].completed;
} }
// Establece la animacion actual // Establece la animacion actual por nombre
void AnimatedSprite::setCurrentAnimation(const std::string& name) { void AnimatedSprite::setCurrentAnimation(const std::string& name) {
const auto NEW_ANIMATION = getIndex(name); auto it = animation_index_.find(name);
if (current_animation_ != NEW_ANIMATION) { if (it == animation_index_.end()) {
current_animation_ = NEW_ANIMATION; std::cout << "** Warning: could not find \"" << name << "\" animation" << '\n';
animations_[current_animation_].current_frame = 0; return;
animations_[current_animation_].accumulated_time = 0.0F;
animations_[current_animation_].completed = false;
setClip(animations_[current_animation_].frames[animations_[current_animation_].current_frame]);
} }
setCurrentAnimation(it->second);
} }
// Establece la animacion actual // Establece la animacion actual

View File

@@ -4,6 +4,7 @@
#include <memory> // Para shared_ptr #include <memory> // Para shared_ptr
#include <string> // Para string #include <string> // Para string
#include <unordered_map> // Para unordered_map
#include <utility> #include <utility>
#include <vector> // Para vector #include <vector> // Para vector
@@ -27,9 +28,6 @@ class AnimatedSprite : public MovingSprite {
float accumulated_time{0.0F}; // Tiempo acumulado para las animaciones (time-based) 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>& surface, float& frame_width, float& frame_height) -> std::vector<AnimationData>; // Carga las animaciones desde fichero YAML
// Constructores // Constructores
explicit AnimatedSprite(const AnimationResource& cached_data); // Constructor con datos pre-cargados del cache explicit AnimatedSprite(const AnimationResource& cached_data); // Constructor con datos pre-cargados del cache
@@ -39,7 +37,6 @@ class AnimatedSprite : public MovingSprite {
// Consultas de estado // Consultas de estado
auto animationIsCompleted() -> bool; // Comprueba si ha terminado la animación 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<int>(animations_[current_animation_].frames.size()); } // Número de frames de la animación actual auto getCurrentAnimationSize() -> int { return static_cast<int>(animations_[current_animation_].frames.size()); } // Número de frames de la animación actual
// Modificadores de animación // Modificadores de animación
@@ -54,7 +51,10 @@ class AnimatedSprite : public MovingSprite {
AnimatedSprite(std::shared_ptr<Surface> surface, SDL_FRect pos); AnimatedSprite(std::shared_ptr<Surface> surface, SDL_FRect pos);
private: private:
void buildNameIndex(); // Construye el mapa nombre→índice
// Variables miembro // Variables miembro
std::vector<AnimationData> animations_; // Vector con las diferentes animaciones std::vector<AnimationData> animations_; // Vector con las diferentes animaciones
std::unordered_map<std::string, int> animation_index_; // Mapa nombre→índice para búsqueda O(1)
int current_animation_{0}; // Animación activa int current_animation_{0}; // Animación activa
}; };