feat(menu): navegació del menú de servei amb sticks

This commit is contained in:
JailDesigner committed 2026-05-01 09:03:20 +02:00
1 parent 2c1c1a442b
commit fddea0c449
2 files changed
+76

No files matched your search

+62
View File
@@ -467,6 +467,63 @@ void ServiceMenu::playAcceptSound() {
Audio::get()->playSound(UiConfig::get().menuAcceptSound(), Audio::Group::INTERFACE);
}
void ServiceMenu::handleGamepadAxisMotion(const SDL_GamepadAxisEvent& ev) {
// Llindars amb histèresi: cal travessar STICK_THRESHOLD per "entrar" a
// un extrem, però només es torna a la zona neutra quan baixa de
// STICK_RELEASE. Així evitem que petits tremolors al voltant del
// llindar facin spam d'events. Valors triats per donar la mateixa
// sensació que el stick del jugador (deadzone=12000) però una mica
// més estrictes per a la navegació discreta del menú.
constexpr Sint16 STICK_THRESHOLD = 16000;
constexpr Sint16 STICK_RELEASE = 8000;
std::int8_t* zone = nullptr;
bool horizontal = false;
switch (ev.axis) {
case SDL_GAMEPAD_AXIS_LEFTX:
zone = &lx_zone_;
horizontal = true;
break;
case SDL_GAMEPAD_AXIS_LEFTY:
zone = &ly_zone_;
horizontal = false;
break;
case SDL_GAMEPAD_AXIS_RIGHTX:
zone = &rx_zone_;
horizontal = true;
break;
case SDL_GAMEPAD_AXIS_RIGHTY:
zone = &ry_zone_;
horizontal = false;
break;
default:
return; // Ignorem L2/R2 i la resta.
}
// Càlcul de la zona nova: si el valor està més enllà del llindar fort,
// entra a l'extrem; si està dins de la zona morta, torna a 0; altrament
// manté la zona prèvia (banda d'histèresi).
std::int8_t new_zone = *zone;
if (ev.value > STICK_THRESHOLD) {
new_zone = +1;
} else if (ev.value < -STICK_THRESHOLD) {
new_zone = -1;
} else if (std::abs(ev.value) < STICK_RELEASE) {
new_zone = 0;
}
if (new_zone == *zone) { return; }
*zone = new_zone;
if (new_zone == 0) { return; } // Soltar el stick no dispara res.
if (horizontal) {
activateCurrent(new_zone);
playAcceptSound();
} else {
moveCursor(new_zone);
playSelectSound();
}
}
void ServiceMenu::handleGamepadButtonDown(const SDL_GamepadButtonEvent& ev) {
// El botó SERVICE (per defecte BACK; remapejable per dispositiu via
// `GamepadBindings`) tanca el menú sencer. Cada mando pot tenir el
@@ -570,6 +627,11 @@ auto ServiceMenu::handleEvent(const SDL_Event& ev) -> bool {
return true;
}
if (ev.type == SDL_EVENT_GAMEPAD_AXIS_MOTION) {
handleGamepadAxisMotion(ev.gaxis);
return true;
}
if (ev.type != SDL_EVENT_KEY_DOWN) { return false; }
const auto SC = ev.key.scancode;
+14
View File
@@ -208,6 +208,12 @@ class ServiceMenu {
// sota el llindar de clang-tidy.
void handleGamepadButtonDown(const SDL_GamepadButtonEvent& ev);
// Navegació via sticks analògics. Treballa per edge-detection: cada
// eix recorda en quina "zona" està (negativa / neutra / positiva) i
// sols dispara una acció quan creua de neutra a un extrem, igual que
// un botó del DPAD. Aplicada tant al stick esquerre com al dret.
void handleGamepadAxisMotion(const SDL_GamepadAxisEvent& ev);
// Sons d'UI: SELECT acompanya navegació (amunt/avall) i back; ACCEPT
// acompanya modificació d'opció (esquerra/dreta/retorn). Els noms de
// recurs venen d'`UiConfig` i el preload el fa el Director.
@@ -270,6 +276,14 @@ class ServiceMenu {
float transition_progress_{0.0F}; // 0 → 1 durant la transició de pàgina.
bool capturing_{false}; // True mentre esperem una tecla per a un KEY_BIND.
// Estat per eix del stick per a edge-detection: -1, 0, +1 segons la
// zona on viu el valor actual. Un canvi de zona dispara una acció
// (només quan entra a un extrem); reset a 0 quan torna al centre.
std::int8_t lx_zone_{0};
std::int8_t ly_zone_{0};
std::int8_t rx_zone_{0};
std::int8_t ry_zone_{0};
// Sub-mòdul de captura de botons del mando. Quan està actiu absorbeix
// tots els events de mando (BUTTON_DOWN/UP, AXIS_MOTION) i, en acabar,
// persisteix la nova configuració via `GamepadBindings`. Replica el