From 2a4c47a6cafe21fca889d8fa44fe8b538327e411 Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Sat, 16 Aug 2025 18:03:32 +0200 Subject: [PATCH] ja permet mapejar botons tipo trigger --- source/define_buttons.cpp | 51 +++++++++++++++++++++++++ source/define_buttons.h | 4 ++ source/input.cpp | 53 ++++++++++++++++++++++++++ source/input.h | 9 ++++- test_triggers.cpp | 78 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 test_triggers.cpp diff --git a/source/define_buttons.cpp b/source/define_buttons.cpp index f460591..c6c1688 100644 --- a/source/define_buttons.cpp +++ b/source/define_buttons.cpp @@ -72,6 +72,9 @@ void DefineButtons::handleEvents(const SDL_Event &event) { case SDL_EVENT_GAMEPAD_BUTTON_UP: checkEnd(); break; + case SDL_EVENT_GAMEPAD_AXIS_MOTION: + doControllerAxisMotion(event.gaxis); + break; default: break; } @@ -86,6 +89,8 @@ auto DefineButtons::enable(Options::Gamepad *options_gamepad) -> bool { index_button_ = 0; message_shown_ = false; closing_ = false; + l2_was_pressed_ = false; + r2_was_pressed_ = false; clearButtons(); updateWindowMessage(); @@ -104,6 +109,8 @@ void DefineButtons::disable() { finished_ = false; message_shown_ = false; closing_ = false; + l2_was_pressed_ = false; + r2_was_pressed_ = false; if (window_message_) { window_message_->hide(); @@ -125,6 +132,44 @@ void DefineButtons::doControllerButtonDown(const SDL_GamepadButtonEvent &event) } } +void DefineButtons::doControllerAxisMotion(const SDL_GamepadAxisEvent &event) { + auto gamepad = input_->getGamepad(event.which); + + if (!gamepad || gamepad != options_gamepad_->instance) { + return; + } + + // Solo manejamos L2 y R2 como botones con lógica de transición + if (event.axis == SDL_GAMEPAD_AXIS_LEFT_TRIGGER) { + bool l2_is_pressed_now = event.value > 16384; + + // Solo actuar en la transición de no presionado a presionado + if (l2_is_pressed_now && !l2_was_pressed_) { + const auto TRIGGER_BUTTON = Input::TRIGGER_L2_AS_BUTTON; + if (checkTriggerNotInUse(TRIGGER_BUTTON)) { + buttons_.at(index_button_).button = TRIGGER_BUTTON; + incIndexButton(); + updateWindowMessage(); + } + } + l2_was_pressed_ = l2_is_pressed_now; + + } else if (event.axis == SDL_GAMEPAD_AXIS_RIGHT_TRIGGER) { + bool r2_is_pressed_now = event.value > 16384; + + // Solo actuar en la transición de no presionado a presionado + if (r2_is_pressed_now && !r2_was_pressed_) { + const auto TRIGGER_BUTTON = Input::TRIGGER_R2_AS_BUTTON; + if (checkTriggerNotInUse(TRIGGER_BUTTON)) { + buttons_.at(index_button_).button = TRIGGER_BUTTON; + incIndexButton(); + updateWindowMessage(); + } + } + r2_was_pressed_ = r2_is_pressed_now; + } +} + void DefineButtons::bindButtons(Options::Gamepad *options_gamepad) { for (const auto &button : buttons_) { Input::bindGameControllerButton(options_gamepad->instance, button.action, button.button); @@ -148,6 +193,12 @@ auto DefineButtons::checkButtonNotInUse(SDL_GamepadButton button) -> bool { }); } +auto DefineButtons::checkTriggerNotInUse(SDL_GamepadButton trigger_button) -> bool { + return std::ranges::all_of(buttons_, [trigger_button](const auto &b) { + return b.button != trigger_button; + }); +} + void DefineButtons::clearButtons() { buttons_.clear(); buttons_.emplace_back(Lang::getText("[DEFINE_BUTTONS] FIRE_LEFT"), Input::Action::FIRE_LEFT, SDL_GAMEPAD_BUTTON_INVALID); diff --git a/source/define_buttons.h b/source/define_buttons.h index 24b96c2..7e8aff8 100644 --- a/source/define_buttons.h +++ b/source/define_buttons.h @@ -60,12 +60,16 @@ class DefineButtons { bool finished_ = false; // Flag para indicar si ha terminado bool closing_ = false; // Flag para indicar que está cerrando bool message_shown_ = false; // Flag para indicar que ya mostró el mensaje + bool l2_was_pressed_ = false; // Estado anterior del trigger L2 + bool r2_was_pressed_ = false; // Estado anterior del trigger R2 // Métodos void incIndexButton(); void doControllerButtonDown(const SDL_GamepadButtonEvent &event); + void doControllerAxisMotion(const SDL_GamepadAxisEvent &event); void bindButtons(Options::Gamepad *options_gamepad); auto checkButtonNotInUse(SDL_GamepadButton button) -> bool; + auto checkTriggerNotInUse(SDL_GamepadButton trigger_button) -> bool; void clearButtons(); void checkEnd(); void updateWindowMessage(); diff --git a/source/input.cpp b/source/input.cpp index 4a6b154..03a6fd1 100644 --- a/source/input.cpp +++ b/source/input.cpp @@ -65,6 +65,10 @@ auto Input::checkAction(Action action, bool repeat, bool check_keyboard, std::sh if (gamepad != nullptr) { success_controller = checkAxisInput(action, gamepad, repeat); + if (!success_controller) { + success_controller = checkTriggerInput(action, gamepad, repeat); + } + if (!success_controller) { if (repeat) { // El usuario quiere saber si está pulsada (estado mantenido) success_controller = gamepad->bindings[action].is_held; @@ -243,6 +247,54 @@ auto Input::checkAxisInput(Action action, std::shared_ptr gamepad, bool return false; } +// Comprueba los triggers del mando como botones digitales +auto Input::checkTriggerInput(Action action, std::shared_ptr gamepad, bool repeat) -> bool { + // Solo manejamos botones específicos que pueden ser triggers + if (gamepad->bindings[action].button != SDL_GAMEPAD_BUTTON_INVALID) { + // Solo procesamos L2 y R2 como triggers + SDL_GamepadButton button = gamepad->bindings[action].button; + + // Verificar si el botón mapeado corresponde a un trigger virtual + // (Para esto necesitamos valores especiales que representen L2/R2 como botones) + bool trigger_active_now = false; + + // Usamos constantes especiales para L2 y R2 como botones + if (button == TRIGGER_L2_AS_BUTTON) { // L2 como botón + Sint16 trigger_value = SDL_GetGamepadAxis(gamepad->pad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER); + trigger_active_now = trigger_value > TRIGGER_THRESHOLD; + } else if (button == TRIGGER_R2_AS_BUTTON) { // R2 como botón + Sint16 trigger_value = SDL_GetGamepadAxis(gamepad->pad, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER); + trigger_active_now = trigger_value > TRIGGER_THRESHOLD; + } else { + return false; // No es un trigger + } + + // Referencia al binding correspondiente + auto &binding = gamepad->bindings[action]; + + if (repeat) { + // Si se permite repetir, simplemente devolvemos el estado actual + return trigger_active_now; + } + + // Si no se permite repetir, aplicamos la lógica de transición + if (trigger_active_now && !binding.trigger_active) { + // Transición de inactivo a activo + binding.trigger_active = true; + return true; + } + if (!trigger_active_now && binding.trigger_active) { + // Transición de activo a inactivo + binding.trigger_active = false; + } + + // Mantener el estado actual + return false; + } + + return false; +} + void Input::addGamepadMappingsFromFile() { if (SDL_AddGamepadMappingsFromFile(gamepad_mappings_file_.c_str()) < 0) { std::cout << "Error, could not load " << gamepad_mappings_file_.c_str() << " file: " << SDL_GetError() << std::endl; @@ -280,6 +332,7 @@ void Input::resetInputStates() { for (auto &binding : gamepad->bindings) { binding.second.is_held = false; binding.second.just_pressed = false; + binding.second.trigger_active = false; } } } diff --git a/source/input.h b/source/input.h index 15e114b..4f4def9 100644 --- a/source/input.h +++ b/source/input.h @@ -21,6 +21,10 @@ class Input { static constexpr bool CHECK_KEYBOARD = true; static constexpr bool DO_NOT_CHECK_KEYBOARD = false; + // Constantes para triggers como botones + static constexpr SDL_GamepadButton TRIGGER_L2_AS_BUTTON = static_cast(100); + static constexpr SDL_GamepadButton TRIGGER_R2_AS_BUTTON = static_cast(101); + // Alias para mantener compatibilidad con el código existente using Action = InputAction; @@ -39,9 +43,10 @@ class Input { bool is_held; // Está pulsada ahora mismo bool just_pressed; // Se acaba de pulsar en este fotograma bool axis_active; // Estado del eje + bool trigger_active; // Estado del trigger como botón digital ButtonState(SDL_GamepadButton btn = SDL_GAMEPAD_BUTTON_INVALID, bool is_held = false, bool just_pressed = false, bool axis_act = false) - : button(btn), is_held(is_held), just_pressed(just_pressed), axis_active(axis_act) {} + : button(btn), is_held(is_held), just_pressed(just_pressed), axis_active(axis_act), trigger_active(false) {} }; struct Keyboard { @@ -178,6 +183,7 @@ class Input { private: // --- Constantes --- static constexpr Sint16 AXIS_THRESHOLD = 30000; + static constexpr Sint16 TRIGGER_THRESHOLD = 16384; // Umbral para triggers (aproximadamente 50% del rango) static constexpr std::array BUTTON_INPUTS = {Action::FIRE_LEFT, Action::FIRE_CENTER, Action::FIRE_RIGHT, Action::START}; // Listado de los inputs para jugar que utilizan botones, ni palancas ni crucetas // --- Variables internas --- @@ -190,6 +196,7 @@ class Input { // --- Métodos internos --- void initSDLGamePad(); static auto checkAxisInput(Action action, std::shared_ptr gamepad, bool repeat) -> bool; + static auto checkTriggerInput(Action action, std::shared_ptr gamepad, bool repeat) -> bool; auto addGamepad(int device_index) -> std::string; auto removeGamepad(SDL_JoystickID id) -> std::string; void addGamepadMappingsFromFile(); diff --git a/test_triggers.cpp b/test_triggers.cpp new file mode 100644 index 0000000..9d7e7e4 --- /dev/null +++ b/test_triggers.cpp @@ -0,0 +1,78 @@ +#include +#include + +int main() { + // Inicializar SDL + if (!SDL_Init(SDL_INIT_GAMEPAD)) { + std::cerr << "Error inicializando SDL: " << SDL_GetError() << std::endl; + return 1; + } + + std::cout << "Prueba de triggers L2/R2 como botones digitales" << std::endl; + std::cout << "Conecta un mando arcade y presiona L2 o R2" << std::endl; + std::cout << "Presiona ESC para salir" << std::endl; + + // Abrir primer gamepad disponible + SDL_Gamepad* gamepad = nullptr; + SDL_JoystickID* joysticks = SDL_GetJoysticks(nullptr); + + if (joysticks) { + for (int i = 0; joysticks[i]; i++) { + if (SDL_IsGamepad(joysticks[i])) { + gamepad = SDL_OpenGamepad(joysticks[i]); + if (gamepad) { + std::cout << "Gamepad conectado: " << SDL_GetGamepadName(gamepad) << std::endl; + break; + } + } + } + SDL_free(joysticks); + } + + if (!gamepad) { + std::cout << "No se encontró ningún gamepad compatible" << std::endl; + SDL_Quit(); + return 1; + } + + bool running = true; + SDL_Event event; + + while (running) { + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_EVENT_QUIT: + running = false; + break; + case SDL_EVENT_GAMEPAD_AXIS_MOTION: + if (event.gaxis.axis == SDL_GAMEPAD_AXIS_LEFT_TRIGGER) { + if (event.gaxis.value > 16384) { + std::cout << "L2 PRESIONADO como botón digital (valor: " << event.gaxis.value << ")" << std::endl; + } + } else if (event.gaxis.axis == SDL_GAMEPAD_AXIS_RIGHT_TRIGGER) { + if (event.gaxis.value > 16384) { + std::cout << "R2 PRESIONADO como botón digital (valor: " << event.gaxis.value << ")" << std::endl; + } + } + break; + case SDL_EVENT_GAMEPAD_BUTTON_DOWN: + std::cout << "Botón presionado: " << event.gbutton.button << std::endl; + break; + } + } + + // Verificar ESC para salir + const bool* keyboard_state = SDL_GetKeyboardState(nullptr); + if (keyboard_state[SDL_SCANCODE_ESCAPE]) { + running = false; + } + + SDL_Delay(16); // ~60 FPS + } + + if (gamepad) { + SDL_CloseGamepad(gamepad); + } + SDL_Quit(); + return 0; +} \ No newline at end of file