Input + moviment del jugador amb fletxes (flip, anim walk/stand, bounds) (v0.8.0)
This commit is contained in:
1 parent
bee9b650db
commit
96cbdf96b6
5 files changed
+99
-18
No files matched your search
@@ -1,9 +1,59 @@
|
|||||||
#include "game/entities/player.hpp"
|
#include "game/entities/player.hpp"
|
||||||
|
|
||||||
#include "core/resources/resource_cache.hpp"
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
Player::Player(float x, float y)
|
#include <algorithm>
|
||||||
: AnimatedSprite(Resource::Cache::get().getAnimationData("player.yaml")) {
|
|
||||||
|
#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);
|
setPos(x, y);
|
||||||
setCurrentAnimation("stand");
|
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<float>(GameCanvas::WIDTH - SPRITE_WIDTH);
|
||||||
|
constexpr auto MIN_Y = -static_cast<float>(SPRITE_HEIGHT - COLLISION_HEIGHT);
|
||||||
|
constexpr auto MAX_Y = static_cast<float>(GameCanvas::HEIGHT - SPRITE_HEIGHT);
|
||||||
|
setPosX(std::clamp(getPosX(), MIN_X, MAX_X));
|
||||||
|
setPosY(std::clamp(getPosY(), MIN_Y, MAX_Y));
|
||||||
|
}
|
||||||
@@ -1,10 +1,33 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
#include "core/rendering/sprite/animated_sprite.hpp"
|
#include "core/rendering/sprite/animated_sprite.hpp"
|
||||||
|
|
||||||
class Player : public AnimatedSprite {
|
class Player : public AnimatedSprite {
|
||||||
public:
|
public:
|
||||||
// Construeix el jugador a partir del recurs d'animació "player.yaml"
|
// Mapeig de tecles per al moviment del jugador. Cada jugador local en pot
|
||||||
// ja registrat al cache. Comença en l'animació "stand".
|
// tenir unes diferents; així podem afegir-ne més sense tocar el codi del
|
||||||
Player(float x, float y);
|
// 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_;
|
||||||
};
|
};
|
||||||
@@ -7,20 +7,25 @@
|
|||||||
#include "core/resources/resource_cache.hpp"
|
#include "core/resources/resource_cache.hpp"
|
||||||
#include "core/system/event_buffer.hpp"
|
#include "core/system/event_buffer.hpp"
|
||||||
#include "game/entities/player.hpp"
|
#include "game/entities/player.hpp"
|
||||||
|
#include "utils/defaults.hpp"
|
||||||
#include "utils/defines.hpp"
|
#include "utils/defines.hpp"
|
||||||
|
|
||||||
namespace {
|
|
||||||
constexpr float PLAYER_FRAME_W = 12.0F;
|
|
||||||
constexpr float PLAYER_FRAME_H = 24.0F;
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
GameScene::GameScene() {
|
GameScene::GameScene() {
|
||||||
Resource::Cache::get().loadSurface("player.gif", "data/player/player.gif");
|
Resource::Cache::get().loadSurface("player.gif", "data/player/player.gif");
|
||||||
Resource::Cache::get().loadAnimation("player.yaml", "data/player/player.yaml");
|
Resource::Cache::get().loadAnimation("player.yaml", "data/player/player.yaml");
|
||||||
|
|
||||||
const float X = static_cast<float>(GameCanvas::CENTER_X) - (PLAYER_FRAME_W / 2.0F);
|
// Jugador 0: fletxes del teclat (Defaults::KeysGame). Instanciar-ne un
|
||||||
const float Y = static_cast<float>(GameCanvas::CENTER_Y) - (PLAYER_FRAME_H / 2.0F);
|
// segon només requereix afegir una altra entrada amb uns Controls
|
||||||
player_ = std::make_unique<Player>(X, Y);
|
// 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<float>(GameCanvas::CENTER_X) - (static_cast<float>(Player::SPRITE_WIDTH) / 2.0F);
|
||||||
|
const float Y = static_cast<float>(GameCanvas::CENTER_Y) - (static_cast<float>(Player::SPRITE_HEIGHT) / 2.0F);
|
||||||
|
players_.emplace_back(std::make_unique<Player>(X, Y, CONTROLS_P1));
|
||||||
}
|
}
|
||||||
|
|
||||||
GameScene::~GameScene() = default;
|
GameScene::~GameScene() = default;
|
||||||
@@ -34,13 +39,13 @@ void GameScene::update(int delta_ms) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const float DT = static_cast<float>(delta_ms) / 1000.0F;
|
const float DT = static_cast<float>(delta_ms) / 1000.0F;
|
||||||
player_->update(DT);
|
for (auto& player : players_) { player->update(DT); }
|
||||||
RenderInfo::get().update(delta_ms);
|
RenderInfo::get().update(delta_ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameScene::render() {
|
void GameScene::render() {
|
||||||
Screen::get().clear(0x00, 0x00, 0x00);
|
Screen::get().clear(0x00, 0x00, 0x00);
|
||||||
player_->render();
|
for (auto& player : players_) { player->render(); }
|
||||||
RenderInfo::get().render();
|
RenderInfo::get().render();
|
||||||
Screen::get().present();
|
Screen::get().present();
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "core/scene/scene.hpp"
|
#include "core/scene/scene.hpp"
|
||||||
|
|
||||||
@@ -15,5 +16,7 @@ class GameScene : public Scene {
|
|||||||
void render() override;
|
void render() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<Player> 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<std::unique_ptr<Player>> players_;
|
||||||
};
|
};
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
namespace Texts {
|
namespace Texts {
|
||||||
constexpr const char* WINDOW_TITLE = "© 2026 Aventures en Egipte — JailDesigner";
|
constexpr const char* WINDOW_TITLE = "© 2026 Aventures en Egipte — JailDesigner";
|
||||||
constexpr const char* COPYRIGHT = "© 2026 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 Texts
|
||||||
|
|
||||||
namespace GameCanvas {
|
namespace GameCanvas {
|
||||||
|
|||||||
Reference in new issue
Block a user