Using 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 integrierter 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.scancode == KEY_ESCAPE:
            get_tree().quit()
public override void _UnhandledInput(InputEvent @event)
{
    if (@event is InputEventKey eventKey)
        if (eventKey.Pressed && eventKey.Scancode == (int)KeyList.Escape)
            GetTree().Quit();
}

Es ist jedoch sauberer und flexibler, die bereitgestellte Funktion InputMap zu verwenden, mit der Sie Eingabeaktionen definieren und ihnen verschiedene Schlüssel 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 die Start-Taste auf einem Gamepad). Sie können diese Zuordnung dann einfacher in den Projekteinstellungen ändern, ohne Ihren Code zu aktualisieren, und sogar eine Schlüsselzuordnung darüber erstellen, damit Ihr Spiel diese Zuordnung zur Laufzeit ändern kann!

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

func _process(delta):
    if Input.is_action_pressed("ui_right"):
        # Move right.
public override void _Process(float delta)
{
    if (Input.IsActionPressed("ui_right"))
    {
        // Move right.
    }
}

Wie funktioniert das?

Jedes Eingabe-Ereignis stammt vom Benutzer/Spieler (obwohl es möglich ist, ein Eingabe-Ereignis zu generieren und es an die Engine zurückzusenden, was für Gestensteuerung nützlich ist). Das Betriebssystemobjekt für jede Plattform liest Ereignisse vom Gerät und leitet sie dann an MainLoop weiter. Da Szenenbaum die Standard-MainLoop-Implementierung ist, werden hier die Ereignisse zugeführt. Godot bietet eine Funktion zum Abrufen des aktuellen Szenenbaum-Objekts: get_tree().

Der Szenenbaum weiß jedoch nicht, was mit dem Ereignis zu tun ist, und gibt es daher an das Ansichtsfenster weiter, beginnend mit "root" Viewport (der erste Node des Szenenbaums). Ansichtsfenster machen ziemlich viel mit den empfangenen Eingaben, in dieser Reihenfolge:

../../_images/input_event_flow.png
  1. Zunächst wird die Funktion standard Node._input() in jedem Node aufgerufen, der sie überschreibt (und die Eingabeverarbeitung nicht mit Node.set_process_input() aufrufen und das Ereignis wird nicht mehr verbreitet. Dies stellt sicher, dass Sie noch vor der GUI alle interessanten Ereignisse filtern können. Für die Gameplay-Eingabe passt im Allgemeinen Node._unhandled_input() besser, da die GUI die Ereignisse abfangen kann.

  2. Second, it will try to feed the input to the GUI, and see if any control can receive it. If so, the Control will be called via the virtual function Control._gui_input() and the signal "gui_input" will be emitted (this function is re-implementable by script by inheriting from it). If the control wants to "consume" the event, it will call Control.accept_event() and the event will not spread any more. Use the Control.mouse_filter property to control whether a Control is notified of mouse events via Control._gui_input() callback, and whether these events are propagated further.

  3. If so far no one consumed the event, the unhandled input callback will be called if overridden (and not disabled with Node.set_process_unhandled_input()). If any function consumes the event, it can call SceneTree.set_input_as_handled(), and the event will not spread any more. The unhandled input callback is ideal for full-screen gameplay events, so they are not received when a GUI is active.

  4. If no one wanted the event so far, and a Camera 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 CollisionObject._input_event() function in the relevant physics object (bodies receive this callback by default, but areas do not. This can be configured through Area properties).

  5. Wenn das Ereignis nicht behandelt wurde, wird es schließlich an den nächsten Ansichtsbereich in der Baumstruktur übergeben, andernfalls wird es ignoriert.

Wenn Sie Ereignisse an alle empfangenden Nodes innerhalb einer Szene senden, erfolgt dies im Ansichtsfenster in umgekehrter Reihenfolge: Beginnend mit dem untersten Node des Szenenbaums und endet am Wurzel-Node:

../../_images/input_event_scene_flow.png

GUI-Ereignisse werden auch im Szenenbaum angezeigt. Da diese Ereignisse jedoch auf bestimmte Steuerelemente abzielen, erhalten nur direkte Vorfahren des zum Ziel gesetzten Kontroll-Nodes das Ereignis.

In Übereinstimmung mit dem Node-basierten Design von Godot können spezialisierte untergeordnete Nodes bestimmte Ereignisse verarbeiten, während ihre Vorfahren und letztendlich die Szenenwurzel bei Bedarf ein allgemeineres Verhalten liefern können.

Anatomie eines Eingabe-Ereignisses

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

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

Ereignis

Typ Index

Beschreibung

InputEvent

NONE

leeres Eingabe-Ereignis

InputEventKey

KEY

Enthält einen Scancode und einen Unicode-Wert sowie Modifikatoren.

InputEventMouseButton

MOUSE_BUTTON

enthält Klickinformationen wie Schaltflächen, Modifikatoren usw.

InputEventMouseMotion

MOUSE_MOTION

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

InputEventJoypadMotion

JOYSTICK_MOTION

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

InputEventJoypadButton

JOYSTICK_BUTTON

Enthält Informationen zu Joystick-/Joypad-Knöpfen.

InputEventScreenTouch

SCREEN_TOUCH

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

InputEventScreenDrag

SCREEN_DRAG

Enthält Informationen zum Ziehen mit mehreren Berührungen. (multi-touch - nur auf Mobilgeräten verfügbar)

InputEventAction

SCREEN_ACTION

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

Aktionen

Ein Eingabe-Ereignis kann eine vordefinierte Aktion darstellen oder nicht. Aktionen sind nützlich, da sie das Eingabegerät beim Programmieren der Spielelogik abstrahieren. Dies ermöglicht:

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

  • Eingaben, die zur Laufzeit neu konfiguriert werden sollen.

Aktionen können über das Menü Projekteinstellungen auf der Registerkarte Aktionen erstellt werden.

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

Alternatively, it may be desired to supply the game back with an action from the game code (a good example of this is detecting gestures). The Input singleton has a method for this: Input.parse_input_event(). You would normally use it like this:

var ev = InputEventAction.new()
# Set as move_left, pressed.
ev.action = "move_left"
ev.pressed = true
# Feedback.
Input.parse_input_event(ev)
var ev = new InputEventAction();
// Set as move_left, pressed.
ev.SetAction("move_left");
ev.SetPressed(true);
// Feedback.
Input.ParseInputEvent(ev);

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 run-time. 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.