From 96cbdf96b6d4aec334e73f27ecd931ce6733dcb8 Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Sun, 19 Apr 2026 09:57:22 +0200 Subject: [PATCH] Input + moviment del jugador amb fletxes (flip, anim walk/stand, bounds) (v0.8.0) --- source/game/entities/player.cpp | 56 +++++++++++++++++++++++++++++-- source/game/entities/player.hpp | 29 ++++++++++++++-- source/game/scenes/game_scene.cpp | 25 ++++++++------ source/game/scenes/game_scene.hpp | 5 ++- source/utils/defines.hpp | 2 +- 5 files changed, 99 insertions(+), 18 deletions(-) diff --git a/source/game/entities/player.cpp b/source/game/entities/player.cpp index 5864075..942c6b6 100644 --- a/source/game/entities/player.cpp +++ b/source/game/entities/player.cpp @@ -1,9 +1,59 @@ #include "game/entities/player.hpp" -#include "core/resources/resource_cache.hpp" +#include -Player::Player(float x, float y) - : AnimatedSprite(Resource::Cache::get().getAnimationData("player.yaml")) { +#include + +#include "core/resources/resource_cache.hpp" +#include "utils/defines.hpp" + +namespace { + + // Velocitat constant. Si més endavant volem sprint, slow zones, etc., + // es pot convertir en un membre modificable. + constexpr float SPEED_PX_PER_S = 60.0F; + +} // namespace + +Player::Player(float x, float y, const Controls& controls) + : AnimatedSprite(Resource::Cache::get().getAnimationData("player.yaml")), + controls_(controls) { setPos(x, y); setCurrentAnimation("stand"); } + +void Player::update(float delta_time) { + const bool* keys = SDL_GetKeyboardState(nullptr); + float vx = 0.0F; + float vy = 0.0F; + if (keys[controls_.left]) { vx -= SPEED_PX_PER_S; } + if (keys[controls_.right]) { vx += SPEED_PX_PER_S; } + if (keys[controls_.up]) { vy -= SPEED_PX_PER_S; } + if (keys[controls_.down]) { vy += SPEED_PX_PER_S; } + + setVelX(vx); + setVelY(vy); + + // El sprite original mira a la dreta; flipem només si es mou a l'esquerra + // i restaurem si torna a la dreta. Moviments purament verticals conserven + // l'últim flip perquè el jugador no "gire" sense motiu. + if (vx < 0.0F) { + setFlip(SDL_FLIP_HORIZONTAL); + } else if (vx > 0.0F) { + setFlip(SDL_FLIP_NONE); + } + + const bool MOVING = vx != 0.0F || vy != 0.0F; + setCurrentAnimation(MOVING ? "walk" : "stand"); + + AnimatedSprite::update(delta_time); + + // Límits de pantalla amb perspectiva falsa: el cap (12px superiors) pot + // quedar fora per dalt; els peus (12×12 inferiors) sempre dins del canvas. + constexpr auto MIN_X = 0.0F; + constexpr auto MAX_X = static_cast(GameCanvas::WIDTH - SPRITE_WIDTH); + constexpr auto MIN_Y = -static_cast(SPRITE_HEIGHT - COLLISION_HEIGHT); + constexpr auto MAX_Y = static_cast(GameCanvas::HEIGHT - SPRITE_HEIGHT); + setPosX(std::clamp(getPosX(), MIN_X, MAX_X)); + setPosY(std::clamp(getPosY(), MIN_Y, MAX_Y)); +} diff --git a/source/game/entities/player.hpp b/source/game/entities/player.hpp index b4beae2..1abd49b 100644 --- a/source/game/entities/player.hpp +++ b/source/game/entities/player.hpp @@ -1,10 +1,33 @@ #pragma once +#include + #include "core/rendering/sprite/animated_sprite.hpp" class Player : public AnimatedSprite { public: - // Construeix el jugador a partir del recurs d'animació "player.yaml" - // ja registrat al cache. Comença en l'animació "stand". - Player(float x, float y); + // Mapeig de tecles per al moviment del jugador. Cada jugador local en pot + // tenir unes diferents; així podem afegir-ne més sense tocar el codi del + // Player, només instanciant amb Controls distints. + struct Controls { + SDL_Scancode up; + SDL_Scancode down; + SDL_Scancode left; + SDL_Scancode right; + }; + + // Sprite 12×24 amb perspectiva falsa: la base real del jugador són els + // 12×12 inferiors. Per això el bounding horitzontal és l'amplada completa, + // però verticalment permetem que el cap (12px superiors) isca fora de la + // pantalla — s'hi fa clipping natural al render. + static constexpr int SPRITE_WIDTH = 12; + static constexpr int SPRITE_HEIGHT = 24; + static constexpr int COLLISION_HEIGHT = 12; + + Player(float x, float y, const Controls& controls); + + void update(float delta_time) override; + + private: + Controls controls_; }; diff --git a/source/game/scenes/game_scene.cpp b/source/game/scenes/game_scene.cpp index 99d105a..eebbbbe 100644 --- a/source/game/scenes/game_scene.cpp +++ b/source/game/scenes/game_scene.cpp @@ -7,20 +7,25 @@ #include "core/resources/resource_cache.hpp" #include "core/system/event_buffer.hpp" #include "game/entities/player.hpp" +#include "utils/defaults.hpp" #include "utils/defines.hpp" -namespace { - constexpr float PLAYER_FRAME_W = 12.0F; - constexpr float PLAYER_FRAME_H = 24.0F; -} // namespace - GameScene::GameScene() { Resource::Cache::get().loadSurface("player.gif", "data/player/player.gif"); Resource::Cache::get().loadAnimation("player.yaml", "data/player/player.yaml"); - const float X = static_cast(GameCanvas::CENTER_X) - (PLAYER_FRAME_W / 2.0F); - const float Y = static_cast(GameCanvas::CENTER_Y) - (PLAYER_FRAME_H / 2.0F); - player_ = std::make_unique(X, Y); + // Jugador 0: fletxes del teclat (Defaults::KeysGame). Instanciar-ne un + // segon només requereix afegir una altra entrada amb uns Controls + // distints (p.ex. WASD). + const Player::Controls CONTROLS_P1{ + .up = Defaults::KeysGame::UP, + .down = Defaults::KeysGame::DOWN, + .left = Defaults::KeysGame::LEFT, + .right = Defaults::KeysGame::RIGHT, + }; + const float X = static_cast(GameCanvas::CENTER_X) - (static_cast(Player::SPRITE_WIDTH) / 2.0F); + const float Y = static_cast(GameCanvas::CENTER_Y) - (static_cast(Player::SPRITE_HEIGHT) / 2.0F); + players_.emplace_back(std::make_unique(X, Y, CONTROLS_P1)); } GameScene::~GameScene() = default; @@ -34,13 +39,13 @@ void GameScene::update(int delta_ms) { } const float DT = static_cast(delta_ms) / 1000.0F; - player_->update(DT); + for (auto& player : players_) { player->update(DT); } RenderInfo::get().update(delta_ms); } void GameScene::render() { Screen::get().clear(0x00, 0x00, 0x00); - player_->render(); + for (auto& player : players_) { player->render(); } RenderInfo::get().render(); Screen::get().present(); } diff --git a/source/game/scenes/game_scene.hpp b/source/game/scenes/game_scene.hpp index b513467..3dab755 100644 --- a/source/game/scenes/game_scene.hpp +++ b/source/game/scenes/game_scene.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include "core/scene/scene.hpp" @@ -15,5 +16,7 @@ class GameScene : public Scene { void render() override; private: - std::unique_ptr player_; + // Vector des del principi: AEE suportarà multijugador local; cada Player + // té els seus propis Controls i entra al vector amb una crida extra. + std::vector> players_; }; diff --git a/source/utils/defines.hpp b/source/utils/defines.hpp index e9f7360..0dba174 100644 --- a/source/utils/defines.hpp +++ b/source/utils/defines.hpp @@ -3,7 +3,7 @@ namespace Texts { constexpr const char* WINDOW_TITLE = "© 2026 Aventures en Egipte — JailDesigner"; constexpr const char* COPYRIGHT = "© 2026 JailDesigner"; - constexpr const char* VERSION = "0.7.1"; // Single source of truth — Makefile el grep + constexpr const char* VERSION = "0.8.0"; // Single source of truth — Makefile el grep } // namespace Texts namespace GameCanvas {