Attention: Here be dragons
This is the latest
(unstable) version of this documentation, which may document features
not available in or compatible with released stable versions of Godot.
Checking the stable version of the documentation...
Utilizzo di InputEvent
Che cos'è?
Gestire gli input è solitamente complesso, non importa il sistema operativo o la piattaforma. Per semplificare un po' le cose, è disponibile uno speciale tipo integrato, InputEvent. Questo tipo di dati può essere configurato per contenere diversi tipi di eventi di input. Gli eventi di input viaggiano attraverso il motore e possono essere ricevuti in più posizioni, a seconda dello scopo.
Ecco un rapido esempio, la chiusura del gioco se viene premuto il tasto Esc:
func _unhandled_input(event):
if event is InputEventKey:
if event.pressed and event.keycode == KEY_ESCAPE:
get_tree().quit()
public override void _UnhandledInput(InputEvent @event)
{
if (@event is InputEventKey eventKey)
{
if (eventKey.Pressed && eventKey.Keycode == Key.Escape)
{
GetTree().Quit();
}
}
}
Tuttavia, è più pulito e flessibile utilizzare la funzionalità InputMap fornita, che consente di definire azioni di input e assegnarle a tasti diversi. In questo modo, è possibile definire più tasti per la stessa azione (ad esempio, il tasto Esc della tastiera e il pulsante Start di un gamepad). È quindi possibile modificare più facilmente questa mappatura nelle impostazioni del progetto, senza aggiornare il codice, e persino creare una funzionalità di mappatura dei tasti, per consentire al gioco di modificare la mappatura dei tasti in fase di esecuzione!
È possibile configurare l'InputMap in Progetto > Impostazioni del progetto > Mappa di input e poi utilizzare tali azioni così:
func _process(delta):
if Input.is_action_pressed("ui_right"):
# Move right.
public override void _Process(double delta)
{
if (Input.IsActionPressed("ui_right"))
{
// Move right.
}
}
Come funziona?
Ogni evento di input ha origine dall'utente/giocatore (anche se è possibile generare un InputEvent e inviarlo al motore, il che è utile per i gesti). Il DisplayServer per ogni piattaforma leggerà gli eventi dal sistema operativo, quindi li invierà alla finestra (Window) radice.
La Viewport della finestra fa un bel po' di cose con l'input ricevuto, in quest'ordine:
Se la Viewport incorpora le finestre, la Viewport tenta di interpretare l'evento sfruttando le proprie capacità di gestore di finestre (ad esempio, per ridimensionare o spostare le finestre).
Successivamente, se una finestra incorporata è attiva, l'evento viene inviato a tale finestra, elaborato nella viewport della finestra e infine considerato come gestito. Se nessuna finestra incorporata ha il focus, l'evento viene inviato ai nodi della viewport attuale nel seguente ordine.
Innanzitutto, la funzione standard Node._input() verrà chiamata in qualsiasi nodo che la sovrascriva (e non abbia disabilitato l'elaborazione degli input con Node.set_process_input()). Se una qualsiasi funzione consuma l'evento, può chiamare Viewport.set_input_as_handled(), e l'evento non verrà propagato più. Ciò permette di filtrare tutti gli eventi interessati, anche prima dell'interfaccia grafica. Per l'input di gioco, Node._unhandled_input() è generalmente più adatto, perché consente all'interfaccia grafica di intercettare gli eventi.
In secondo luogo, tenterà di inviare l'input all'interfaccia grafica e verificherà se qualunque controllo è in grado di riceverlo. Se sì, il Control verrà chiamato tramite la funzione virtuale Control._gui_input() e verrà emesso il segnale "gui_input" (questa funzione è reimplementabile tramite script, ereditando da essa). Se il controllo desidera "consumare" l'evento, chiamerà Control.accept_event() e l'evento non verrà propagato più. Usa la proprietà Control.mouse_filter per controllare se un Control viene notificato degli eventi del mouse tramite il callback Control._gui_input() e se questi eventi sono propagati ulteriolmente.
Se finora nessuno ha consumato l'evento, verrà chiamato il callback Node._shortcut_input() se sovrascritto (e non disabilitato con Node.set_process_shortcut_input()). Ciò accade solo per InputEventKey, InputEventShortcut e InputEventJoypadButton. Se una qualsiasi funzione consuma l'evento, può chiamare Viewport.set_input_as_handled(), e l'evento non verrà propagato più. Il callback per l'input di scorciatoie è ideale per gestire eventi che sono concepiti come scorciatoie.
Se finora nessuno ha consumato l'evento, verrà chiamato il callback Node._unhandled_key_input() se sovrascritto (e non disabilitato con Node.set_process_unhandled_key_input()). Ciò accade solo se l'evento è un InputEventKey. Se una qualsiasi funzione consuma l'evento, può chiamare Viewport.set_input_as_handled() e l'evento non verrà propagato più. Il callback per l'input da tastiera non gestito è ideale per gli eventi da tastiera.
Se finora nessuno ha consumato l'evento, verrà chiamato il callback Node._unhandled_input() se sovrascritto (e non disabilitato con Node.set_process_unhandled_input()). Se una qualsiasi funzione consuma l'evento, può chiamare Viewport.set_input_as_handled() e l'evento non verrà propagato più. Il callback per l'input non gestito è ideale per gli eventi di gioco a schermo intero, quindi non sono ricevuti quando un'interfaccia grafica è attiva.
Se nessuno ha ancora voluto l'evento e Object Picking è attivo, l'evento viene utilizzato per la selezione degli oggetti. Per la viewport principale, è possibile anche abilitare questa capacità nelle Impostazioni del progetto. Nel caso di una scena 3D, se una Camera3D è assegnata alla Viewport, verrà proiettato un raggio verso il mondo fisico (nella direzione del raggio dal clic). Se questo raggio colpisce un oggetto, verrà chiamata la funzione CollisionObject3D._input_event() nell'oggetto fisico corrispondente. Nel caso di una scena 2D, concettualmente accade la stessa cosa con CollisionObject2D._input_event().
Quando invia eventi ai suoi nodi figli e discendenti, la viewport lo fa, come illustrato nel grafico seguente, in ordine inverso di profondità, partendo dal nodo in fondo all'albero di scene e terminando con il nodo radice. Sono escluse da questo processo i nodi Window e SubViewport.
Nota
Questo ordine non si applica a Control._gui_input(), che utilizza un metodo diverso basato sulla posizione dell'evento o sul Control in focus. Inoltre, gli eventi di GUI da mouse risalgono l'albero di scene, soggetti alle restrizioni di Control.mouse_filter descritte sopra. Tuttavia, poiché questi eventi puntano a Control specifici, solo gli antenati diretti del nodo Control puntato ricevono l'evento. Gli eventi di GUI da tastiera e joypad non risalgono l'albero di scene e si possono gestire solo dal Control che li ha ricevuti. Altrimenti, verranno propagati come gli eventi non di GUI attraverso Node._unhandled_input().
Poiché le Viewport non inviano eventi ad altri SubViewport, è necessario utilizzare uno dei seguenti metodi:
Utilizzare un SubViewportContainer, che invia automaticamente gli eventi ai suoi SubViewports figli dopo Node._input() o Control._gui_input().
Implementare la propagazione degli eventi in base alle esigenze individuali.
In conformità con la progettazione basata sui nodi di Godot, questo permette ai nodi figli specializzati di gestire e consumare eventi particolari, mentre i loro antenati, e ultimamente la radice della scena, possono fornire un comportamento più generalizzato, se necessario.
Anatomia di un InputEvent
InputEvent è solo un tipo integrato base, non rappresenta nulla e contiene solo alcune informazioni basilari, come l'ID dell'evento (che è incrementato per ogni evento), l'indice di dispositivo, ecc.
Esistono diversi tipi specializzati di InputEvent, descritti nella tabella seguente:
Evento |
Descrizione |
Evento di input vuoto. |
|
Contiene un codice tasto e un valore Unicode, nonché i modificatori. |
|
Contiene informazioni su un clic, come il pulsante, modificatori, ecc. |
|
Contiene informazioni sul movimento, come la posizione relativa e assoluta e la velocità. |
|
Contiene informazioni su un asse analogico di un joystick/joypad. |
|
Contiene informazioni su un pulsante di un joystick/joypad. |
|
Contiene informazioni sulla pressione/rilascio multi-touch. (disponibile solo sui dispositivi mobili) |
|
Contiene informazioni di trascinamento multi-touch. (disponibile solo sui dispositivi mobili) |
|
Contiene una posizione, un fattore e modificatori. |
|
Contiene una posizione, un delta e modificatori. |
|
Contiene informazioni relative al MIDI. |
|
Contiene una scorciatoia. |
|
Contiene un'azione generica. Questi eventi sono spesso generati dal programmatore come feedback. (maggiori informazioni di seguito) |
Azioni di input
Le azioni di input sono un raggruppamento di zero o più InputEvent sotto un titolo comunemente comprensibile (ad esempio, l'azione predefinita "ui_left" che raggruppa sia l'input sinistro del joypad sia il tasto freccia sinistra della tastiera). Non è obbligatorio che rappresentino un InputEvent, ma sono utili perché astraggono vari input durante la programmazione della logica di gioco.
Ciò permette di:
Far funzionare lo stesso codice su dispositivi diversi con input diversi (ad esempio, tastiera su PC, joystick su console).
Riconfigurare gli input in fase di esecuzione.
Attivare azioni tramite codice in fase di esecuzione.
È possibile creare azioni dal menu Impostazioni del progetto nella scheda Mappa di input e assegnarvi eventi di input.
Ogni evento possiede i metodi InputEvent.is_action(), InputEvent.is_pressed() e InputEvent.is_echo().
Alternativamente, potrebbe essere opportuno restituire al gioco un'azione dal codice di gioco (un buon esempio è il rilevamento dei gesti). Il singleton Input ha un metodo per questo: Input.parse_input_event(). Normalmente lo si usa in questo modo:
var ev = InputEventAction.new()
# Set as ui_left, pressed.
ev.action = "ui_left"
ev.pressed = true
# Feedback.
Input.parse_input_event(ev)
var ev = new InputEventAction();
// Set as ui_left, pressed.
ev.Action = "ui_left";
ev.Pressed = true;
// Feedback.
Input.ParseInputEvent(ev);
Vedi anche
Consultare Creazione di azioni di input per un tutorial su come aggiungere azioni di input nelle impostazioni del progetto.
InputMap
Spesso è necessario personalizzare e rimappare gli input dal codice. Se l'intero flusso di lavoro dipende dalle azioni, il singleton InputMap è ideale per riassegnare o creare azioni diverse in fase di esecuzione. Questo singleton non è salvato (deve essere modificato manualmente) e il suo stato è eseguito dalle impostazioni del progetto (project.godot). Pertanto, qualsiasi sistema dinamico di questo tipo deve memorizzare le impostazioni nel modo che il programmatore ritiene più opportuno.