Utilisation d'InputEvent

Qu'est-ce que c'est ?

La gestion des entrées est généralement complexe, quel que soit le système d'exploitation ou la plate-forme. Pour faciliter un peu les choses, un type spécial intégré est fourni, InputEvent. Ce type de données peut être configuré pour contenir plusieurs types d'événements d'entrée. Les événements d'entrée voyagent à travers le moteur et peuvent être reçus à plusieurs endroits, selon le but recherché.

Voici un exemple rapide, la fermeture de votre jeu si la touche quitter est appuyée :

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();
}

Cependant, il est plus propre et plus flexible d'utiliser la fonction fournie InputMap, qui vous permet de définir des actions d'entrée et de leur assigner différentes touches. De cette façon, vous pouvez définir plusieurs touches pour la même action (par exemple, la touche d'échappement du clavier et le bouton de démarrage sur une manette de jeu). Vous pouvez alors plus facilement changer cette cartographie dans les paramètres du projet sans mettre à jour votre code, et même construire une fonction de cartographie de touche par-dessus pour permettre à votre jeu de changer la cartographie de touche à l'exécution !

Vous pouvez configurer votre InputMap sous Project > Project Settings > Input Map et utiliser ces actions comme ceci :

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.
    }
}

Comment ça marche ?

Chaque événement d'entrée provient de l'utilisateur/joueur (bien qu'il soit possible de générer un InputEvent et de le renvoyer au moteur, ce qui est utile pour les gestes). L'objet OS pour chaque plate-forme lira les événements du périphérique, puis les enverra à MainLoop. Comme SceneTree est l'implémentation par défaut de MainLoop, les événements lui sont envoyés. Godot fournit une fonction pour obtenir l'objet SceneTree courant : get_tree().

Mais SceneTree ne sait pas quoi faire de l'événement, donc il le donnera aux viewports, en commençant par la "racine" Viewport (le premier nœud de l'arbre de scènes). Viewport fait beaucoup de choses avec les données reçues, dans l'ordre :

../../_images/input_event_flow.png
  1. Tout d'abord, la fonction standard Node._input() sera appelée dans tout nœud qui la surcharge (et qui n'a pas désactivé le traitement des entrées avec Node.set_process_input()). Si une fonction consomme l'événement, elle peut appeler SceneTree.set_input_as_handled(), et l'événement ne se répandra plus. Cela vous permet de filtrer tous les événements d'intérêt, même avant l'interface graphique. Pour l'entrée du gameplay, Node._unhandled_input() est généralement plus adapté, car il permet à l'interface graphique d'intercepter les événements.

  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. Si jusqu'à présent personne n'a consommé l'événement, le rappel d'entrée non géré sera appelé s'il est surchargé (et non désactivé avec Node.set_process_unhandled_input()). Si une fonction consomme l'événement, elle peut appeler SceneTree.set_input_as_handled(), et l'événement ne se répandra plus. Le rappel d'entrée non géré est idéal pour les événements de gameplay en plein écran, de sorte qu'ils ne sont pas reçus lorsqu'une interface graphique est active.

  4. If no one wanted the event so far, and a Camera is assigned to the Viewport with Object Picking turned on, a ray to the physics world (in the ray direction from the click) will be cast. (For the root viewport, this can also be enabled in Project Settings) 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. Enfin, si l'événement n'a pas été traité, il sera transmis au prochain Viewport dans l'arbre, sinon il sera ignoré.

Lorsque vous envoyez des événements à tous les nœuds qui écoute d'une scène, la fenêtre de visualisation le fait dans l'ordre inverse de la profondeur : Commençant par le nœud en bas de l'arbre de scène, et se terminant au nœud racine :

../../_images/input_event_scene_flow.png

Les événements de l'interface graphique remontent également dans l'arbre des scènes mais, comme ces événements ciblent des Controls spécifiques, seuls les ancêtres directs du nœud de Control ciblé reçoivent l'événement.

Conformément au design de Godot basée sur les nœuds, cela permet à des nœuds enfants spécialisés de gérer et de consommer des événements particuliers, tandis que leurs ancêtres, et finalement la racine de la scène, peuvent fournir un comportement plus généralisé si nécessaire.

Anatomie d'un InputEvent

InputEvent est juste un type intégré de base, il ne représente rien et contient seulement quelques informations de base, comme l'ID de l'événement (qui est incrémenté pour chaque événement), l'index du périphérique, etc.

Il existe plusieurs types spécialisés d'InputEvent, décrits dans le tableau ci-dessous :

Évènement

Indice de type

Description

InputEvent

NONE

Évènement d'entrée vide.

InputEventKey

CLÉ

Contient un code d'analyse et une valeur Unicode, ainsi que des modificateurs.

InputEventMouseButton

MOUSE_BUTTON

Contient des informations de clic, telles que bouton, modificateurs, etc.

InputEventMouseMotion

MOUSE_MOTION

Contient l'information du mouvement, comme les positions relatives et absolues ainsi que la vitesse.

InputEventJoypadMotion

JOYSTICK_MOTION

Contient l'information de l(axe analogique du Joystick/Joypad.

InputEventJoypadButton

JOYSTICK_BUTTON

Contient des informations sur les boutons de la manette/du joystick.

InputEventScreenTouch

SCREEN_TOUCH

Contient des informations de pression/relâchement multi-touche. (uniquement disponible sur les appareils mobiles)

InputEventScreenDrag

SCREEN_DRAG

Contient des informations de glissement multi-touche. (uniquement disponible sur les appareils mobiles)

InputEventAction

SCREEN_ACTION

Contient une action générique. Ces événements sont souvent générés par le programmeur en tant que retour d'information. (plus d'informations ci-dessous)

Actions

Un InputEvent peut ou non représenter une action prédéfinie. Les actions sont utiles car elles permettent d'abstraire le périphérique d'entrée lors de la programmation de la logique du jeu. Cela permet :

  • Le même code pour travailler sur différents appareils avec différentes entrées (par exemple, clavier sur PC, Joypad sur console).

  • Entrée à reconfigurer au moment de l'exécution.

Les actions peuvent être créées à partir du menu Paramètres du projet dans l'onglet Actions.

Tout événement a les méthodes InputEvent.is_action(), InputEvent.is_pressed() et InputEvent.

Alternativement, il peut être souhaité de fournir au jeu un retour avec une action à partir du code de jeu (un bon exemple de cela est la détection des gestes). Le singleton Input a une méthode pour cela : Input.parse_input_event(). Vous l'utiliseriez normalement comme ceci :

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

Il est souvent souhaitable de personnaliser et de re-cartographier les entrées à partir du code. Si tout votre workflow dépend d'actions, le singleton InputMap est idéal pour réaffecter ou créer différentes actions à l'exécution. Ce singleton n'est pas sauvegardé (doit être modifié manuellement) et son état est lancé à partir des paramètres du projet (project.godot). Ainsi, tout système dynamique de ce type doit stocker les paramètres de la manière la plus appropriée pour le programmeur.