diff --git a/source/core/rendering/sprite/moving_sprite.cpp b/source/core/rendering/sprite/moving_sprite.cpp index ee2074f..7357cda 100644 --- a/source/core/rendering/sprite/moving_sprite.cpp +++ b/source/core/rendering/sprite/moving_sprite.cpp @@ -72,3 +72,12 @@ void MovingSprite::setPosY(float value) { y_ = value; pos_.y = static_cast(y_); } + +void MovingSprite::tryMove(float dx, float dy, const CollisionFn& collides) { + const float OLD_X = getPosX(); + setPosX(OLD_X + dx); + if (collides(collisionBox())) { setPosX(OLD_X); } + const float OLD_Y = getPosY(); + setPosY(OLD_Y + dy); + if (collides(collisionBox())) { setPosY(OLD_Y); } +} diff --git a/source/core/rendering/sprite/moving_sprite.hpp b/source/core/rendering/sprite/moving_sprite.hpp index be6932d..f537361 100644 --- a/source/core/rendering/sprite/moving_sprite.hpp +++ b/source/core/rendering/sprite/moving_sprite.hpp @@ -2,6 +2,7 @@ #include +#include #include #include "core/rendering/sprite/sprite.hpp" @@ -40,6 +41,15 @@ class MovingSprite : public Sprite { auto getFlip() -> SDL_FlipMode { return flip_; } void flip() { flip_ = (flip_ == SDL_FLIP_HORIZONTAL) ? SDL_FLIP_NONE : SDL_FLIP_HORIZONTAL; } + // Moviment eix-separat amb col·lisió: aplica `dx` (i si la nova + // `collisionBox()` col·lisiona, el reverteix); després aplica `dy` + // (idem). El caller calcula els deltes (típicament `vx_*dt`, + // `vy_*dt`) i passa la funció de detecció. Aquesta separació deixa + // que el sprite "llisqui" contra obstacles en compte de quedar-se + // bloquejat en sec quan només un eix col·lisiona. + using CollisionFn = std::function; + void tryMove(float dx, float dy, const CollisionFn& collides); + protected: void move(float delta_time); diff --git a/source/core/rendering/sprite/sprite.hpp b/source/core/rendering/sprite/sprite.hpp index 806b2d5..5e5cb7b 100644 --- a/source/core/rendering/sprite/sprite.hpp +++ b/source/core/rendering/sprite/sprite.hpp @@ -19,6 +19,15 @@ class Sprite { // Hook d'actualització per frame. Base buida (no hi ha Sprite pur a // actualitzar); les subclasses mòbils/animades fan override. virtual void update(float /*delta_time*/) {} + + // Caixa que `MovingSprite::tryMove` envia al callback de col·lisió per + // decidir si el moviment és vàlid. Per defecte coincideix amb `pos_` + // (el sprite sencer); les subclasses que usen falsa perspectiva (p.ex. + // `Player`) la sobreescriuen amb una sub-caixa més petita (només els + // peus) per a que el cap puga sobresortir sense bloquejar el + // moviment. + [[nodiscard]] virtual auto collisionBox() const -> SDL_FRect { return pos_; } + virtual void render(); // Renderitza substituint l'índex source_color per target_color (ús típic: // la classe Text, que recolora glifs monocroms per paràmetre). diff --git a/source/game/entities/player.cpp b/source/game/entities/player.cpp index 995dccc..4fb5767 100644 --- a/source/game/entities/player.cpp +++ b/source/game/entities/player.cpp @@ -104,22 +104,13 @@ void Player::update(float delta_time) { snapToVerticalLane(delta_time); } - // Guardem la posició prèvia i deixem que AnimatedSprite::update aplique - // la velocitat com sempre; així no dupliquem la integració temporal ni - // l'animació. Després, axis-separated collision: revertim i re-apliquem - // cada eix comprovant el mapa de colisions, de manera que el jugador - // llisca contra els sarcòfags en compte de quedar-se bloquejat en sec. - const float OLD_X = getPosX(); - const float OLD_Y = getPosY(); - AnimatedSprite::update(delta_time); - const float NEW_X = getPosX(); - const float NEW_Y = getPosY(); - setPos(OLD_X, OLD_Y); - - setPosX(NEW_X); - if (map_.collides(baseBox())) { setPosX(OLD_X); } - setPosY(NEW_Y); - if (map_.collides(baseBox())) { setPosY(OLD_Y); } + // Animació del frame + moviment col·lisió-aware. `tryMove` aplica els + // deltes per eix i reverteix l'eix que col·lisiona, així el jugador + // llisca contra sarcòfags i murs en compte de quedar-se bloquejat en + // sec. La caixa que s'envia al callback és `collisionBox()` (override + // a Player retornant `getBaseBox()`), no el sprite sencer. + animate(delta_time); + tryMove(vx * delta_time, vy * delta_time, [this](const SDL_FRect& box) { return map_.collides(box); }); clampToPlayfield(); diff --git a/source/game/entities/player.hpp b/source/game/entities/player.hpp index 221b94d..584c36d 100644 --- a/source/game/entities/player.hpp +++ b/source/game/entities/player.hpp @@ -67,6 +67,12 @@ class Player : public AnimatedSprite { // amb el jugador (momies, trampes, porta). [[nodiscard]] auto getBaseBox() const -> SDL_FRect; + // Override del hook de Sprite: `MovingSprite::tryMove` envia aquesta + // caixa al callback de col·lisió. La base del Player és més petita + // que el sprite (el cap pot sobresortir per murs/sarcòfags sense + // bloquejar el moviment). + [[nodiscard]] auto collisionBox() const -> SDL_FRect override { return getBaseBox(); } + private: // Alias privat històric. Mantingut perquè viu dins de l'implementació // (snapToLane, clamp). Els consumidors externs usen `getBaseBox()`.