revisat motor de animacions
This commit is contained in:
@@ -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]
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
};
|
};
|
||||||
Reference in New Issue
Block a user