Up to date

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

Использование InputEvent

Что это?

Управлять вводом обычно сложно, независимо от ОС или платформы. Чтобы немного упростить это, предоставляется специальный встроенный тип InputEvent. Этот тип данных может быть настроен на несколько типов входных событий. Входные события проходят через движок и могут быть получены в нескольких местах, в зависимости от цели.

Вот короткий пример, закрывающий вашу игру, если нажата клавиша Escape:

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

Однако более чистым и гибким является использование предоставляемой функции InputMap, которая позволяет определить действия ввода и назначить им различные клавиши. Таким образом, вы можете определить несколько клавиш для одного и того же действия (например, клавиша "escape" на клавиатуре и кнопка "start" на геймпаде). Затем вы можете легко изменить это сопоставление в настройках проекта, не обновляя код, и даже создать функцию сопоставления клавиш поверх этой функции, чтобы ваша игра могла изменять сопоставление клавиш во время выполнения!

Вы можете настроить свою InputMap в разделе Project > Project Settings > Input Map, а затем использовать эти действия следующим образом:

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

Как это работает?

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

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.

События GUI также перемещаются вверх по дереву сцены, но, поскольку эти события нацелены на определенные Controls, только прямые предки целевого узла Controls получают событие.

В соответствии с node-дизайном Godot это позволяет специализированным дочерним узлам обрабатывать и потреблять определенные события, в то время как их предки и, в конечном счете, корень сцены могут обеспечить более обобщенное поведение, если это необходимо.

Анатомия InputEvent

InputEvent - это просто базовый встроенный тип, он ничего не представляет и содержит только некоторую базовую информацию, такую как ID события (который увеличивается для каждого события), индекс устройства и т. д.

Существует несколько специализированных типов InputEvent, описанных в таблице ниже:

Событие

Описание

InputEvent

Пустое входное событие.

InputEventKey

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

InputEventMouseButton

Содержит информацию о щелчке, такую как кнопка, модификаторы и т. д.

InputEventMouseMotion

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

InputEventJoypadMotion

Содержит информацию об аналоговой оси джойстика/джойпада.

InputEventJoypadButton

Содержит информацию о кнопке джойстика/джойпада.

InputEventScreenTouch

Содержит информацию о нажатии/отпускании с несколькими касаниями. (доступно только на мобильных устройствах)

InputEventScreenDrag

Содержит информацию о перетаскивании с помощью мультисенсорного ввода. (доступно только на мобильных устройствах)

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

Содержит общее действие. Эти события часто генерируются программистом в виде обратной связи. (подробнее об этом ниже)

Действия

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:

  • Один и тот же код работать на разных устройствах с разным вводом (например, клавиатура на ПК, джойстик на консоли).

  • Перенастраивать ввод во время выполнения.

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

Любое событие имеет методы InputEvent.is_action(), InputEvent.is_pressed() и InputEvent.

В качестве альтернативы может потребоваться вернуть в игру действие из игрового кода (хорошим примером этого является обнаружение жестов). Входной синглтон имеет для этого метод: Input.parse_input_event(). Обычно вы использовали бы это так:

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

InputMаp

Часто требуется настройка и повторное отображение входных данных из кода. Если весь ваш рабочий процесс зависит от действий, синглтон InputMap идеально подходит для переназначения или создания различных действий во время выполнения. Этот синглтон не сохраняется (должен быть изменен вручную), и его состояние запускается из настроек проекта (project.godot). Поэтому любая динамическая система такого типа должна хранить настройки так, как считает нужным программист.