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

O que é isso?

O gerenciamento de entrada normalmente é complexo, não importa o SO ou a plataforma. Para facilitar isto um pouco, um tipo especial embutido é fornecido, InputEvent. Este tipo de dado pode ser configurado para conter vários tipos de eventos de entrada. Eventos de entrada viajam pela engine e podem ser recebidos em vários locais, dependendo da finalidade.

Eis um exemplo rápido, que fecha seu jogo se a tecla Escape for pressionada:

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

No entanto, é mais limpo e flexível usar o recurso fornecido InputMap, que o permite definir ações de entrada e atribuir-lhes diferentes teclas. Desta forma você pode definir várias teclas para a mesma ação (por exemplo, a tecla Esc do teclado e o botão Start em um controle). Você então pode alterar mais facilmente este mapeamento nas configurações do projeto sem atualizar seu código, e até fazer um recurso por cima para permitir seu jogo alterar o mapeamento de teclas em tempo de execução!

Você pode configurar seu InputMap em Projeto > Configurações do Projeto > Mapa de Entrada e usar essas ações como esta:

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

Como 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 (bodies receive this callback by default, but areas do not. This can be configured through Area3D properties). In the case of a 2D scene, conceptually the same happens with CollisionObject2D._input_event().

When sending events to its child and descendant nodes, the viewport will do so, as depicted in the following graphic, in a reverse depth-first order, starting with the node at the bottom of the scene tree, and ending at the root node. Excluded from this process are Windows and SubViewports.

../../_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.

Os eventos da GUI também sobem a árvore de cena, mas, uma vez que esses eventos visam Controls específicos, apenas os ancestrais diretos do nó de Control de destino recebem o evento.

De acordo com o design baseado em nós do Godot, isto permite que nós filhos especializados tratem e consumam eventos específicos, enquanto seus ancestrais e, em última instância, a raíz da cena, podem fornecer um comportamento mais generalizado, se necessário.

Anatomia de um InputEvent

InputEvent é apenas um tipo embutido base, não representa nada e contém apenas algumas informações básicas, como o ID do evento (que é aumentado para cada evento), índice do dispositivo, etc.

Existem vários tipos especializados de InputEvent, descritos na tabela abaixo:

Evento

Descrição

InputEvent

Evento de Entrada vazio.

InputEventKey

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

InputEventMouseButton

Contém informações de clique, como botão, modificadores, etc.

InputEventMouseMotion

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

InputEventJoypadMotion

Contém informações de eixo analógico do Joystick/Joypad.

InputEventJoypadButton

Contém informações de botão do Joystick/Joypad.

InputEventScreenTouch

Contém informações de pressionamento/liberação multitoque. (disponível apenas em dispositivos móveis)

InputEventScreenDrag

Contém informações de arrasto multitoque. (disponível apenas em dispositivos móveis)

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

Contains a shortcut.

InputEventAction

Contém uma ação genérica. Esses eventos são frequentemente gerados pelo programador como feedback. (mais sobre isso abaixo)

Ações

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:

  • O mesmo código para trabalhar em diferentes dispositivos com diferentes entradas (por exemplo, teclado no PC, Joypad no console).

  • Entrada a ser reconfigurada em tempo de execução.

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

Qualquer evento tem os métodos InputEvent.is_action(), InputEvent.is_pressed() e InputEvent.

Alternativamente, pode ser desejado fornecer ao jogo uma ação do código do jogo (um bom exemplo disso é a detecção de gestos). O singleton Input tem um método para isso: Input.parse_input_event(). Você normalmente usaria assim:

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

InputMap

A personalização e o remapeamento da entrada do código são frequentemente desejados. Se todo o seu fluxo de trabalho depende de ações, o singleton InputMap é ideal para reatribuir ou criar ações diferentes em tempo de execução. Este singleton não é salvo (deve ser modificado manualmente) e seu estado é executado a partir das configurações do projeto (project.godot). Portanto, qualquer sistema dinâmico desse tipo precisa armazenar as configurações da maneira que o programador achar melhor.