# Gamepad / controller Supporto ai controller implementato il 2026-10-24. Il gameplay non è stato riscritto: tutte le azioni passano già dalla Input Map, quindi il lavoro è stato mappare i binding, aggiungere il ramo analogico della visuale e aprire i gate che prima pretendevano il mouse catturato. ## Mappatura (layout SDL/Xbox) | Azione | Tastiera/mouse | Gamepad | | --- | --- | --- | | `move_forward` / `move_back` | W / S, frecce | stick sinistro Y (asse 1) | | `move_left` / `move_right` | A / D, frecce | stick sinistro X (asse 0) | | `jump` | Spazio | A (pulsante 0) | | `shoot` | tasto sinistro del mouse | grilletto destro (asse 5) · RB (10) | | `reload` | R | X (pulsante 2) | | `grenade` | tasto destro del mouse | B (1) · LB (9) | | `pause` | Esc | Start (6) | | `toggle_fullscreen` | Alt+F | Back/Select (4) | | `look_*` (nuove) | movimento del mouse | stick destro (assi 2 e 3) | Tutti i binding joypad hanno `device = -1`, quindi funzionano con qualunque pad collegato, non solo con il primo. ## Dove sta il codice - `project.godot` → `[input]`: eventi `InputEventJoypadButton` / `InputEventJoypadMotion` sulle azioni esistenti, quattro nuove azioni `look_left/right/up/down` (deadzone 0.2) e la ridefinizione di `ui_accept`/`ui_cancel`. - `scripts/player.gd`: - `gamepad_mode`: si accende da solo al primo pulsante/stick usato (`_is_gamepad_activity`); - `_can_aim()` sostituisce il gate `touch_mode or mouse_mode == CAPTURED` di sparo e granata: senza, col pad non si sarebbe potuto né sparare né lanciare; - `_handle_gamepad_look(dt)`: converte lo stick destro in "pixel relativi equivalenti" (900 px/s a fondo corsa, curva `m^1.8`) e li passa ad `apply_look()`, che resta l'unico punto con sensibilità e limiti di inclinazione; - a partita finita ricomincia la partita anche con A/X, e il messaggio di morte cambia testo in base al dispositivo in uso. - `scripts/start_screen.gd`: riga di aiuto che compare **solo** se c'è un pad collegato (aggiornata in hotplug via `Input.joy_connection_changed`). ## Perché `ui_accept` va ridefinito a mano Nel motore 4.7.1 `ui_accept` è mappato su Invio, Invio del tastierino e Spazio, e `ui_cancel` solo su Esc: **nessun pulsante di gamepad**. Con il pad si poteva navigare fra i controlli (le `ui_up/down/left/right` hanno già dpad e stick) ma non confermare. La ridefinizione in `project.godot` rimette anche i tasti di tastiera: senza, Invio e Spazio avrebbero smesso di funzionare. Il test lo controlla. ## Verifica ```bash godot --headless --path . --script res://tools/test_gamepad.gd ``` 33 controlli: struttura della mappa, conservazione dei tasti su `ui_accept`/`ui_cancel`, pipeline eventi reale (`Input.parse_input_event` con un pad su device 0), accensione di `gamepad_mode`, rotazione della visuale con lo stick destro e limiti di inclinazione. Con un pad vero conviene rifare la stessa prova a schermo reale (`--rendering-method gl_compatibility`) perché il puntatore e il focus non sono verificabili in headless. Suite esistenti riverificate: `test_fullscreen_shortcut`, `test_touch_controls -- --touch`, `test_grenade`, `test_boss`, `test_rebalance_branding`, `test_player_profile` tutte verdi. `test_start_screen` ha 2 FAIL preesistenti (pulsante GIOCA in headless), identici su HEAD senza queste modifiche. ## Limite noto: il nome del giocatore La regola arcade/kiosk chiede il nome a ogni avvio, e il campo è un `LineEdit` con `virtual_keyboard_enabled`: la tastiera su schermo del motore però esiste solo su Android/iOS/Web. Con **solo** il pad, su desktop non c'è modo di scrivere il nome. Fase successiva: tastiera on-screen navigabile col dpad (griglia A-Z), oppure scelta del nome da una lista di profili recenti.