Up to date

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

Usando InputEvent

¿Qué es eso?

Administrar inputs (o entradas) es normalmente complejo, sin importar en qué SO o plataforma. Para aliviar un poco esto se provee un tipo built-in especial, InputEvent, este puede ser configurado para contener varios tipos de eventos. Los eventos de entrada van a través del motor y pueden ser recibidos en múltiples lugares, dependiendo de su propósito.

Aquí hay un ejemplo rápido de cerrar el juego cuando se presiona la tecla "escape":

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

Sin embargo, es más limpio y flexible usar la funcionalidad InputMap, la que permite definir acciones de entrada y asignarlas a diferentes teclas. De este modo, se pueden definir múltiples teclas para la misma acción (por ejemplo, la tecla escape y el botón start de un gamepad). Así se puede cambiar el mapeo fácilmente en las opciones de proyecto sin necesidad de modificar el código, incluso se puede crear una funcionalidad de mapeo que permita cambiar las teclas utilizadas dentro del juego en ejecución!

Puedes configurar el InputMap dentro de Proyecto > Ajustes del Proyecto > Mapa de Entradas y luego usa las acciones de este modo:

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

¿Cómo funciona?

Every input event is originated from the user/player (though it's possible to generate an InputEvent and feed them back to the engine, which is useful for gestures). The DisplayServer for each platform will read events from the operating system, then feed them to the root Window.

The window's Viewport does quite a lot of stuff with the received input, in order:

../../_images/input_event_flow.webp
  1. If the Viewport is embedding Windows, the Viewport tries to interpret the event in its capability as a Window-Manager (e.g. for resizing or moving Windows).

  2. Next if an embedded Window is focused, the event is sent to that Window and processed in the Windows Viewport and afterwards treated as handled. If no embedded Window is focused, the event is sent to the nodes of the current viewport in the following order.

  3. First of all, the standard Node._input() function will be called in any node that overrides it (and hasn't disabled input processing with Node.set_process_input()). If any function consumes the event, it can call Viewport.set_input_as_handled(), and the event will not spread any more. This ensures that you can filter all events of interest, even before the GUI. For gameplay input, Node._unhandled_input() is generally a better fit, because it allows the GUI to intercept the events.

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

  5. If so far no one consumed the event, the Node._shortcut_input() callback will be called if overridden (and not disabled with Node.set_process_shortcut_input()). This happens only for InputEventKey, InputEventShortcut and InputEventJoypadButton. If any function consumes the event, it can call Viewport.set_input_as_handled(), and the event will not spread any more. The shortcut input callback is ideal for treating events that are intended as shortcuts.

  6. If so far no one consumed the event, the Node._unhandled_key_input() callback will be called if overridden (and not disabled with Node.set_process_unhandled_key_input()). This happens only if the event is a InputEventKey. If any function consumes the event, it can call Viewport.set_input_as_handled(), and the event will not spread any more. The unhandled key input callback is ideal for key events.

  7. If so far no one consumed the event, the Node._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 Viewport.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.

  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().

Cuando se envían eventos a hijos y nodos descendientes, el viewport lo hará como se indica en la siguiente imagen, en un orden depth-first (primero en profundidad), comenzando por el nodo al final del árbol de escenas y finalizando en el nodo raíz. Están excluidos de este proceso los nodos Window y Subviewport.

../../_images/input_event_scene_flow.png

This order doesn't apply to Control._gui_input(), which uses a different method based on event location or focused Control.

Since Viewports don't send events to other SubViewports, one of the following methods has to be used:

  1. Use a SubViewportContainer, which automatically sends events to its child SubViewports after Node._input() or Control._gui_input().

  2. Implement event propagation based on the individual requirements.

Los eventos de GUI irán hacia arriba del árbol de escenas, como los eventos apuntan a Controls específicos, sólo se dirige a ancestros de los nodos Control indicados para recibir el evento.

De acuerdo al diseño basado en nodos de Godot, esto habilita que nodos hijos especializados manipulen y consuman eventos particulares, mientras que los ancestros y finalmente el árbol de escenas, pueden proveer un comportamiento más generalizado si es necesario.

Anatomía de un InputEvent

InputEvent es un tipo base integrado, no representa nada y sólo contiene información básica, como el identificador del evento (ID, el cual es incrementado por cada evento), índice de dispositivo, etc.

Existen varios tipos de InputEvent especializados, descriptos en la siguiente tabla:

Evento

Descripción

InputEvent

Evento de entrada vacío.

InputEventKey

Contains a keycode and Unicode value, as well as modifiers.

InputEventMouseButton

Contiene información de click como botón, modificadores, etc.

InputEventMouseMotion

Contains motion information, such as relative and absolute positions and speed.

InputEventJoypadMotion

Contiene información de ejes análogos de Joystick/Joypad.

InputEventJoypadButton

Contiene información de botones de Joystick/Joypad.

InputEventScreenTouch

Contiene información sobre press/release de multi-touch. (sólo disponible en dispositivos móviles)

InputEventScreenDrag

Contiene información de arrastre multi-touch. (sólo disponible en dispositivos móviles)

InputEventMagnifyGesture

Contains a position, a factor as well as modifiers.

InputEventPanGesture

Contains a position, a delta as well as modifiers.

InputEventMIDI

Contains MIDI-related information.

InputEventShortcut

Contiene un atajo.

InputEventAction

Contiene una acción genérica. Estos eventos a menudo son generados por el programador como feedback. (más información a continuación)

Acciones

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.

This allows for:

  • Que el mismo código funcione para diferentes dispositivos con distintos dispositivos de entrada (por ejemplo, teclado en PC, Joypad en consolas).

  • Las entradas pueden ser reconfiguradas en tiempo de ejecución.

  • Actions to be triggered programmatically at run-time.

Actions can be created from the Project Settings menu in the Input Map tab and assigned input events.

Todos los eventos tienen los métodos InputEvent.is_action(), InputEvent.is_pressed() y InputEvent.

Alternativamente, es posible que quieras enviar una acción al juego desde el código (un ejemplo de esto es la detección de gestos). El singleton Input tiene un método para esto: Input.parse_input_event(). Se usa normalmente así:

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

InputMap

A menudo se desea personalizar y re-mapear la entrada desde código. Si todo el flujo de trabajo depende de las acciones, el singleton InputMap es ideal para reasignar o crear diferentes acciones en tiempo de ejecución. Este singleton no se guarda (debe ser modificado manualmente) y su estado se ejecuta desde la configuración del proyecto (project.godot). Por lo tanto, cualquier sistema dinámico de este tipo necesita almacenar los ajustes de la forma que el programador considere más adecuada.