polimorfise d'enemics
moving platforms
This commit is contained in:
@@ -6,18 +6,13 @@
|
||||
|
||||
#include "core/rendering/sprite/animated_sprite.hpp" // Para SAnimatedSprite
|
||||
#include "core/resources/resource_cache.hpp" // Para Resource
|
||||
#include "utils/utils.hpp"
|
||||
#include "game/entities/path_enemy.hpp" // Para PathEnemy
|
||||
|
||||
// Constructor
|
||||
// Constructor base: configura sprite, collider, flip/mirror
|
||||
Enemy::Enemy(const Data& enemy)
|
||||
: sprite_(std::make_shared<AnimatedSprite>(Resource::Cache::get()->getAnimationData(enemy.animation_path))),
|
||||
x1_(enemy.x1),
|
||||
x2_(enemy.x2),
|
||||
y1_(enemy.y1),
|
||||
y2_(enemy.y2),
|
||||
should_flip_(enemy.flip),
|
||||
should_mirror_(enemy.mirror) {
|
||||
// Obten el resto de valores
|
||||
sprite_->setPosX(enemy.x);
|
||||
sprite_->setPosY(enemy.y);
|
||||
sprite_->setVelX(enemy.vx);
|
||||
@@ -36,74 +31,13 @@ void Enemy::render() {
|
||||
sprite_->render();
|
||||
}
|
||||
|
||||
// Actualiza las variables del objeto
|
||||
void Enemy::update(float delta_time) {
|
||||
sprite_->update(delta_time);
|
||||
checkPath();
|
||||
collider_ = getRect();
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
// Solo actualiza la animación sin mover al enemigo
|
||||
void Enemy::updateAnimation(float delta_time) {
|
||||
sprite_->animate(delta_time);
|
||||
}
|
||||
|
||||
// Resetea el enemigo a su posición inicial (para editor)
|
||||
void Enemy::resetToInitialPosition(const Data& data) {
|
||||
sprite_->setPosX(data.x);
|
||||
sprite_->setPosY(data.y);
|
||||
sprite_->setVelX(data.vx);
|
||||
sprite_->setVelY(data.vy);
|
||||
|
||||
x1_ = data.x1;
|
||||
x2_ = data.x2;
|
||||
y1_ = data.y1;
|
||||
y2_ = data.y2;
|
||||
|
||||
applyFlipMirror(data.vx);
|
||||
|
||||
collider_ = getRect();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Comprueba si ha llegado al limite del recorrido para darse media vuelta
|
||||
void Enemy::checkPath() { // NOLINT(readability-make-member-function-const)
|
||||
if (sprite_->getPosX() > x2_ || sprite_->getPosX() < x1_) {
|
||||
// Recoloca
|
||||
if (sprite_->getPosX() > x2_) {
|
||||
sprite_->setPosX(x2_);
|
||||
} else {
|
||||
sprite_->setPosX(x1_);
|
||||
}
|
||||
|
||||
// Cambia el sentido
|
||||
sprite_->setVelX(sprite_->getVelX() * (-1));
|
||||
|
||||
// Invierte el sprite
|
||||
if (should_flip_) {
|
||||
sprite_->flip();
|
||||
}
|
||||
}
|
||||
|
||||
if (sprite_->getPosY() > y2_ || sprite_->getPosY() < y1_) {
|
||||
// Recoloca
|
||||
if (sprite_->getPosY() > y2_) {
|
||||
sprite_->setPosY(y2_);
|
||||
} else {
|
||||
sprite_->setPosY(y1_);
|
||||
}
|
||||
|
||||
// Cambia el sentido
|
||||
sprite_->setVelY(sprite_->getVelY() * (-1));
|
||||
|
||||
// Invierte el sprite
|
||||
if (should_flip_) {
|
||||
sprite_->flip();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Aplica flip horizontal y/o mirror vertical al sprite
|
||||
void Enemy::applyFlipMirror(float vx) {
|
||||
const int FLIP = (should_flip_ && vx < 0.0F) ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE;
|
||||
@@ -119,4 +53,10 @@ auto Enemy::getRect() -> SDL_FRect {
|
||||
// Obtiene el rectangulo de colision del enemigo
|
||||
auto Enemy::getCollider() -> SDL_FRect& {
|
||||
return collider_;
|
||||
}
|
||||
}
|
||||
|
||||
// Factory: crea el subtipo de enemigo correcto según data.type
|
||||
auto Enemy::create(const Data& data) -> std::shared_ptr<Enemy> {
|
||||
// Por ahora solo existe PathEnemy; futuros tipos se añadirán aquí
|
||||
return std::make_shared<PathEnemy>(data);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ class AnimatedSprite;
|
||||
class Enemy {
|
||||
public:
|
||||
struct Data {
|
||||
std::string type{"path"}; // Tipo de enemigo (path, etc.)
|
||||
std::string animation_path; // Ruta al fichero con la animación
|
||||
float x{0.0F}; // Posición inicial en el eje X
|
||||
float y{0.0F}; // Posición inicial en el eje Y
|
||||
@@ -25,30 +26,26 @@ class Enemy {
|
||||
};
|
||||
|
||||
explicit Enemy(const Data& enemy); // Constructor
|
||||
~Enemy() = default; // Destructor
|
||||
virtual ~Enemy() = default; // Destructor virtual
|
||||
|
||||
void render(); // Pinta el enemigo en pantalla
|
||||
void update(float delta_time); // Actualiza las variables del objeto
|
||||
virtual void render(); // Pinta el enemigo en pantalla
|
||||
virtual void update(float delta_time) = 0; // Actualiza las variables del objeto (pura virtual)
|
||||
#ifdef _DEBUG
|
||||
void updateAnimation(float delta_time); // Solo actualiza la animación sin mover al enemigo
|
||||
void resetToInitialPosition(const Data& data); // Resetea el enemigo a su posición inicial (para editor)
|
||||
virtual void updateAnimation(float delta_time); // Solo actualiza la animación sin mover al enemigo
|
||||
virtual void resetToInitialPosition(const Data& data) = 0; // Resetea a posición inicial (pura virtual)
|
||||
#endif
|
||||
|
||||
auto getRect() -> SDL_FRect; // Devuelve el rectangulo que contiene al enemigo
|
||||
auto getCollider() -> SDL_FRect&; // Obtiene el rectangulo de colision del enemigo
|
||||
|
||||
private:
|
||||
// Factory: crea el subtipo de enemigo correcto según data.type
|
||||
static auto create(const Data& data) -> std::shared_ptr<Enemy>;
|
||||
|
||||
protected:
|
||||
void applyFlipMirror(float vx); // Aplica flip horizontal y/o mirror vertical al sprite
|
||||
void checkPath(); // Comprueba si ha llegado al limite del recorrido para darse media vuelta
|
||||
|
||||
std::shared_ptr<AnimatedSprite> sprite_; // Sprite del enemigo
|
||||
|
||||
// Variables
|
||||
int x1_{0}; // Limite izquierdo de la ruta en el eje X
|
||||
int x2_{0}; // Limite derecho de la ruta en el eje X
|
||||
int y1_{0}; // Limite superior de la ruta en el eje Y
|
||||
int y2_{0}; // Limite inferior de la ruta en el eje Y
|
||||
SDL_FRect collider_{}; // Caja de colisión
|
||||
bool should_flip_{false}; // Indica si el enemigo hace flip al terminar su ruta
|
||||
bool should_mirror_{false}; // Indica si el enemigo se dibuja volteado verticalmente
|
||||
SDL_FRect collider_{}; // Caja de colisión
|
||||
bool should_flip_{false}; // Indica si el enemigo hace flip al terminar su ruta
|
||||
bool should_mirror_{false}; // Indica si el enemigo se dibuja volteado verticalmente
|
||||
};
|
||||
|
||||
96
source/game/entities/moving_platform.cpp
Normal file
96
source/game/entities/moving_platform.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
#include "game/entities/moving_platform.hpp"
|
||||
|
||||
#include <cstdlib> // Para rand
|
||||
|
||||
#include "core/rendering/sprite/animated_sprite.hpp" // Para AnimatedSprite
|
||||
#include "core/resources/resource_cache.hpp" // Para Resource
|
||||
|
||||
// Constructor
|
||||
MovingPlatform::MovingPlatform(const Data& data)
|
||||
: sprite_(std::make_shared<AnimatedSprite>(Resource::Cache::get()->getAnimationData(data.animation_path))),
|
||||
x1_(data.x1),
|
||||
x2_(data.x2),
|
||||
y1_(data.y1),
|
||||
y2_(data.y2) {
|
||||
sprite_->setPosX(data.x);
|
||||
sprite_->setPosY(data.y);
|
||||
sprite_->setVelX(data.vx);
|
||||
sprite_->setVelY(data.vy);
|
||||
|
||||
collider_ = getRect();
|
||||
|
||||
// Coloca un frame al azar o el designado
|
||||
sprite_->setCurrentAnimationFrame((data.frame == -1) ? (rand() % sprite_->getCurrentAnimationSize()) : data.frame);
|
||||
}
|
||||
|
||||
// Actualiza posición, calcula desplazamiento real del frame
|
||||
void MovingPlatform::update(float delta_time) {
|
||||
float old_x = sprite_->getPosX();
|
||||
float old_y = sprite_->getPosY();
|
||||
|
||||
sprite_->update(delta_time);
|
||||
checkPath();
|
||||
|
||||
last_dx_ = sprite_->getPosX() - old_x;
|
||||
last_dy_ = sprite_->getPosY() - old_y;
|
||||
|
||||
collider_ = getRect();
|
||||
}
|
||||
|
||||
// Pinta la plataforma en pantalla
|
||||
void MovingPlatform::render() {
|
||||
sprite_->render();
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
// Solo actualiza la animación sin mover la plataforma
|
||||
void MovingPlatform::updateAnimation(float delta_time) {
|
||||
sprite_->animate(delta_time);
|
||||
}
|
||||
|
||||
// Resetea la plataforma a su posición inicial (para editor)
|
||||
void MovingPlatform::resetToInitialPosition(const Data& data) {
|
||||
sprite_->setPosX(data.x);
|
||||
sprite_->setPosY(data.y);
|
||||
sprite_->setVelX(data.vx);
|
||||
sprite_->setVelY(data.vy);
|
||||
|
||||
x1_ = data.x1;
|
||||
x2_ = data.x2;
|
||||
y1_ = data.y1;
|
||||
y2_ = data.y2;
|
||||
|
||||
collider_ = getRect();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Devuelve el rectangulo que contiene a la plataforma
|
||||
auto MovingPlatform::getRect() -> SDL_FRect {
|
||||
return sprite_->getRect();
|
||||
}
|
||||
|
||||
// Obtiene el rectangulo de colisión
|
||||
auto MovingPlatform::getCollider() -> SDL_FRect& {
|
||||
return collider_;
|
||||
}
|
||||
|
||||
// Comprueba los límites del recorrido para invertir dirección
|
||||
void MovingPlatform::checkPath() { // NOLINT(readability-make-member-function-const)
|
||||
if (sprite_->getPosX() > x2_ || sprite_->getPosX() < x1_) {
|
||||
if (sprite_->getPosX() > x2_) {
|
||||
sprite_->setPosX(x2_);
|
||||
} else {
|
||||
sprite_->setPosX(x1_);
|
||||
}
|
||||
sprite_->setVelX(sprite_->getVelX() * (-1));
|
||||
}
|
||||
|
||||
if (sprite_->getPosY() > y2_ || sprite_->getPosY() < y1_) {
|
||||
if (sprite_->getPosY() > y2_) {
|
||||
sprite_->setPosY(y2_);
|
||||
} else {
|
||||
sprite_->setPosY(y1_);
|
||||
}
|
||||
sprite_->setVelY(sprite_->getVelY() * (-1));
|
||||
}
|
||||
}
|
||||
53
source/game/entities/moving_platform.hpp
Normal file
53
source/game/entities/moving_platform.hpp
Normal file
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <memory> // Para shared_ptr
|
||||
#include <string> // Para string
|
||||
|
||||
class AnimatedSprite;
|
||||
|
||||
class MovingPlatform {
|
||||
public:
|
||||
struct Data {
|
||||
std::string animation_path; // Ruta al fichero con la animación
|
||||
float x{0.0F}; // Posición inicial en el eje X
|
||||
float y{0.0F}; // Posición inicial en el eje Y
|
||||
float vx{0.0F}; // Velocidad en el eje X
|
||||
float vy{0.0F}; // Velocidad en el eje Y
|
||||
int x1{0}; // Límite izquierdo de la ruta en el eje X
|
||||
int x2{0}; // Límite derecho de la ruta en el eje X
|
||||
int y1{0}; // Límite superior de la ruta en el eje Y
|
||||
int y2{0}; // Límite inferior de la ruta en el eje Y
|
||||
int frame{0}; // Frame inicial para la animación
|
||||
};
|
||||
|
||||
explicit MovingPlatform(const Data& data);
|
||||
~MovingPlatform() = default;
|
||||
|
||||
void update(float delta_time);
|
||||
void render();
|
||||
#ifdef _DEBUG
|
||||
void updateAnimation(float delta_time);
|
||||
void resetToInitialPosition(const Data& data);
|
||||
#endif
|
||||
|
||||
auto getRect() -> SDL_FRect;
|
||||
auto getCollider() -> SDL_FRect&;
|
||||
|
||||
// Desplazamiento real del último frame (para transportar al jugador)
|
||||
[[nodiscard]] auto getLastDX() const -> float { return last_dx_; }
|
||||
[[nodiscard]] auto getLastDY() const -> float { return last_dy_; }
|
||||
|
||||
private:
|
||||
void checkPath(); // Comprueba los límites del recorrido
|
||||
|
||||
std::shared_ptr<AnimatedSprite> sprite_;
|
||||
SDL_FRect collider_{};
|
||||
int x1_{0};
|
||||
int x2_{0};
|
||||
int y1_{0};
|
||||
int y2_{0};
|
||||
float last_dx_{0.0F}; // Desplazamiento horizontal del último frame
|
||||
float last_dy_{0.0F}; // Desplazamiento vertical del último frame
|
||||
};
|
||||
75
source/game/entities/path_enemy.cpp
Normal file
75
source/game/entities/path_enemy.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
#include "game/entities/path_enemy.hpp"
|
||||
|
||||
#include "core/rendering/sprite/animated_sprite.hpp" // Para AnimatedSprite
|
||||
|
||||
// Constructor: delega al base y almacena boundaries
|
||||
PathEnemy::PathEnemy(const Data& data)
|
||||
: Enemy(data),
|
||||
x1_(data.x1),
|
||||
x2_(data.x2),
|
||||
y1_(data.y1),
|
||||
y2_(data.y2) {
|
||||
}
|
||||
|
||||
// Actualiza posición y animación del enemigo
|
||||
void PathEnemy::update(float delta_time) {
|
||||
sprite_->update(delta_time);
|
||||
checkPath();
|
||||
collider_ = getRect();
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
// Resetea el enemigo a su posición inicial (para editor)
|
||||
void PathEnemy::resetToInitialPosition(const Data& data) {
|
||||
sprite_->setPosX(data.x);
|
||||
sprite_->setPosY(data.y);
|
||||
sprite_->setVelX(data.vx);
|
||||
sprite_->setVelY(data.vy);
|
||||
|
||||
x1_ = data.x1;
|
||||
x2_ = data.x2;
|
||||
y1_ = data.y1;
|
||||
y2_ = data.y2;
|
||||
|
||||
applyFlipMirror(data.vx);
|
||||
|
||||
collider_ = getRect();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Comprueba si ha llegado al limite del recorrido para darse media vuelta
|
||||
void PathEnemy::checkPath() { // NOLINT(readability-make-member-function-const)
|
||||
if (sprite_->getPosX() > x2_ || sprite_->getPosX() < x1_) {
|
||||
// Recoloca
|
||||
if (sprite_->getPosX() > x2_) {
|
||||
sprite_->setPosX(x2_);
|
||||
} else {
|
||||
sprite_->setPosX(x1_);
|
||||
}
|
||||
|
||||
// Cambia el sentido
|
||||
sprite_->setVelX(sprite_->getVelX() * (-1));
|
||||
|
||||
// Invierte el sprite
|
||||
if (should_flip_) {
|
||||
sprite_->flip();
|
||||
}
|
||||
}
|
||||
|
||||
if (sprite_->getPosY() > y2_ || sprite_->getPosY() < y1_) {
|
||||
// Recoloca
|
||||
if (sprite_->getPosY() > y2_) {
|
||||
sprite_->setPosY(y2_);
|
||||
} else {
|
||||
sprite_->setPosY(y1_);
|
||||
}
|
||||
|
||||
// Cambia el sentido
|
||||
sprite_->setVelY(sprite_->getVelY() * (-1));
|
||||
|
||||
// Invierte el sprite
|
||||
if (should_flip_) {
|
||||
sprite_->flip();
|
||||
}
|
||||
}
|
||||
}
|
||||
22
source/game/entities/path_enemy.hpp
Normal file
22
source/game/entities/path_enemy.hpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "game/entities/enemy.hpp"
|
||||
|
||||
class PathEnemy : public Enemy {
|
||||
public:
|
||||
explicit PathEnemy(const Data& data);
|
||||
~PathEnemy() override = default;
|
||||
|
||||
void update(float delta_time) override;
|
||||
#ifdef _DEBUG
|
||||
void resetToInitialPosition(const Data& data) override;
|
||||
#endif
|
||||
|
||||
private:
|
||||
void checkPath(); // Comprueba si ha llegado al limite del recorrido para darse media vuelta
|
||||
|
||||
int x1_{0}; // Limite izquierdo de la ruta en el eje X
|
||||
int x2_{0}; // Limite derecho de la ruta en el eje X
|
||||
int y1_{0}; // Limite superior de la ruta en el eje Y
|
||||
int y2_{0}; // Limite inferior de la ruta en el eje Y
|
||||
};
|
||||
@@ -398,6 +398,9 @@ void Player::checkFalling() {
|
||||
return;
|
||||
}
|
||||
|
||||
// ON_GROUND: si está sobre una plataforma móvil, no comprobar tiles
|
||||
if (on_platform_) { return; }
|
||||
|
||||
// ON_GROUND: comprobar si sigue habiendo suelo
|
||||
float foot_y = (y_ + oy) + HEIGHT;
|
||||
if (!tc.hasGroundBelow(x_ + ox, foot_y, WIDTH)) {
|
||||
@@ -537,6 +540,18 @@ void Player::syncSpriteAndCollider() {
|
||||
collider_box_ = getRect();
|
||||
}
|
||||
|
||||
// Aplica el desplazamiento de una plataforma móvil al jugador
|
||||
void Player::applyPlatformDisplacement(float dx, float surface_y) {
|
||||
y_ = surface_y - HEIGHT; // Snap vertical al top de la plataforma
|
||||
x_ += dx; // Desplazamiento horizontal
|
||||
vy_ = 0.0F;
|
||||
on_platform_ = true;
|
||||
if (state_ != State::ON_GROUND) {
|
||||
transitionToState(State::ON_GROUND);
|
||||
}
|
||||
syncSpriteAndCollider();
|
||||
}
|
||||
|
||||
void Player::placeSprite() {
|
||||
sprite_->setPos(x_, y_);
|
||||
}
|
||||
|
||||
@@ -72,6 +72,9 @@ class Player {
|
||||
void setAdjacentRoom(std::shared_ptr<Room> room, Room::Border direction);
|
||||
void clearAdjacentRoom();
|
||||
[[nodiscard]] auto isAlive() const -> bool { return is_alive_; }
|
||||
[[nodiscard]] auto getVY() const -> float { return vy_; }
|
||||
void applyPlatformDisplacement(float dx, float surface_y);
|
||||
void clearPlatformFlag() { on_platform_ = false; }
|
||||
void setPaused(bool value) { is_paused_ = value; }
|
||||
void setIgnoreInput(bool value) { ignore_input_ = value; }
|
||||
[[nodiscard]] auto getIgnoreInput() const -> bool { return ignore_input_; }
|
||||
@@ -126,6 +129,7 @@ class Player {
|
||||
bool is_alive_ = true;
|
||||
bool is_paused_ = false;
|
||||
bool ignore_input_ = false;
|
||||
bool on_platform_ = false;
|
||||
bool turning_ = false;
|
||||
Direction facing_ = Direction::RIGHT;
|
||||
Room::Border border_ = Room::Border::TOP;
|
||||
|
||||
Reference in New Issue
Block a user