diff --git a/source/s_animated_sprite.cpp b/source/s_animated_sprite.cpp new file mode 100644 index 0000000..25fa1a2 --- /dev/null +++ b/source/s_animated_sprite.cpp @@ -0,0 +1,269 @@ +#include "s_animated_sprite.h" +#include // Para size_t +#include // Para basic_ostream, basic_istream, operator<<, basic... +#include // Para cout, cerr +#include // Para basic_stringstream +#include // Para runtime_error +#include "surface.h" // Para Surface +#include "utils.h" // Para printWithDots + +// Carga las animaciones en un vector(Animations) desde un fichero +Animations loadAnimationsFromFile(const std::string &file_path) +{ + std::ifstream file(file_path); + if (!file) + { + std::cerr << "Error: Fichero no encontrado " << file_path << std::endl; + throw std::runtime_error("Fichero no encontrado: " + file_path); + } + + printWithDots("Animation : ", file_path.substr(file_path.find_last_of("\\/") + 1), "[ LOADED ]"); + + std::vector buffer; + std::string line; + while (std::getline(file, line)) + { + if (!line.empty()) + buffer.push_back(line); + } + + return buffer; +} + +// Constructor +SAnimatedSprite::SAnimatedSprite(std::shared_ptr surface, const std::string &file_path) + : SMovingSprite(surface) +{ + // Carga las animaciones + if (!file_path.empty()) + { + Animations v = loadAnimationsFromFile(file_path); + setAnimations(v); + } +} + +// Constructor +SAnimatedSprite::SAnimatedSprite(std::shared_ptr surface, const Animations &animations) + : SMovingSprite(surface) +{ + if (!animations.empty()) + { + setAnimations(animations); + } +} + +// Obtiene el indice de la animación a partir del nombre +int SAnimatedSprite::getIndex(const std::string &name) +{ + auto index = -1; + + for (const auto &a : animations_) + { + index++; + if (a.name == name) + { + return index; + } + } + std::cout << "** Warning: could not find \"" << name.c_str() << "\" animation" << std::endl; + return -1; +} + +// Calcula el frame correspondiente a la animación +void SAnimatedSprite::animate() +{ + if (animations_[current_animation_].speed == 0) + { + return; + } + + // Calcula el frame actual a partir del contador + animations_[current_animation_].current_frame = animations_[current_animation_].counter / animations_[current_animation_].speed; + + // Si alcanza el final de la animación, reinicia el contador de la animación + // en función de la variable loop y coloca el nuevo frame + if (animations_[current_animation_].current_frame >= static_cast(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(); + animations_[current_animation_].completed = true; + } + else + { // Si hay loop, vuelve al frame indicado + animations_[current_animation_].counter = 0; + animations_[current_animation_].current_frame = animations_[current_animation_].loop; + } + } + // En caso contrario + else + { + // Escoge el frame correspondiente de la animación + setClip(animations_[current_animation_].frames[animations_[current_animation_].current_frame]); + + // Incrementa el contador de la animacion + animations_[current_animation_].counter++; + } +} + +// Comprueba si ha terminado la animación +bool SAnimatedSprite::animationIsCompleted() +{ + return animations_[current_animation_].completed; +} + +// Establece la animacion actual +void SAnimatedSprite::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_].counter = 0; + animations_[current_animation_].completed = false; + setClip(animations_[current_animation_].frames[animations_[current_animation_].current_frame]); + } +} + +// Establece la animacion actual +void SAnimatedSprite::setCurrentAnimation(int index) +{ + const auto new_animation = index; + if (current_animation_ != new_animation) + { + current_animation_ = new_animation; + animations_[current_animation_].current_frame = 0; + animations_[current_animation_].counter = 0; + animations_[current_animation_].completed = false; + setClip(animations_[current_animation_].frames[animations_[current_animation_].current_frame]); + } +} + +// Actualiza las variables del objeto +void SAnimatedSprite::update() +{ + animate(); + SMovingSprite::update(); +} + +// Reinicia la animación +void SAnimatedSprite::resetAnimation() +{ + animations_[current_animation_].current_frame = 0; + animations_[current_animation_].counter = 0; + animations_[current_animation_].completed = false; +} + +// Carga la animación desde un vector de cadenas +void SAnimatedSprite::setAnimations(const Animations &animations) +{ + int frame_width = 1; + int frame_height = 1; + int frames_per_row = 1; + int max_tiles = 1; + + size_t index = 0; + while (index < animations.size()) + { + std::string line = animations.at(index); + + // Parsea el fichero para buscar variables y valores + if (line != "[animation]") + { + // Encuentra la posición del caracter '=' + size_t pos = line.find("="); + + // Procesa las dos subcadenas + if (pos != std::string::npos) + { + std::string key = line.substr(0, pos); + int value = std::stoi(line.substr(pos + 1)); + if (key == "frame_width") + frame_width = value; + else if (key == "frame_height") + frame_height = value; + else + std::cout << "Warning: unknown parameter " << key << std::endl; + + frames_per_row = surface_->getWidth() / frame_width; + const int w = surface_->getWidth() / frame_width; + const int h = surface_->getHeight() / frame_height; + max_tiles = w * h; + } + } + + // Si la linea contiene el texto [animation] se realiza el proceso de carga de una animación + if (line == "[animation]") + { + AnimationData animation; + do + { + index++; + line = animations.at(index); + size_t pos = line.find("="); + + if (pos != std::string::npos) + { + 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::stoi(value); + else if (key == "loop") + animation.loop = std::stoi(value); + else if (key == "frames") + { + // Se introducen los valores separados por comas en un vector + std::stringstream ss(value); + std::string tmp; + SDL_Rect rect = {0, 0, frame_width, frame_height}; + while (getline(ss, tmp, ',')) + { + // Comprueba que el tile no sea mayor que el maximo indice permitido + const int num_tile = std::stoi(tmp); + if (num_tile <= max_tiles) + { + rect.x = (num_tile % frames_per_row) * frame_width; + rect.y = (num_tile / frames_per_row) * frame_height; + animation.frames.emplace_back(rect); + } + } + } + + else + std::cout << "Warning: unknown parameter " << key << std::endl; + } + } while (line != "[/animation]"); + + // Añade la animación al vector de animaciones + animations_.emplace_back(animation); + } + + // Una vez procesada la linea, aumenta el indice para pasar a la siguiente + index++; + } + + // Pone un valor por defecto + setWidth(frame_width); + setHeight(frame_height); +} + +// Establece el frame actual de la animación +void SAnimatedSprite::setCurrentAnimationFrame(int num) +{ + // Descarta valores fuera de rango + if (num < 0 || num >= static_cast(animations_[current_animation_].frames.size())) + { + num = 0; + } + + // Cambia el valor de la variable + animations_[current_animation_].current_frame = num; + animations_[current_animation_].counter = 0; + + // Escoge el frame correspondiente de la animación + setClip(animations_[current_animation_].frames[animations_[current_animation_].current_frame]); +} \ No newline at end of file diff --git a/source/s_animated_sprite.h b/source/s_animated_sprite.h new file mode 100644 index 0000000..21f3022 --- /dev/null +++ b/source/s_animated_sprite.h @@ -0,0 +1,72 @@ +#pragma once + +#include // Para SDL_Rect +#include // Para shared_ptr +#include // Para string +#include // Para vector +#include "s_moving_sprite.h" // Para SMovingSprite +class Surface; // lines 9-9 + +struct AnimationData +{ + std::string name; // Nombre de la animacion + std::vector frames; // Cada uno de los frames que componen la animación + int speed; // Velocidad de la animación + int loop; // Indica a que frame vuelve la animación al terminar. -1 para que no vuelva + bool completed; // Indica si ha finalizado la animación + int current_frame; // Frame actual + int counter; // Contador para las animaciones + + AnimationData() : name(std::string()), speed(5), loop(0), completed(false), current_frame(0), counter(0) {} +}; + +using Animations = std::vector; + +// Carga las animaciones en un vector(Animations) desde un fichero +Animations loadAnimationsFromFile(const std::string &file_path); + +class SAnimatedSprite : public SMovingSprite +{ +protected: + // Variables + std::vector animations_; // Vector con las diferentes animaciones + int current_animation_ = 0; // Animacion activa + + // Calcula el frame correspondiente a la animación actual + void animate(); + + // Carga la animación desde un vector de cadenas + void setAnimations(const Animations &animations); + +public: + // Constructor + SAnimatedSprite(std::shared_ptr surface, const std::string &file_path); + SAnimatedSprite(std::shared_ptr surface, const Animations &animations); + explicit SAnimatedSprite(std::shared_ptr surface) + : SMovingSprite(surface) {} + + // Destructor + virtual ~SAnimatedSprite() = default; + + // Actualiza las variables del objeto + void update() override; + + // Comprueba si ha terminado la animación + bool animationIsCompleted(); + + // Obtiene el indice de la animación a partir del nombre + int getIndex(const std::string &name); + + // Establece la animacion actual + void setCurrentAnimation(const std::string &name = "default"); + void setCurrentAnimation(int index = 0); + + // Reinicia la animación + void resetAnimation(); + + // Establece el frame actual de la animación + void setCurrentAnimationFrame(int num); + + // Obtiene el numero de frames de la animación actual + int getCurrentAnimationSize() { return static_cast(animations_[current_animation_].frames.size()); } +}; \ No newline at end of file diff --git a/source/s_moving_sprite.cpp b/source/s_moving_sprite.cpp new file mode 100644 index 0000000..6b0a3fa --- /dev/null +++ b/source/s_moving_sprite.cpp @@ -0,0 +1,96 @@ +#include "s_moving_sprite.h" +#include "surface.h" // for Surface + +// Constructor +SMovingSprite::SMovingSprite(std::shared_ptr surface, SDL_Rect pos, SDL_RendererFlip flip) + : SSprite(surface, pos), + x_(pos.x), + y_(pos.y), + flip_(flip) { SSprite::pos_ = pos; } + +SMovingSprite::SMovingSprite(std::shared_ptr surface, SDL_Rect pos) + : SSprite(surface, pos), + x_(pos.x), + y_(pos.y), + flip_(SDL_FLIP_NONE) { SSprite::pos_ = pos; } + +SMovingSprite::SMovingSprite(std::shared_ptr surface) + : SSprite(surface), + x_(0.0f), + y_(0.0f), + flip_(SDL_FLIP_NONE) { SSprite::clear(); } + +// Reinicia todas las variables +void SMovingSprite::clear() +{ + 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 + + flip_ = SDL_FLIP_NONE; // Establece como se ha de voltear el sprite + + SSprite::clear(); +} + +// Mueve el sprite +void SMovingSprite::move() +{ + x_ += vx_; + y_ += vy_; + + vx_ += ax_; + vy_ += ay_; + + pos_.x = static_cast(x_); + pos_.y = static_cast(y_); +} + +// Actualiza las variables internas del objeto +void SMovingSprite::update() +{ + move(); +} + +// Muestra el sprite por pantalla +void SMovingSprite::render() +{ + surface_->render(pos_.x, pos_.y, &clip_, flip_); +} + +// Establece la posición y_ el tamaño del objeto +void SMovingSprite::setPos(SDL_Rect rect) +{ + x_ = static_cast(rect.x); + y_ = static_cast(rect.y); + + pos_ = rect; +} + +// Establece el valor de las variables +void SMovingSprite::setPos(float x, float y) +{ + x_ = x; + y_ = y; + + pos_.x = static_cast(x_); + pos_.y = static_cast(y_); +} + +// Establece el valor de la variable +void SMovingSprite::setPosX(float value) +{ + x_ = value; + pos_.x = static_cast(x_); +} + +// Establece el valor de la variable +void SMovingSprite::setPosY(float value) +{ + y_ = value; + pos_.y = static_cast(y_); +} \ No newline at end of file diff --git a/source/s_moving_sprite.h b/source/s_moving_sprite.h new file mode 100644 index 0000000..58a179f --- /dev/null +++ b/source/s_moving_sprite.h @@ -0,0 +1,82 @@ +#pragma once + +#include // for SDL_Rect, SDL_Point +#include // for SDL_RendererFlip, SDL_FLIP_HORIZONTAL +#include // for max +#include // for shared_ptr +#include "s_sprite.h" // for SSprite +class Surface; // lines 9-9 + +// Clase SMovingSprite. Añade movimiento y flip al sprite +class SMovingSprite : public SSprite +{ +public: + +protected: + float x_; // Posición en el eje X + float y_; // Posición en el eje Y + + float vx_ = 0.0f; // Velocidad en el eje X. Cantidad de pixeles a desplazarse + float vy_ = 0.0f; // Velocidad en el eje Y. Cantidad de pixeles a desplazarse + + float ax_ = 0.0f; // Aceleración en el eje X. Variación de la velocidad + float ay_ = 0.0f; // Aceleración en el eje Y. Variación de la velocidad + + SDL_RendererFlip flip_; // Indica como se voltea el sprite + + // Mueve el sprite + void move(); + +public: + // Constructor + SMovingSprite(std::shared_ptr surface, SDL_Rect pos, SDL_RendererFlip flip); + SMovingSprite(std::shared_ptr surface, SDL_Rect pos); + explicit SMovingSprite(std::shared_ptr surface); + + // Destructor + virtual ~SMovingSprite() = default; + + // Actualiza las variables internas del objeto + virtual void update(); + + // Reinicia todas las variables a cero + void clear() override; + + // Muestra el sprite por pantalla + void render() override; + + // Obtiene la variable + float getPosX() const { return x_; } + float getPosY() const { return y_; } + float getVelX() const { return vx_; } + float getVelY() const { return vy_; } + float getAccelX() const { return ax_; } + float getAccelY() const { return ay_; } + + // Establece la variable + void setVelX(float value) { vx_ = value; } + void setVelY(float value) { vy_ = value; } + void setAccelX(float value) { ax_ = value; } + void setAccelY(float value) { ay_ = value; } + + // Establece el valor de la variable + void setFlip(SDL_RendererFlip flip) { flip_ = flip; } + + // Gira el sprite horizontalmente + void flip() { flip_ = (flip_ == SDL_FLIP_HORIZONTAL) ? SDL_FLIP_NONE : SDL_FLIP_HORIZONTAL; } + + // Obtiene el valor de la variable + SDL_RendererFlip getFlip() { return flip_; } + + // Establece la posición y_ el tamaño del objeto + void setPos(SDL_Rect rect); + + // Establece el valor de las variables + void setPos(float x, float y); + + // Establece el valor de la variable + void setPosX(float value); + + // Establece el valor de la variable + void setPosY(float value); +}; \ No newline at end of file diff --git a/source/s_sprite.cpp b/source/s_sprite.cpp new file mode 100644 index 0000000..ecfec3b --- /dev/null +++ b/source/s_sprite.cpp @@ -0,0 +1,45 @@ +#include "s_sprite.h" +#include "surface.h" // Para Surface + +// Constructor +SSprite::SSprite(std::shared_ptr surface, int x, int y, int w, int h) + : surface_(surface), + pos_((SDL_Rect){x, y, w, h}), + clip_((SDL_Rect){0, 0, pos_.w, pos_.h}) {} + +SSprite::SSprite(std::shared_ptr surface, SDL_Rect rect) + : surface_(surface), + pos_(rect), + clip_((SDL_Rect){0, 0, pos_.w, pos_.h}) {} + +SSprite::SSprite(std::shared_ptr surface) + : surface_(surface), + pos_({0, 0, surface_->getWidth(), surface_->getHeight()}), + clip_(pos_) {} + +// Muestra el sprite por pantalla +void SSprite::render() +{ + surface_->render(pos_.x, pos_.y, &clip_); +} + +// Establece la posición del objeto +void SSprite::setPosition(int x, int y) +{ + pos_.x = x; + pos_.y = y; +} + +// Establece la posición del objeto +void SSprite::setPosition(SDL_Point p) +{ + pos_.x = p.x; + pos_.y = p.y; +} + +// Reinicia las variables a cero +void SSprite::clear() +{ + pos_ = {0, 0, 0, 0}; + clip_ = {0, 0, 0, 0}; +} \ No newline at end of file diff --git a/source/s_sprite.h b/source/s_sprite.h new file mode 100644 index 0000000..15c6a35 --- /dev/null +++ b/source/s_sprite.h @@ -0,0 +1,68 @@ +#pragma once + +#include // Para SDL_Rect, SDL_Point +#include // Para shared_ptr +class Surface; + +// Clase SSprite +class SSprite +{ +protected: + // Variables + std::shared_ptr surface_; // Surface donde estan todos los dibujos del sprite + SDL_Rect pos_; // Posición y tamaño donde dibujar el sprite + SDL_Rect clip_; // Rectangulo de origen de la surface que se dibujará en pantalla + +public: + // Constructor + SSprite(std::shared_ptr, int x, int y, int w, int h); + SSprite(std::shared_ptr, SDL_Rect rect); + explicit SSprite(std::shared_ptr); + + // Destructor + virtual ~SSprite() = default; + + // Muestra el sprite por pantalla + virtual void render(); + + // Reinicia las variables a cero + virtual void clear(); + + // Obtiene la posición y el tamaño + int getX() const { return pos_.x; } + int getY() const { return pos_.y; } + int getWidth() const { return pos_.w; } + int getHeight() const { return pos_.h; } + + // Devuelve el rectangulo donde está el sprite + SDL_Rect getPosition() const { return pos_; } + SDL_Rect &getRect() { return pos_; } + + // Establece la posición y el tamaño + void setX(int x) { pos_.x = x; } + void setY(int y) { pos_.y = y; } + void setWidth(int w) { pos_.w = w; } + void setHeight(int h) { pos_.h = h; } + + // Establece la posición del objeto + void setPosition(int x, int y); + void setPosition(SDL_Point p); + void setPosition(SDL_Rect r) { pos_ = r; } + + // Aumenta o disminuye la posición + void incX(int value) { pos_.x += value; } + void incY(int value) { pos_.y += value; } + + // Obtiene el rectangulo que se dibuja de la surface + SDL_Rect getClip() const { return clip_; } + + // Establece el rectangulo que se dibuja de la surface + void setClip(SDL_Rect rect) { clip_ = rect; } + void setClip(int x, int y, int w, int h) { clip_ = (SDL_Rect){x, y, w, h}; } + + // Obtiene un puntero a la surface + std::shared_ptr getSurface() const { return surface_; } + + // Establece la surface a utilizar + void setSurface(std::shared_ptr surface) { surface_ = surface; } +}; \ No newline at end of file