Verwendung von InputEvent

Was ist das?

Das Verwalten von Eingaben ist normalerweise komplex, unabhängig vom Betriebssystem oder der Plattform. Um dies ein wenig zu vereinfachen, wird ein spezieller Built-in-Typ bereitgestellt InputEvent. Dieser Datentyp kann so konfiguriert werden, dass er verschiedene Arten von Eingabeereignissen enthält. Eingabeereignisse laufen durch die Engine und können je nach Zweck an mehreren Orten empfangen werden.

Hier ist ein kurzes Beispiel, dass das Spiel beendet, sobald die Escape-Taste gedrückt wird:

func _unhandled_input(event):
    if event is InputEventKey:
        if event.pressed and event.keycode == KEY_ESCAPE:
            get_tree().quit()

Es ist jedoch sauberer und flexibler, die bereitgestellte Funktion InputMap zu verwenden, mit der Sie Eingabeaktionen definieren und ihnen verschiedene Tasten zuweisen können. Auf diese Weise können Sie mehrere Tasten für dieselbe Aktion definieren (z.B. die Escape-Taste auf der Tastatur und den Start-Button auf einem Gamepad). Sie können diese Zuordnung dann einfacher in den Projekteinstellungen ändern, ohne Ihren Code zu aktualisieren, und sogar eine Tastenzuordnung darüber erstellen, damit Ihr Spiel diese Zuordnung zur Laufzeit ändern kann!

Sie können Ihre InputMap unter Projekt > Projekt Einstellungen > Eingabe-Zuordnung einrichten und dann die folgenden Aktionen ausführen:

func _process(delta):
    if Input.is_action_pressed("ui_right"):
        # Move right.

Wie funktioniert es?

Jedes Eingabeereignis geht vom Benutzer/Spieler aus (obwohl es möglich ist, ein InputEvent zu generieren und es an die Engine zurückzugeben, was für Gesten nützlich ist). Der DisplayServer für jede Plattform liest Ereignisse vom Betriebssystem und leitet sie dann an das Root-Fenster weiter.

Der Viewport des Fensters macht eine ganze Menge Dinge mit den empfangenen Eingaben, der Reihe nach:

../../_images/input_event_flow.webp
  1. Wenn das Viewport Fenster einbettet, versucht das Viewport das Event in seiner Funktion als Window-Manager zu interpretieren (z.B. für Größenänderung oder Verschieben von Fenstern).

  2. Wenn ein eingebettetes Fenster fokussiert ist, wird das Event an dieses Fenster gesendet, im Fenster-Viewport verarbeitet und anschließend als behandelt betrachtet. Wenn kein eingebettetes Fenster fokussiert ist, wird das Event an die Nodes des aktuellen Viewports in der folgenden Reihenfolge gesendet.

  3. Zunächst einmal wird die Standardfunktion Node._input() in jedem Node aufgerufen, der sie überschreibt (und die Eingabeverarbeitung nicht mit Node.set_process_input() deaktiviert hat). Wenn eine Funktion das Ereignis konsumiert, kann sie Viewport.set_input_as_handled() aufrufen, und das Ereignis wird nicht mehr verbreitet. Dies stellt sicher, dass Sie alle Ereignisse von Interesse filtern können, sogar vor der GUI. Für Gameplay-Eingaben ist Node._unhandled_input() im Allgemeinen besser geeignet, weil es der GUI erlaubt, die Ereignisse abzufangen.

  4. Zweitens wird versucht, die Eingabe an die GUI weiterzuleiten und zu sehen, ob irgendein Control sie empfangen kann. Wenn ja, wird das Control über die virtuelle Funktion Control._gui_input() aufgerufen und das Signal "gui_input" wird ausgesendet (diese Funktion kann von Skripten durch Vererbung neu implementiert werden). Wenn das Control das Event "konsumieren" will, ruft es Control.accept_event() auf und das Event wird nicht mehr verbreitet. Mit der Property Control.mouse_filter können Sie steuern, ob ein Control über Mausereignisse per Control._gui_input()-Callback benachrichtigt wird und ob diese Ereignisse weiter verbreitet werden.

  5. Wenn bisher niemand das Event konsumiert hat, wird der Node._shortcut_input()-Callback aufgerufen, falls überschrieben (und nicht mit Node.set_process_shortcut_input() deaktiviert). Dies geschieht nur für InputEventKey, InputEventShortcut und InputEventJoypadButton. Wenn eine Funktion das Event konsumiert, kann sie Viewport.set_input_as_handled() aufrufen, und das Event wird nicht mehr verbreitet. Der Tastenkürzel-Input-Callback ist ideal für die Behandlung von Ereignissen, die als Tastenkürzel gedacht sind.

  6. Wenn bisher niemand das Event konsumiert hat, wird der Node._unhandled_key_input()-Callback aufgerufen, falls überschrieben (und nicht mit Node.set_process_unhandled_key_input() deaktiviert). Dies geschieht nur, wenn das Event ein InputEventKey ist. Wenn eine Funktion das Event konsumiert, kann sie Viewport.set_input_as_handled() aufrufen, und das Event wird nicht mehr verbreitet. Der Callback für unbehandelte Tasteneingaben ist ideal für Tasten-Events.

  7. Wenn bisher niemand das Event konsumiert hat, wird der Node._unhandled_input()-Callback aufgerufen, wenn er überschrieben ist (und nicht mit Node.set_process_unhandled_input() deaktiviert wurde). Wenn eine Funktion das Event konsumiert, kann sie Viewport.set_input_as_handled() aufrufen, und das Event wird nicht mehr weitergegeben. Der unbehandelte Eingabe-Callback ist ideal für Vollbild-Gameplay-Ereignisse, damit sie nicht empfangen werden, wenn eine GUI aktiv ist.

  8. Wenn niemand das Event bisher wollte, und Object Picking eingeschaltet ist, wird das Event für das Object Picking verwendet. Für den Root-Viewport kann dies auch in Projekteinstellungen aktiviert werden. Im Falle einer 3D-Szene, wenn dem Viewport eine Camera3D zugewiesen ist, wird ein Strahl in die Physikwelt (in Strahlrichtung des Klicks) geworfen. Wenn dieser Strahl ein Objekt trifft, wird die Funktion CollisionObject3D._input_event() im entsprechenden Physikobjekt aufgerufen. Im Falle einer 2D-Szene geschieht konzeptionell dasselbe mit CollisionObject2D._input_event().

Wenn der Viewport Events an seine Child- und darunter liegenden Nodes sendet, geschieht dies, wie in der folgenden Grafik dargestellt, in umgekehrter Reihenfolge, beginnend mit dem Node am unteren Ende des Szenenbaums und endend mit dem Root Node. Ausgenommen von diesem Prozess sind Fenster und SubViewports.

../../_images/input_event_scene_flow.webp

Bemerkung

This order doesn't apply to Control._gui_input(), which uses a different method based on event location or focused Control. GUI mouse events also travel up the scene tree, subject to the Control.mouse_filter restrictions described above. However, since these events target specific Controls, only direct ancestors of the targeted Control node receive the event. GUI keyboard and joypad events do not travel up the scene tree, and can only be handled by the Control that received them. Otherwise, they will be propagated as non-GUI events through Node._unhandled_input().

Da die Viewports keine Events an andere SubViewports senden, muss eine der folgenden Methoden verwendet werden:

  1. Verwenden Sie einen SubViewportContainer, der automatisch Events an seine untergeordneten SubViewports nach Node._input() oder Control._gui_input() sendet.

  2. Implementierung der Event-Propagation auf der Grundlage der individuellen Anforderungen.

In Übereinstimmung mit dem Node-basierten Design von Godot können spezialisierte Child-Nodes bestimmte Events verarbeiten, während ihre Vorfahren und letztendlich der Szenen-Root bei Bedarf ein allgemeineres Verhalten liefern können.

Anatomie eines InputEvents

InputEvent ist nur ein Built-in-Basistyp, stellt nichts dar und enthält nur einige grundlegende Informationen wie die Event-ID (die für jedes Event erhöht wird), den Geräteindex usw.

Es gibt verschiedene spezielle Arten von Eingabe-Events, die in der folgenden Tabelle beschrieben werden:

Event

Beschreibung

InputEvent

Leeres Eingabe-Event.

InputEventKey

Enthält einen Tasten-Code und einen Unicode-Wert sowie Modifikatoren.

InputEventMouseButton

Enthält Klickinformationen, z. B. Button, Modifikatoren usw.

InputEventMouseMotion

Enthält Bewegungsinformationen, wie z.B. relative und absolute Positionen und Geschwindigkeit.

InputEventJoypadMotion

Enthält Informationen zu den analogen Joystick/Joypad-Achsen.

InputEventJoypadButton

Enthält Informationen zu Joystick-/Joypad-Buttons.

InputEventScreenTouch

Enthält Multi-Touch-Informationen für Drücken/Loslassen. (nur auf Mobilgeräten verfügbar)

InputEventScreenDrag

Enthält Informationen zum Multi-Touch-Ziehen (nur auf Mobilgeräten verfügbar)

InputEventMagnifyGesture

Enthält eine Position, einen Faktor sowie Modifikatoren.

InputEventPanGesture

Enthält eine Position, ein Delta sowie Modifikatoren.

InputEventMIDI

Enthält MIDI-bezogene Informationen.

InputEventShortcut

Enthält ein Tastenkürzel.

InputEventAction

Enthält eine generische Aktion. Diese Events werden häufig vom Programmierer als Feedback generiert. (mehr dazu weiter unten)

Input actions

Input actions are a grouping of zero or more InputEvents into a commonly understood title (for example, the default "ui_left" action grouping both joypad-left input and a keyboard's left arrow key). They are not required to represent an InputEvent but are useful because they abstract various inputs when programming the game logic.

Dies ermöglicht:

  • Derselbe Code funktioniert für verschiedene Geräte mit unterschiedlichen Eingaben (z.B. Tastatur auf dem PC, Joypad auf Konsolen).

  • Input to be reconfigured at runtime.

  • Actions to be triggered programmatically at runtime.

Aktionen können über das Menü Projekteinstellungen auf dem Tab Eingabe-Zuordnung erstellt und Eingabe-Events zugewiesen werden.

Any event has the methods InputEvent.is_action(), InputEvent.is_pressed() and InputEvent.is_echo().

Alternativ kann es gewünscht sein, dem Spiel eine Aktion aus dem Spielcode zurückzuliefern (ein gutes Beispiel dafür ist die Erkennung von Gesten). Das Input-Singleton hat eine Methode dafür: Input.parse_input_event(). Normalerweise würden Sie sie wie folgt verwenden:

var ev = InputEventAction.new()
# Set as ui_left, pressed.
ev.action = "ui_left"
ev.pressed = true
# Feedback.
Input.parse_input_event(ev)

Siehe auch

See Eingabeaktionen erstellen for a tutorial on adding input actions in the project settings.

InputMap

Customizing and re-mapping input from code is often desired. If your whole workflow depends on actions, the InputMap singleton is ideal for reassigning or creating different actions at runtime. This singleton is not saved (must be modified manually) and its state is run from the project settings (project.godot). So any dynamic system of this type needs to store settings in the way the programmer best sees fit.