Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.

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. If no one wanted the event so far, and Object Picking is turned on, the event is used for object picking. For the root viewport, this can also be enabled in Project Settings. In the case of a 3D scene if a Camera3D is assigned to the Viewport, a ray to the physics world (in the ray direction from the click) will be cast. If this ray hits an object, it will call the CollisionObject3D._input_event() function in the relevant physics object. In the case of a 2D scene, conceptually the same happens with 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.png

Diese Reihenfolge gilt nicht für Control._gui_input(), die je nach Event-Ort oder fokussiertem Control eine andere Methode verwendet.

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.

GUI-Events werden auch im Szenenbaum angezeigt. Da diese Events jedoch auf bestimmte Controls abzielen, erhalten nur direkte Vorfahren des als Ziel gesetzten Control-Nodes das Event.

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)

Aktionen

Aktionen sind eine Gruppierung von null oder mehr InputEvents zu einem allgemein verständlichen Titel (z.B. die Default-Aktion "ui_left", die sowohl die Joypad-Links-Eingabe als auch die linke Pfeiltaste der Tastatur gruppiert). Sie sind nicht erforderlich, um ein InputEvent darzustellen, sind aber nützlich, weil sie verschiedene Eingaben bei der Programmierung der Spiellogik abstrahieren.

Dies ermöglicht:

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

  • Eingaben, die zur Laufzeit neu konfiguriert werden sollen.

  • Aktionen, die zur Laufzeit programmatisch ausgelöst werden sollen.

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

Jedes Event hat die Methoden InputEvent.is_action(), InputEvent.is_pressed() und InputEvent.

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)

InputMap

Die Anpassung und Neuzuordnung von Eingaben aus dem Code ist oft gewünscht. Wenn Ihr gesamter Workflow von Aktionen abhängt, ist das Singleton InputMap ideal für die Neuzuweisung oder Erstellung verschiedener Aktionen zur Laufzeit. Dieses Singleton wird nicht gespeichert (es muss manuell geändert werden) und sein Zustand wird über die Projekteinstellungen (project.godot) gesteuert. Daher muss jedes dynamische System dieser Art die Einstellungen so speichern, wie es der Programmierer für richtig hält.