Використання inputEvent

Що це таке?

Управління входом, як правило, складне, незалежно від ОС, або платформи. Щоб трохи полегшити це, передбачений спеціальний вбудований тип Вхідних Подій InputEvent. Цей тип даних можна настроїти так, щоб він містив кілька типів вхідних подій. Вхідні події проходять через рушій і можуть бути отримані в декількох місцях, в залежності від мети.

Ось короткий приклад, закривання гри при натисканні клавіші Esc:

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

Однак чистіше і зручніше використовувати надану функцію Карти Введення InputMap, яка дозволяє визначати дії введення і призначати їм різні клавіші. Таким чином, ви можете визначити кілька клавіш для однієї дії (наприклад, клавішу виходу з клавіатури та кнопку пуску на геймпаді). Потім ви зможете легше змінити ці призначення в параметрах проекту, не оновлюючи код, і навіть створити функцію перепризначення клавіш, щоб дозволити гравцям призначати зручні для них клавіші!

Карту вводу можна настроїти в розділі Проект > Параметри проекту > Input Map, а потім використати ці дії, наприклад, так:

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

Як усе це працює?

Кожна вхідна подія походить від користувача/гравця (хоча можна згенерувати InputEvent і подати її назад в рушій, що корисно для жестів). Об'єкт ОС для кожної платформи буде читати події з пристрою, а потім подавати їх в MainLoop (Головний Цикл). Оскільки SceneTree (Дерево сцени) є реалізацією MainLoop за замовчуванням, події подаються до нього. Godot надає функцію отримання поточного об'єкта SceneTree: get_tree().

Але SceneTree не знає, що робити з подією, тому віддає її на вікна перегляду, починаючи з "root" (кореневого) Viewport (Вікна перегляду) (першого вузла дерева сцени). Вікно перегляду робить з отриманим входом досить багато речей, по порядку:

../../_images/input_event_flow.png
  1. Перш за все, буде викликатися стандартна функція Node._input() в будь-якому вузлі, який її перевизначає (і не відключив обробку вводу з Node.set_process_input()). Якщо якась функція використовує подію, вона може викликати SceneTree.set_input_as_handled(), і подія більше не буде поширюватися. Це гарантує, що ви можете фільтрувати всі потрібні події, ще до графічного інтерфейсу. Для ігрового введення, як правило, краще підходить Node._unhandled_input(), оскільки дозволяє графічному інтерфейсу перехоплювати події.

  2. По-друге, він спробує подати вхід в графічний інтерфейс, і подивитися, чи може будь-який вузол керування Control отримати його. Якщо так, то вузол буде викликаний через віртуальну функцію Control._gui_input() і видасть сигнал "gui_input" (ця функція повторно реалізується скриптом шляхом успадкування від нього). Якщо елемент керування хоче використати подію, він викличе Control.accept_event() і подія більше не поширюватиметься. Використовуйте властивість Control.mouse_filter, щоб контролювати, чи Control отримав сповіщення про події мишки за допомогою зворотного виклику Control._gui_input(), і чи поширюються ці події далі.

  3. Якщо досі подія ніде не була використана, буде викликаний необроблений вхідний зворотний виклик, якщо вона перевизначена (і не відключена за допомогою Node.set_process_unhandled_input()). Якщо якась функція використовує подію, вона може викликати SceneTree.set_input_as_handled(), і подія більше не буде поширюватися. Необроблений вхідний зворотний виклик ідеально підходить для повноекранних ігрових подій, тому вони не отримуються, коли графічний інтерфейс активний.

  4. Якщо досі ніхто не захотів цієї події, і камера призначена для Вікна перегляду має включений Object Picking (призначений для вибирання об'єктів у вікні мишкою), у фізичний світ (в місці клацання) буде кинутий промінь. (Для кореневого вікна перегляду цю функцію також можна ввімкнути в Параметрах проекта) Якщо цей промінь натикається на об'єкт, він викличе функцію CollisionObject._input_event() у відповідному фізичному об'єкті (тіла отримують цей зворотний виклик за замовчуванням, але області - ні, проте це можна налаштувати за допомогою властивостей області).

  5. Нарешті, якщо подія так і не була оброблена, вона буде передана на наступне в дереві вікно перегляду, в противному випадку вона буде проігнорована.

При відправці подій на всі вузли прослуховування в межах сцени, вікно перегляду буде робити це в зворотному порядку глибини: Починаючи з вузла в нижній частині дерева сцени, і закінчуючи на кореневим вузлом:

../../_images/input_event_scene_flow.png

Події графічного інтерфейсу також переміщуються вгору по дереву сцени, але, оскільки ці події націлені на конкретні елементи керування (вузли Control), то події отримують лише прямі предки цільового вузла керування.

Відповідно до архітектури Godot, яка заснована на вузлах, це дозволяє спеціалізованим дочірнім вузлам обробляти і споживати певні події, в той час як їх предки, і в кінцевому підсумку корінь сцени, можуть забезпечити більш узагальнену поведінку, якщо це необхідно.

Анатомія InputEvent

InputEvent - це просто базовий вбудований тип вхідної події, він нічого не представляє і містить лише деяку основну інформацію, таку як ідентифікатор події (який збільшується для кожної події), індекс пристрою, тощо.

Існує кілька спеціалізованих типів InputEvent, описаних в таблиці нижче:

Подія

Індекс типа

Опис

InputEvent

НІЧОГО

Порожня вхідна подія.

InputEventKey

KEY

Містить сканкод і значення Юнікоду, а також модифікатори.

InputEventMouseButton

MOUSE_BUTTON

Містить інформацію про кліки мишкою, таку як клавіша, модифікатори тощо.

InputEventMouseMotion

MOUSE_MOTION

Містить інформацію про рух мишки, таку як відносні, абсолютні позиції та швидкість.

InputEventJoypadMotion

JOYSTICK_MOTION

Містить інформацію про аналогову вісь Джойстика/Джойпада.

InputEventJoypadButton

JOYSTICK_BUTTON

Містить інформацію про джойстик/джойпад.

InputEventScreenTouch

SCREEN_TOUCH

Містить інформацію про натискання/відтискання кількох дотиків. (доступно тільки на мобільних пристроях)

InputEventScreenDrag

SCREEN_DRAG

Містить відомості про переміщення кількох дотиків. (доступно тільки на мобільних пристроях)

InputEventAction

SCREEN_ACTION

Містить загальну дію. Ці події часто генеруються програмістом як зворотний зв'язок. (Докладніше про це нижче)

Дії

InputEvent може, або не може, представляти попередньо визначену дію. Дії корисні тим, що вони абстрагують пристрій введення при програмуванні логіки гри. Це дозволяє:

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

  • Переналаштування вводу під час виконання.

Дії можна створити з меню Параметри проекту на вкладці Дії.

Будь-яка подія має методи InputEvent.is_action(), InputEvent.is_pressed() та InputEvent.

Крім того, може знадобитися надати грі дію з коду гри (хорошим прикладом цього є виявлення жестів). Синглтон Input має для цього метод: Input.parse_input_event(). Зазвичай ви використовуєте його так:

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

InputMap (карта введення)

Часто потрібне налаштування та повторне зіставлення вхідних даних з коду. Якщо весь робочий процес залежить від дій, синглтон InputMap ідеально підходить для перепризначення, або створення, різних дій під час виконання. Цей синглтон не зберігається (потрібно змінювати вручну), а його стан запускається з параметрів проекту (project.godot). Таким чином, будь-яка динамічна система такого типу повинна зберігати налаштування так, як програміст найкраще вважає за потрібне.