Eingabe-Ereignisse

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. Zweitens wird versucht, die Eingabe in die GUI zu leiten und zu prüfen, ob ein Steuerelement sie empfangen kann.Falls ja, wird Control über die virtuelle Funktion Control._gui_input() aufgerufen und das Signal "input_event" wird ausgegeben (Diese Funktion kann per Skript erneut implementiert werden indem es davon erbt). Wenn das Steuerelement das Ereignis "verbrauchen" möchte, ruft es Control.accept_event() auf und das Ereignis wird nicht mehr verbreitet. Verwenden Sie die Eigenschaft Control.mouse_filter zum steuern, ob eine Control über Mausereignisse benachrichtigt wird über Control._gui_input() Aufruf und ob Diese Ereignisse weiter verbreitet werden.
  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 Contains a scancode and Unicode value, as well as modifiers.
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.

Any event has the methods InputEvent.is_action(), InputEvent.is_pressed() and 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.