Контроллеры, геймпады и джойстики
Godot поддерживает сотни моделей контроллеров "из коробки". Контроллеры поддерживаются на Windows, macOS, Linux, Android, iOS и в веб-версии.
Примечание
Начиная с версии Godot 4.5, движок использует SDL 3 для поддержки контроллеров в Windows, macOS и Linux. Это означает, что список поддерживаемых контроллеров и их поведение должны точно соответствовать тому, что доступно в других играх и движках, использующих SDL 3. Обратите внимание, что SDL используется только для ввода, а не для управления окнами или звука.
До Godot 4.5 движок использовал собственный код поддержки контроллеров. Это могло привести к некорректной работе некоторых контроллеров. Этот пользовательский код по-прежнему используется для поддержки контроллеров на Android, iOS и в веб-версии, поэтому проблемы могут возникать только на этих платформах.
Примечание: более специализированные устройства, такие как рули, педали и HOTAS, менее протестированы и могут работать не так, как ожидалось. Переопределение силовой обратной связи для этих устройств также еще не реализовано. Если у вас есть доступ к одному из этих устройств, не стесняйтесь сообщать об ошибках на GitHub.
В этом руководстве вы узнаете:
Как написать логику ввода для поддержки ввода как с клавиатуры, так и с контроллера.
Как поведение контроллеров может отличаться от поведения клавиатуры/мыши.
Устранение неполадок с контроллерами в Godot.
Поддержка универсального ввода
Благодаря системе действий ввода Godot, Godot позволяет поддерживать ввод как с клавиатуры, так и с контроллера, без необходимости писать отдельные пути кода. Вместо жесткого кодирования клавиш или кнопок контроллера в ваших сценариях вам следует создать действия ввода в настройках проекта, которые затем будут ссылаться на указанные входы клавиш и контроллера.
Действия ввода подробно описаны на странице Использование InputEvent.
Примечание
В отличие от ввода с клавиатуры, поддержка ввода с помощью мыши и контроллера для действия (например, осмотра в игре от первого лица) потребует разных путей кода, поскольку они должны обрабатываться отдельно.
Какой singleton метод ввода я должен использовать?
Существует 3 способа получения аналогового ввода:
Если у вас есть две оси (например, джойстик или движение WASD) и вы хотите, чтобы обе оси вели себя как один вход, используйте
Input.get_vector():
# `velocity` will be a Vector2 between `Vector2(-1.0, -1.0)` and `Vector2(1.0, 1.0)`.
# This handles deadzone in a correct way for most use cases.
# The resulting deadzone will have a circular shape as it generally should.
var velocity = Input.get_vector("move_left", "move_right", "move_forward", "move_back")
# The line below is similar to `get_vector()`, except that it handles
# the deadzone in a less optimal way. The resulting deadzone will have
# a square-ish shape when it should ideally have a circular shape.
var velocity = Vector2(
Input.get_action_strength("move_right") - Input.get_action_strength("move_left"),
Input.get_action_strength("move_back") - Input.get_action_strength("move_forward")
).limit_length(1.0)
// `velocity` will be a Vector2 between `Vector2(-1.0, -1.0)` and `Vector2(1.0, 1.0)`.
// This handles deadzone in a correct way for most use cases.
// The resulting deadzone will have a circular shape as it generally should.
Vector2 velocity = Input.GetVector("move_left", "move_right", "move_forward", "move_back");
// The line below is similar to `get_vector()`, except that it handles
// the deadzone in a less optimal way. The resulting deadzone will have
// a square-ish shape when it should ideally have a circular shape.
Vector2 velocity = new Vector2(
Input.GetActionStrength("move_right") - Input.GetActionStrength("move_left"),
Input.GetActionStrength("move_back") - Input.GetActionStrength("move_forward")
).LimitLength(1.0);
Если у вас есть одна ось, которая может двигаться в обе стороны (например, дроссель на ручке управления полетом), или когда вы хотите обрабатывать отдельные оси индивидуально, используйте
Input.get_axis():
# `walk` will be a floating-point number between `-1.0` and `1.0`.
var walk = Input.get_axis("move_left", "move_right")
# The line above is a shorter form of:
var walk = Input.get_action_strength("move_right") - Input.get_action_strength("move_left")
// `walk` will be a floating-point number between `-1.0` and `1.0`.
float walk = Input.GetAxis("move_left", "move_right");
// The line above is a shorter form of:
float walk = Input.GetActionStrength("move_right") - Input.GetActionStrength("move_left");
Для других типов аналогового ввода, таких как обработка триггера или обработка одного направления за раз, используйте
Input.get_action_strength():
# `strength` will be a floating-point number between `0.0` and `1.0`.
var strength = Input.get_action_strength("accelerate")
// `strength` will be a floating-point number between `0.0` and `1.0`.
float strength = Input.GetActionStrength("accelerate");
Для неаналогового цифрового/логического ввода (только значения «нажато» или «не нажато»), например кнопок контроллера, кнопок мыши или клавиш клавиатуры, используйте Input.is_action_pressed():
# `jumping` will be a boolean with a value of `true` or `false`.
var jumping = Input.is_action_pressed("jump")
// `jumping` will be a boolean with a value of `true` or `false`.
bool jumping = Input.IsActionPressed("jump");
Примечание
Если вам нужно узнать, было ли действие ввода только что совершено в предыдущем кадре, используйте Input.is_action_just_pressed() вместо Input.is_action_pressed(). В отличие от Input.is_action_pressed(), который возвращает true, пока кнопка зажата, Input.is_action_just_pressed() вернёт true только в течение одного кадра после нажатия кнопки.
Vibration (Вибрация)
Вибрация (также называемая тактильной обратной связью) может использоваться для усиления ощущений от игры. Например, в гоночной игре с помощью вибрации можно передать поверхность, по которой едет машина, или создать внезапную вибрацию при столкновении.
Используйте метод start_joy_vibration синглтона Input, чтобы запустить вибрацию геймпада. Используйте stop_joy_vibration, чтобы остановить вибрацию раньше (полезно, если при запуске не была указана длительность).
На мобильных устройствах вы также можете использовать vibrate_handheld, чтобы вибрировало само устройство (независимо от геймпада). На Android для этого требуется, чтобы разрешение VIBRATE было включено в пресете экспорта для Android перед экспортом проекта.
Примечание
Для некоторых игроков вибрация может вызывать дискомфорт. Обязательно предоставьте внутриигровой ползунок для отключения вибрации или уменьшения её интенсивности.
Различия между клавиатурой/мышью и вводом контроллера
Если вы привыкли обрабатывать ввод с клавиатуры и мыши, вы можете быть удивлены тем, как контроллеры справляются с конкретными ситуациями.
Мертвая зона
В отличие от клавиатур и мышей, контроллеры предлагают оси с аналоговыми входами. Преимущество аналоговых входов в том, что они обеспечивают дополнительную гибкость действий. В отличие от цифровых входов, которые могут обеспечить силу только 0.0 и 1.0, аналоговый вход может обеспечить любую силу между 0.0 и 1.0. Недостатком является то, что без системы мертвых зон сила аналоговой оси никогда не будет равна 0.0 из-за того, как физически построен контроллер. Вместо этого она будет задерживаться на низком значении, таком как 0.062. Это явление известно как дрейф и может быть более заметно на старых или неисправных контроллерах.
В качестве реального примера возьмем гоночную игру. Благодаря аналоговым входам мы можем медленно направлять автомобиль в ту или иную сторону. Однако без системы мертвых зон автомобиль будет медленно двигаться сам по себе, даже если игрок не прикасается к джойстику. Это происходит потому, что сила оси направления не будет равна 0.0, когда мы этого ожидаем. Поскольку мы не хотим, чтобы в этом случае автомобиль управлялся сам по себе, мы определяем значение "мертвой зоны" 0.2, которое будет игнорировать все входные данные, сила которых меньше 0.2. Идеальное значение мертвой зоны достаточно высоко, чтобы игнорировать ввод, вызванный дрейфом джойстика, но достаточно низко, чтобы не игнорировать фактический ввод от игрока.
В Godot есть встроенная система мертвых зон для решения этой проблемы. Значение по умолчанию — 0.5, но вы можете настроить его для каждого действия отдельно во вкладке Input Map (Список действий) в Project Settings. Для Input.get_vector() мертвая зона может быть указана как необязательный 5-й параметр. Если он не указан, будет вычислено среднее значение мертвой зоны из всех действий в векторе.
"Эхо" события
В отличие от ввода с клавиатуры, удержание кнопки контроллера, например направления крестовины, не будет генерировать повторяющиеся события ввода через фиксированные интервалы (также известные как события «эха»). Это связано с тем, что операционная система вообще никогда не отправляет «эхо»-события для ввода контроллера.
Если вы хотите, чтобы кнопки контроллера отправляли события echo, вам придётся генерировать объекты InputEvent в коде и анализировать их с помощью Input.parse_input_event() с регулярными интервалами. Это можно сделать с помощью узла Timer.
Фокус окна
В отличие от ввода с клавиатуры, ввод с контроллера виден всем окнам операционной системы, включая неактивные окна.
Хотя это полезно для стороннего функционала разделения экрана <https://nucleus-coop.github.io/>, это также может иметь негативные последствия. Игроки могут случайно отправить команды контроллера запущенному проекту, взаимодействуя с другим окном.
Если вы хотите игнорировать события, когда окно проекта не сфокусировано, вам нужно создать autoload с названием Focus со следующим скриптом и использовать его для проверки всех ваших входных данных:
# Focus.gd
extends Node
var focused := true
func _notification(what: int) -> void:
match what:
NOTIFICATION_APPLICATION_FOCUS_OUT:
focused = false
NOTIFICATION_APPLICATION_FOCUS_IN:
focused = true
func input_is_action_pressed(action: StringName) -> bool:
if focused:
return Input.is_action_pressed(action)
return false
func event_is_action_pressed(event: InputEvent, action: StringName) -> bool:
if focused:
return event.is_action_pressed(action)
return false
Затем вместо Input.is_action_pressed(action) используйте Focus.input_is_action_pressed(action), где action — имя действия ввода. Также вместо event.is_action_pressed(action) используйте Focus.event_is_action_pressed(event, action), где event — ссылка на InputEvent, а action — имя действия ввода.
Профилактика энергосбережения
В отличие от ввода с клавиатуры и мыши, ввод с контроллера не блокирует спящий режим и меры энергосбережения (например, выключение экрана по истечении определенного времени).
Чтобы решить эту проблему, Godot по умолчанию включает режим энергосбережения при запуске проекта. Если вы заметили, что система отключает дисплей при игре с геймпадом, проверьте значение параметра Display > Window > Energy Saving > Keep Screen On в настройках проекта.
Устранение неполадок
См. также
Вы можете просмотреть список известных проблем с поддержкой контроллера на GitHub.
Мой контроллер не распознается Годо.
Сначала проверьте, распознаётся ли ваш контроллер другими приложениями. Вы можете воспользоваться сайтом Gamepad Tester, чтобы убедиться, что ваш контроллер распознаётся.
В Windows Godot поддерживает не более 4 контроллеров одновременно. Это связано с тем, что Godot использует API XInput, который поддерживает только 4 контроллера одновременно. Дополнительные контроллеры, превышающие это ограничение, Godot игнорирует.
Мой контроллер работает на данной платформе, но не работает на другой платформе.
Linux
Если вы используете бинарный файл движка, скомпилированный самостоятельно, убедитесь, что он скомпилирован с поддержкой udev. Эта поддержка включена по умолчанию, но её можно отключить, указав udev=no в командной строке SCons. Если вы используете бинарный файл движка, поставляемый дистрибутивом Linux, дважды проверьте, скомпилирован ли он с поддержкой udev.
Контроллеры по-прежнему могут работать без поддержки udev, но это менее надежно, поскольку необходимо использовать регулярный опрос для проверки подключения или отключения контроллеров во время игры (горячее подключение).
Android/iOS
Как описано в начале страницы, поддержка контроллеров на мобильных платформах основана на пользовательской реализации, а не на использовании SDL для ввода данных. Это означает, что поддержка контроллеров может быть менее надёжной, чем на настольных платформах.
Поддержка ввода контроллера на основе SDL на мобильных платформах запланирована в будущем выпуске.
Web
Поддержка Web-контроллеров зачастую менее надёжна по сравнению с "родными" платформами. Качество поддержки контроллеров, как правило, сильно различается в разных браузерах. В результате вам, возможно, придётся попросить игроков использовать другой браузер, если у них не получится запустить контроллер.
Как и для мобильных платформ, поддержка ввода контроллера на основе SDL на веб-платформе запланирована в будущем выпуске.