Привет! Сегодня мы создадим VR-проект на Unity, работающий по стандарту OpenXR. 

Время идёт, технологии не стоят на месте, и способов разработать свой VR-проект всё больше и больше. OpenXR позволяет получить полный контроль над своим VR-проектом и безболезненно перенести в виртуальную реальность свою игру. 

Что такое OpenXR? 

OpenXR – это стандарт доступа к дополненной и виртуальной реальностям, разработанный Khronos Group и представленный в 2019 году. По сути своей, благодаря стандарту OpenXR вы можете полностью контролировать всё, что касается VR в своем Unity-проекте: положение головы, рук, работы с контроллерами и т.п. OpenXR пришёл на смену VRTK, SteamVR и специально разработанным SDK для каждой отдельной платформы (HTC Vive, WMR, Oculus), и объединивший работу со всеми платформами под одним API.

Подготовка Unity проекта

Для работы с OpenXR в Unity нам, как ни странно, потребуется Unity-проект. Создадим его. Я буду использовать Unity 2021.3.8f1.

Поднимать VR в проекте мы будем с нуля, не на основе VR Template, так что я использовал стандартный 3D шаблон – URP или HDRP в этом тестовом проекте нам не потребуются.
Поднимать VR в проекте мы будем с нуля, не на основе VR Template, так что я использовал стандартный 3D шаблон – URP или HDRP в этом тестовом проекте нам не потребуются.

В открывшемся Unity-проекте первое, что нам потребуется сделать – установить OpenXR Plugin через Unity Package Manager. Открываем Package Manager через Window/Package Manager, переключаемся на Unity Registry, ищем в списке пакетов OpenXR Plugin и устанавливаем его.

После установки возникнет диалоговое окно с предложением перезапустить Unity из-за включения новой Input System – соглашаемся и ждём перезапуска Unity.
После установки возникнет диалоговое окно с предложением перезапустить Unity из-за включения новой Input System – соглашаемся и ждём перезапуска Unity.

Когда Unity перезагрузится переходим к следующему шагу – включению и настройке OpenXR. Для этого мы открываем Project Settings (Edit/Project Settings…) и в открывшемся окне спускаемся на вкладку “XR Plug-in Management”. На этой вкладке находим группу Plug-in Providers и в ней ставим галочку напротив OpenXR. 

После включения OpenXR мы увидим, что справа от надписи “OpenXR” появился знак предупреждения. Это уведомление о некорректной настройке проекта под OpenXR. Нажимаем на иконку с предупреждением и открывается окно Open XR Project Validation. В данном окне будут перечислены имеющиеся проблемы с настройкой проекта (на мобильных платформах, например, тут будет указана невозможность работы OpenXR Plugin без использования IL2CPP). В нашем случае указано только то, что у нас не указан ни один interaction profile. Нажимаем на кнопку Edit справа от имеющейся проблемы и переходим в окно настройки OpenXR.

В окне настройки OpenXR необходимо в группе Interaction Profiles выбрать какой-нибудь профиль взаимодействий. Для меня это Oculus Touch Controller Profile, т.к. для работы я буду использовать Oculus Touch 2, подключенный к компьютеру по Oculus Link кабелю. Также необходимо выбрать Oculus в поле Play Mode OpenXR Runtime или оставить System Default если Oculus уже проставлен в своей программе как Default XR Play Mode.

Заключительным шагом будет трансформация Main Camera в VR Rig. Сделать это очень просто, т.к. в контекстном меню работы со сценой появился соответствующий пункт. Нажимаем правой кнопкой мыши по Main Camera в окне Hierarchy и выбираем пункт XR/Convert Main Camera to XR Rig. Вуоля! На сцене вместо камеры появился полноценный VR игрок – правда, без рук, но их мы добавим позже.

Теперь проверим, что OpenXR настроен и работает правильно. Добавим плоскость для теста и разместим её и Rig игрока в нулевых координатах. После всего можно нажать на кнопку Play и убедиться в том, что мы можем крутить головой на нашей сцене. Для обладателей Oculus Quest 2 потребуется дополнительно убедиться, что шлем подключен в режиме Link.

Добавляем руки

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

На сцене создадим в объекте XR Rig два пустых объекта с именами “Left Hand” и “Right Hand”. Для того, чтобы Unity автоматически их распознала как объекты рук, необходимо добавить к ним компонент TrackedPoseDriver в котором проставить Device как Generic XR Controller, а в Pose Source соответственно проставить Left Controller и Right Controller.

Далее добавим небольшой визуал, чтобы видеть наши ручки: в каждый объект рук добавим дочерние Capsule с размерами (0.1, 0.1, 0.1). Теперь запускаем сцену и видим, что помимо вращения головой мы можем двигать руками. 

Работа с контроллерами

Работа с контроллерами в OpenXR происходит через структуру InputDevice, но получить его не так просто, как кажется. Напишем несложный скрипт, который присваивает в соответствующие поля левый и правый контроллеры.

Чтобы корректно обработать имеющиеся InputDevices, нам надо на старте получить список подключенных устройств (метод UnityEndinge.XR.InputDevices.GetDevices) и подписаться на событие подключения устройства (событие InputDevices.deviceConnected). Все получаемые InputDevice мы должны прогнать через метод InitController, который определит, является ли данный InputDevice контроллером и если является, то каким – левым или правым.

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR;

public class TinyInputDeviceManager : MonoBehaviour
{
    public InputDevice LeftController;
    public InputDevice RightController;

    private void Awake()
    {
        var listDevices = new List<InputDevice>();
        InputDevices.GetDevices(listDevices);

        foreach (var inputDevice in listDevices)
        {
            InitController(inputDevice);
        }

        InputDevices.deviceConnected += InitController;
    }

    private void OnDestroy()
    {
        InputDevices.deviceConnected -= InitController;
    }

    private void InitController(InputDevice inputDevice)
    {
        if (!inputDevice.characteristics.HasFlag(InputDeviceCharacteristics.Controller))
        {
            return;
        }

        if (inputDevice.characteristics.HasFlag(InputDeviceCharacteristics.Left))
        {
            LeftController = inputDevice;
        }
        else
        {
            RightController = inputDevice;
        }
    }
}

Чуть более расширенную версию InputDeviceManager можно посмотреть на моём gist.github.com.

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

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

Чтобы получить какое-либо из значений контроллера, необходимо обратиться к InputDevice контроллера (для этого, собственно, мы и выше и запоминали, какой контроллер левый, какой правый) и вызвать у него метод TryGetFeatureValue. Возвращаемое bool-значение означает удалось ли получить значение из InputDevice, первым параметром определяется что мы хотим взять, а вторым out-параметром определяется выходное значение искомого. 

Напишем скрипт который меняет material.color у капсулы в зависимости от степени наждатия на trigger и на grip контроллера. 

using UnityEngine;
using UnityEngine.XR;

public class HandCapsule : MonoBehaviour
{
    public bool IsLeftHand;

    public InputDeviceManager InputDeviceManager;

    private Renderer _renderer;

    private void Awake()
    {
        _renderer = GetComponentInChildren<Renderer>();
    }

    private void Reset()
    {
        InputDeviceManager = FindObjectOfType<InputDeviceManager>();
    }

    private void Update()
    {
        var inputDevice = IsLeftHand ? InputDeviceManager.LeftController : InputDeviceManager.RightController;

        inputDevice.TryGetFeatureValue(CommonUsages.trigger, out var triggerValue);
        inputDevice.TryGetFeatureValue(CommonUsages.grip, out var gripValue);

        _renderer.material.color = new Color(triggerValue, gripValue, 1f);
    }
}

Далее навешиваем все написанные нами скрипты на объекты: InputControllerManager (или TinyInputControllerManager) навешиваем на XR Rig, а HandCapsule на капсулы в руках. Проставляем значение IsLeftHand на true в капсуле, лежащей в объекте Left Hand. 

Запускаем, нажимаем триггеры или grip и наблюдаем, как наши капсулы в руках переливаются всеми цветами радуги.

Благодаря TryGetFeatureValue можно обратиться к любой кнопке, стику, тачпаду и т.п. на ваших контроллерах и, в приципе, на шлеме. Соответственно, вам всего навсего потребуется реализовать логику взаимодействия с Input’ами в вашей игре, будь то телепорт на отклонение стика вперед, locomotion на тот же стик, взятие объекта на grip или вызов меню на одноименной кнопке.

Не знаю, что можно добавить, т.к. базовый функционал работы OpenXR описан и дальше работа лежит на вас и на вашей фантазии. Можно ознакомиться со стандартными примерами OpenXR, которые лежат в UPM в самом пакете, там есть полезные фишки, например использование Haptics.

Но на этом всё. Удачи в ваших начинаниях с Unity в связке с OpenXR!

Также хочу пригласить всех желающих на бесплатный урок: "Metaverse = XR + AI + Blockchain + Crypto + Web3". В рамках которого познакомимся с технологиями, из которых состоит метавселенная, и поймем в какой момент будущее уже успело наступить.

Комментарии (0)