Использование 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.

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

Каждое событие ввода инициируется пользователем/игроком (хотя можно сгенерировать событие InputEvent и передать его обратно движку, что полезно для жестов). DisplayServer для каждой платформы будет считывать события из операционной системы и передавать их корневому объекту Window.

Окно Viewport выполняет довольно много действий с полученными входными данными, в следующем порядке:

../../_images/input_event_flow.webp
  1. Если Viewport внедряет Windows, Viewport пытается интерпретировать событие с точки зрения своих возможностей как оконного менеджера (например, для изменения размера или перемещения окон).

  2. Далее, если фокус установлен на встроенном окне, событие отправляется этому окну и обрабатывается в области просмотра Windows, после чего считается обработанным. Если фокус не установлен ни на одном встроенном окне, событие отправляется узлам текущей области просмотра в следующем порядке.

  3. Прежде всего, стандартная функция Node._input() будет вызвана в любом узле, который ее переопределяет (и не отключил обработку ввода с помощью Node.set_process_input()). Если какая-либо функция использует событие, она может вызвать Viewport.set_input_as_handled(), и событие больше не будет распространяться. Это гарантирует, что вы сможете фильтровать все интересующие вас события, даже до того, как графический интерфейс. Для ввода игрового процесса, Node._unhandled_input() , как правило, подходит лучше, поскольку позволяет графическому интерфейсу перехватывать события.

  4. Во-вторых, он попытается передать входные данные в графический интерфейс и проверить, сможет ли какой-либо элемент управления их принять. Если это так, то Control будет вызван через виртуальную функцию Control._gui_input() будет подан сигнал "gui_input" (эта функция может быть повторно реализована скриптом путем наследования от него). Если элемент управления хочет "потребить" событие, он вызовет Control.accept_event(), и событие больше не будет распространяться. Используйте свойство Control.mouse_filter чтобы управлять тем, будет ли Control уведомляться о событиях мыши с помощью обратного вызова Control._gui_input(), а также тем, будут ли эти события распространяться дальше.

  5. Если до сих пор никто не воспользовался событием, будет вызван обратный вызов Node._shortcut_input() , если он переопределен (и не отключен с помощью Node.set_process_shortcut_input()). Это происходит только для InputEventKey, InputEventShortcut и InputEventJoypadButton. Если какая-либо функция использует событие, она может вызвать Viewport.set_input_as_handled(), и событие больше не будет распространяться. Обратный вызов ввода сокращенного кода идеально подходит для обработки событий, которые предназначены для сокращенного кода.

  6. Если до сих пор никто не воспользовался событием, будет вызван обратный вызов Node._unhandled_key_input(), если он переопределен (и не отключен с помощью Node.set_process_unhandled_key_input()). Это происходит только в том случае, если событие является InputEventKey. Если какая-либо функция использует событие, она может вызвать Viewport.set_input_as_handled(), и событие больше не будет распространяться. Обратный вызов необработанного ввода с клавиатуры идеально подходит для событий с клавишами.

  7. Если до сих пор никто не воспользовался событием, будет вызван обратный вызов Node._unhandled_input() , если он переопределен (и не отключен с помощью Node.set_process_unhandled_input()). Если какая-либо функция использует событие, она может вызвать Viewport.set_input_as_handled(), и событие больше не будет распространяться. Обратный вызов необработанного ввода идеально подходит для событий полноэкранного игрового процесса, поэтому они не поступают, когда активен графический интерфейс.

  8. Если событие пока никому не нужно и Object Picking включено, событие используется для выбора объекта. Для корневого окна просмотра это также можно включить в Project Settings. В случае 3D-сцены, если Camera3D назначено окну просмотра, будет проложен луч в физический мир (в направлении от точки щелчка). Если этот луч попадает на объект, он вызовет функцию CollisionObject3D._input_event() в соответствующем физическом объекте. В случае 2D-сцены концептуально то же самое происходит с CollisionObject2D._input_event().

При отправке событий в свои дочерние и подчиненные узлы окна просмотра будет делать это, как показано на следующем рисунке, в обратном порядке глубины, начиная с узла в нижней части дерева сцены и заканчивая корневым узлом. Из этого процесса исключены Windows и SubViewports.

../../_images/input_event_scene_flow.webp

Примечание

Этот порядок не применяется к Control._gui_input(), который использует другой метод в зависимости от местоположения события или сфокусированного элемента Control. События GUI (графического интерфейса) mouse также перемещаются вверх по дереву сцены в соответствии с ограничениями Control.mouse_filter, описанными выше. Однако, поскольку эти события направлены на конкретные элементы Control, только прямые предки целевого узла управления получают событие. События графического интерфейса keyboard and joypad не перемещаются по дереву сцены и могут быть обработаны только тем элементом управления, который их получил. В противном случае они будут распространяться как события, не связанные с графическим интерфейсом пользователя Node._unhandled_input().

Поскольку Viewports не отправляют события другим SubViewports, необходимо использовать один из следующих методов:

  1. Используйте SubViewportContainer, который автоматически отправляет события своему дочернему элементу SubViewports после Node._input() или Control._gui_input().

  2. Реализовать распространение событий на основе индивидуальных требований.

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

Анатомия InputEvent

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

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

Событие

Описание

InputEvent

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

InputEventKey

Содержит код ключа и значение Unicode, а также модификаторы.

InputEventMouseButton

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

InputEventMouseMotion

Содержит информацию о движении, такую как относительное и абсолютное положение и скорость.

InputEventJoypadMotion

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

InputEventJoypadButton

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

InputEventScreenTouch

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

InputEventScreenDrag

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

InputEventMagnifyGesture

Содержит позицию, фактор, а также модификаторы.

InputEventPanGesture

Содержит позицию (position), дельту (delta), а также модификаторы (modifiers).

InputEventMIDI

Содержит информацию, связанную с MIDI.

InputEventShortcut

Содержит ярлык.

InputEventAction

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

Действия ввода

Input actions (Действия ввода) представляют собой группировку нуля или более событий InputEvent в общепонятное название (например, действие по умолчанию "ui_left", объединяющее как нажатие левой кнопки джойстика, так и нажатие клавиши «стрелка влево» на клавиатуре). Они не обязательно должны представлять событие InputEvent, но полезны, поскольку абстрагируют различные входные данные при программировании игровой логики.

Это позволяет:

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

  • Входные данные подлежат перенастройке во время выполнения.

  • Действия, которые должны быть запущены программно во время выполнения.

Действия можно создавать в меню Project Settings на вкладке Input Map и назначать входные события.

Any event has the methods InputEvent.is_action(), InputEvent.is_pressed() and InputEvent.is_echo().

В качестве альтернативы может потребоваться вернуть в игру действие из игрового кода (хорошим примером этого является обнаружение жестов). Входной синглтон имеет для этого метод: 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). Поэтому любая динамическая система такого типа должна хранить настройки так, как считает нужным программист.