Карта действий OpenXR: настройка ввода
Наборы, действия, профили контроллеров, рабочая карта на C# и диагностика привязок.
Карта действий связывает смысл ввода с элементами XR-контроллера. В игре проверяется действие select, а карта определяет, какая кнопка его вызывает на каждом профиле устройства. Это отдельная система от обычного InputMap: добавление действия в Project Settings → Input Map не создаёт привязку OpenXR.
Из чего состоит карта
| Ресурс | Назначение | Пример |
|---|---|---|
| OpenXRActionMap | Корневой ресурс карты | openxr_action_map.rdata |
| OpenXRActionSet | Группа действий одного режима игры | gameplay, menu |
| OpenXRAction | Именованное действие и тип его значения | select, Bool |
| OpenXRInteractionProfile | Привязки для профиля контроллера | /interaction_profiles/khr/simple_controller |
| OpenXRIPBinding | Одно действие и один полный путь ввода | /user/hand/right/input/select/click |
Внутреннее имя хранится в ResourceName; LocalizedName — подпись для человека. Делайте внутренние имена короткими, стабильными, строчными, например select или move_player. Не используйте перевод подписи как ключ в игровом коде. Имена действий внутри набора и их подписи не должны дублироваться: это проверяет XR runtime при создании действий. Требования OpenXR к имени и subaction paths.
ToplevelPaths задаёт устройства, для которых существует действие, например левую и правую руки. BindingPath задаёт конкретный элемент такого устройства. Для двух рук создаются две привязки к одному действию; строка с двумя путями через запятую не нужна.
Начните с одного действия
Минимальная карта для выбора предмета:
- Создайте набор
gameplay, подпись Gameplay, Priority=0. - Добавьте действие
select, подпись Select, тип Bool. - Укажите ToplevelPaths:
/user/hand/leftи/user/hand/right. - Добавьте профиль
/interaction_profiles/khr/simple_controller. - Добавьте две привязки к
select:/user/hand/left/input/select/clickи/user/hand/right/input/select/click. - Сохраните карту в
asset://openxr_action_map.rdata.
Simple Controller описывает минимальный набор ввода. Он не предоставляет стики, все особенности конкретного контроллера или гарантию поддержки любой гарнитурой. Для дополнительного ввода добавляйте соответствующие профили и допустимые для них пути. Привязки приложения являются предложениями runtime, а не прямым доступом к сырым кнопкам. Правила предложенных привязок OpenXR.
Создание той же карты на C#
Этот вспомогательный класс создаёт ресурс и умеет сохранить его. Это не Entity-скрипт для прикрепления к объекту и не код запуска XR-сеанса.
using Reckfell;
public static class SelectionActionMap
{
// Caller owns the map. This builds resources; it does not start an XR session.
public static OpenXRActionMap Create()
{
var map = new OpenXRActionMap();
using var set = new OpenXRActionSet
{
ResourceName = "gameplay", LocalizedName = "Gameplay", Priority = 0
};
using var select = new OpenXRAction
{
ResourceName = "select", LocalizedName = "Select",
ActionType = OpenXRAction.ActionTypeEnum.Bool,
ToplevelPaths = ["/user/hand/left", "/user/hand/right"]
};
set.AddAction(select);
map.AddActionSet(set);
using var profile = new OpenXRInteractionProfile
{
InteractionProfilePath = "/interaction_profiles/khr/simple_controller"
};
using var left = new OpenXRIPBinding
{
Action = select, BindingPath = "/user/hand/left/input/select/click"
};
using var right = new OpenXRIPBinding
{
Action = select, BindingPath = "/user/hand/right/input/select/click"
};
using var bindings = new Reckfell.Collections.Array { left, right };
profile.Bindings = bindings;
map.AddInteractionProfile(profile);
return map;
}
public static Error Save(string path = "asset://openxr_action_map.rdata")
{
using var map = Create();
return ResourceSaver.Save(map, path);
}
}Из отдельного инструмента подготовки ресурсов вызовите SelectionActionMap.Save() и проверьте Error.Ok. По умолчанию файл будет записан в asset://openxr_action_map.rdata проекта, где выполнен вызов. Сначала подготовьте файл, затем запускайте XR-проект. Сохранение новой карты в _Ready() уже запущенного XR-сеанса не переподключает текущие действия.
В редакторе та же структура редактируется в доке OpenXR Action Map. Включите xr/openxr/enabled и перезапустите редактор при требовании перезапуска. Параметр xr/openxr/default_action_map должен указывать на нужный файл; его исходное значение — asset://openxr_action_map.rdata. Этот путь важнее совпадения имени файла с примером.
При отсутствии загружаемой карты native-код создаёт стандартную. В сборке с инструментами он также пытается сохранить её. Не рассчитывайте на автоматическое создание нужных игровых действий в экспортированном проекте: храните собственную карту в проекте и проверьте её попадание в экспорт.
Как читать действие
У XRController3D выберите tracker соответствующей руки: left_hand или right_hand. После успешного запуска и фокуса XR-сеанса опрашивайте действие по внутреннему имени:
| Тип действия | C#-метод XRController3D | Что означает |
|---|---|---|
| Bool | IsButtonPressed("select") | Состояние нажатия, не отдельное событие одного кадра |
| Float | GetFloat("trigger") | Аналоговое значение настроенного действия |
| Vector2 | GetVector2("move") | Две оси настроенного действия |
| Pose | Свойство Pose у XR-объекта | Выбор именованной отслеживаемой позы, не кнопка |
trigger, move и нужную позу необходимо сначала определить и привязать; минимальная карта выше содержит только select. Она не заменяет настройку XROrigin3D, камеры и запуска XR.
В редакторе карты существует тип Haptic. Однако в текущем установленном C# SDK у OpenXRAction.ActionTypeEnum нет именованного Haptic: не используйте несуществующий член и не подставляйте числовой cast. Настройте такое действие в редакторе карты. Оно должно ссылаться на поддерживаемый выход, например /user/hand/right/output/haptic, прежде чем вызывать TriggerHapticPulse. Длительность этого вызова задаётся в секундах; Duration у OpenXRHapticVibration для модификаторов — в наносекундах. Наличие binding не подтверждает работу вибрации на устройстве.
Наборы и изменение карты
Для первого проекта оставьте один набор. Позже разделяйте gameplay и menu, если один элемент контроллера должен выполнять разные задачи. Активность переключается через OpenXRInterface.SetActionSetActive по имени существующего набора. Priority определяет разрешение конфликтов активных наборов; не оставляйте конфликтующие наборы активными без необходимости. Добавление OpenXRAction во второй OpenXRActionSet переносит его из первого: для независимых действий нужны разные ресурсы.
Не меняйте исходные ресурсы в надежде перестроить уже подключённый XR-сеанс. После подключения наборов OpenXR запрещает добавлять действия и менять предложенные привязки для этого сеанса. Подготовьте карту и перезапустите сеанс. Переключение активности существующих наборов — отдельная операция. Ограничения xrAttachSessionActionSets.
GetActionSets() в текущей реализации возвращает общий Array. Его Clear() изменяет карту без Changed. Для обычного редактирования пользуйтесь AddActionSet/RemoveActionSet и соответствующими методами ресурсов; не очищайте возвращённый контейнер как будто это независимая копия.
Модификаторы добавляйте после проверки базового ввода. Параметры xr/openxr/binding_modifiers/analog_threshold и xr/openxr/binding_modifiers/dpad_binding запрашивают соответствующие расширения; поддержка зависит от runtime. Для аналогового порога разделяйте порог включения и выключения, чтобы избежать дребезга около границы. D-pad предназначен для подходящего двумерного ввода, а не произвольного пути. Каждый модификатор нужно проверить на целевом runtime: сохранение ресурса не подтверждает его применение устройством.
Если действие не работает
| Симптом | Проверка |
|---|---|
| В коде всегда false | XR-сеанс запущен и имеет фокус; выбран правильный tracker; набор активен |
| Работает только одна рука | Есть обе ToplevelPaths и отдельный BindingPath для каждой руки |
| Карта сохранена, но изменений нет | Совпадает xr/openxr/default_action_map; сеанс перезапущен после сохранения |
| Не работает только на другом контроллере | Добавлен нужный interaction profile; путь допустим для него; runtime принял bindings |
| Ошибка создания action | Нет пустых/повторных имён и подписей; тип действия подходит пути |
| Не работает вибрация | Есть выходное действие Haptic и нужный output-путь; передано верное имя и tracker; единицы длительности верны |
В установленном движке без гарнитуры проверены точный C#-пример, структура, ссылки двух привязок на одно действие, сохранение .rdata и независимая повторная загрузка. Также проверены перенос действия между наборами и общая природа Array. Физический ввод, фокус сеанса, pose tracking, haptics, модификаторы и редакторские действия этим прогоном не проверялись.