From a230351b79ddb3bd90122fef0e93921eec44f55d Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Wed, 22 Apr 2026 19:43:37 +0200 Subject: [PATCH] =?UTF-8?q?feat(gamepad):=20controls=20de=20gamepad=20per?= =?UTF-8?q?=20al=20player=20i=20el=20men=C3=BA=20de=20servei?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/game/entities/player.cpp | 32 ++++++++++++++++++++++++++++++ source/game/ui/service_menu.cpp | 35 +++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/source/game/entities/player.cpp b/source/game/entities/player.cpp index e6edd1b..6cec2bb 100644 --- a/source/game/entities/player.cpp +++ b/source/game/entities/player.cpp @@ -8,6 +8,7 @@ #include #include +#include "core/input/gamepad_manager.hpp" #include "core/input/input_state.hpp" #include "core/resources/resource_cache.hpp" #include "core/resources/resource_helper.hpp" @@ -20,6 +21,29 @@ namespace { // Alias local per reduir soroll; la constant viu a Defaults::Player. constexpr float SPEED_PX_PER_S = Defaults::Player::SPEED_PX_PER_S; + // Dead-zone del stick analògic esquerre (rang Sint16: -32768..32767). + // Converteix eix analògic en direcció digital; per sota, es considera + // repòs i no contribueix a la velocitat. + constexpr Sint16 STICK_DEADZONE = 12000; + + // Acumula la contribució del primer gamepad (cruceta + stick esquerre) + // sobre els acumuladors de velocitat. Extracció d'`update()` per a + // mantenir la complexitat cognitiva sota llindar (threshold 25). + void accumulateGamepadMovement(float& vx, float& vy) { + SDL_Gamepad* pad = GamepadManager::get().getPrimary(); + if (pad == nullptr) { return; } + if (InputState::getGamepadButton(pad, SDL_GAMEPAD_BUTTON_DPAD_LEFT)) { vx -= SPEED_PX_PER_S; } + if (InputState::getGamepadButton(pad, SDL_GAMEPAD_BUTTON_DPAD_RIGHT)) { vx += SPEED_PX_PER_S; } + if (InputState::getGamepadButton(pad, SDL_GAMEPAD_BUTTON_DPAD_UP)) { vy -= SPEED_PX_PER_S; } + if (InputState::getGamepadButton(pad, SDL_GAMEPAD_BUTTON_DPAD_DOWN)) { vy += SPEED_PX_PER_S; } + const Sint16 LX = InputState::getGamepadAxis(pad, SDL_GAMEPAD_AXIS_LEFTX); + const Sint16 LY = InputState::getGamepadAxis(pad, SDL_GAMEPAD_AXIS_LEFTY); + if (LX < -STICK_DEADZONE) { vx -= SPEED_PX_PER_S; } + if (LX > STICK_DEADZONE) { vx += SPEED_PX_PER_S; } + if (LY < -STICK_DEADZONE) { vy -= SPEED_PX_PER_S; } + if (LY > STICK_DEADZONE) { vy += SPEED_PX_PER_S; } + } + } // namespace void Player::loadFrom(const std::string& yaml_path) { @@ -70,6 +94,14 @@ void Player::update(float delta_time) { if (keys[controls_.up]) { vy -= SPEED_PX_PER_S; } if (keys[controls_.down]) { vy += SPEED_PX_PER_S; } + // Cruceta i stick esquerre del primer gamepad (si n'hi ha). També + // respecten el bloqueig d'`InputState`, així que amb el menú obert + // els botons tornen false/0 i no contribueixen a la velocitat. + // Direccions oposades (teclat left + d-pad right) es cancel·len de + // forma natural; premuda simultània en la mateixa direcció no duplica + // la velocitat perquè cada branca només resta/suma una vegada. + accumulateGamepadMovement(vx, vy); + setVelX(vx); setVelY(vy); diff --git a/source/game/ui/service_menu.cpp b/source/game/ui/service_menu.cpp index 9656ecd..a9973b5 100644 --- a/source/game/ui/service_menu.cpp +++ b/source/game/ui/service_menu.cpp @@ -426,6 +426,41 @@ void ServiceMenu::activateCurrent(int direction) { auto ServiceMenu::handleEvent(const SDL_Event& ev) -> bool { if (!open_) { return false; } + + // Botons del gamepad: cruceta per navegar, EAST/NORTH com a OK, + // WEST/SOUTH com a BACK de pàgina, BACK tanca el menú sencer. Quan + // el menú està obert consumim TOTS els BUTTON_DOWN perquè cap botó + // arribi als consumidors de gameplay. Paral·lel al consum total de + // KEY_DOWN més avall. + if (ev.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN) { + switch (ev.gbutton.button) { + case SDL_GAMEPAD_BUTTON_DPAD_UP: + moveCursor(-1); + break; + case SDL_GAMEPAD_BUTTON_DPAD_DOWN: + moveCursor(+1); + break; + case SDL_GAMEPAD_BUTTON_DPAD_LEFT: + activateCurrent(-1); + break; + case SDL_GAMEPAD_BUTTON_DPAD_RIGHT: + case SDL_GAMEPAD_BUTTON_EAST: + case SDL_GAMEPAD_BUTTON_NORTH: + activateCurrent(+1); + break; + case SDL_GAMEPAD_BUTTON_WEST: + case SDL_GAMEPAD_BUTTON_SOUTH: + popPage(); + break; + case SDL_GAMEPAD_BUTTON_BACK: + close(); + break; + default: + break; + } + return true; + } + if (ev.type != SDL_EVENT_KEY_DOWN) { return false; } const auto SC = ev.key.scancode;