Начало работы с VR
Эта статья предназначена для всех, кто хочет начать разработку проектов виртуальной реальности в UNIGINE, и настоятельно рекомендуется всем новым пользователям. Мы собираемся изучить демонстрацию VR Sample, чтобы увидеть, что внутри, и узнать, как использовать ее для создания нашего собственного проекта для VR. Мы также рассмотрим несколько простых примеров внесения изменений и расширения основных функций этого примера.
Итак, приступим!
VR Sample#
Думая о разработчиках виртуальной реальности, мы создали демо-версию VR Sample, позволяющую сразу приступить к созданию собственных проектов. Он поддерживает Oculus Rift, HTC Vive / Vive Pro "из коробки".
Рекомендуем использовать эту демонстрацию как основу для вашего VR-проекта. Здесь вы найдете набор 3D-моделей всех популярных контроллеров VR, а также реализацию основных механик, таких как захват и толкание объектов, нажатие кнопок, открытие / закрытие ящиков и многое другое.
Пример проекта создается с использованием Component System , поэтому функциональность каждого объекта определяется присоединенными компонентами.
Вы можете расширить функциональность объекта, просто добавив к нему дополнительные компоненты. Например, к объекту лазерной указки прикреплены следующие компоненты:
- ObjMovable - позволяет хватать и бросать объект
- ObjLaserPointer - позволяет отбрасывать луч света на объект.
1. Создание шаблонного проекта#
Итак, у нас есть классная демонстрация с некоторыми вещами внутри, но как нам использовать ее в качестве шаблона? Это просто - просто откройте свой SDK Browser, перейдите на вкладку Примеры и выберите Демо .
Найдите VR Sample в разделе Available и щелкните Install. После установки демоверсия появится в разделе Installed, и вы можете нажать Copy as Project, чтобы создать проект на основе этого образца.
В открывшемся окне Create New Project введите имя вашего нового проекта VR в соответствующее поле и нажмите Create New Project.
2. Настройка устройства и настройка проекта.#
Предположим, вы успешно установили выбранный головной дисплей (HMD) (пожалуйста, посетите Oculus Rift Setup или HTC Vive Setup , если вы этого не сделали). Если у вас возникли проблемы с началом работы HTC Vive, может оказаться полезным это руководство по устранению неполадок .
Основные устройства виртуальной реальности поддерживаются "из коробки", поэтому единственное, что вам нужно сделать, это убедиться, что для вашего HMD загружен правильный плагин.
HTC Vive | Oculus Rift |
---|---|
Если вы запускаете приложение через UNIGINE SDK Browser, установите для параметра Stereo 3D значение HTC Vive на вкладке Options и нажмите Apply: Для запуска плагина укажите extern_plugin параметр командной строки при запуске приложения:
|
Если вы запускаете приложение через UNIGINE SDK Browser, установите для параметра Stereo 3D значение Oculus Rift на вкладке Options и нажмите Apply: Для запуска плагина укажите extern_plugin параметр командной строки при запуске приложения:
|
3. Откройте исходный код проекта.#
Чтобы открыть проект VR в среде IDE, выберите его на вкладке Projects в браузере UNIGINE SDK и нажмите Open Code IDE.
Когда откроется IDE, вы увидите, что проект содержит много разных классов. Этот краткий обзор даст вам представление о том, о чем они все.
Не забудьте установить соответствующую платформу и параметры конфигурации для вашего проекта перед компиляцией кода в Visual Studio.
Теперь мы можем попробовать собрать наше приложение в первый раз.
Создайте свое приложение на Visual Studio (BUILD -> Build Solution) или другом языке и запустите его, выбрав проект на вкладке Projects в браузере UNIGINE SDK и нажав Run.
Перед запуском вашего приложения через браузер UNIGINE SDK убедитесь, что выбран соответствующий Customize Run Options (версия Debug и архитектура x64 в нашем случае), щелкнув многоточие под кнопкой Run.
4. Присоединение объектов к HMD#
Иногда может потребоваться прикрепить к HMD какой-нибудь предмет (например, шляпу). Все подвижные объекты (с назначенным свойством movable.prop) имеют переключатель, включающий эту опцию.
Например, если вы хотите сделать цилиндр на столе, присоединяемым к HMD, просто выберите соответствующий узел с именем "цилиндр" в поле World Hierarchy, щелкните Edit в разделе Reference и включите параметр Can Attach to Head.
Затем выберите ссылку на родительский узел и нажмите Apply.
То же самое можно сделать с помощью кода во время выполнения:
// retrieving a NodeReference named "cylinder" and getting its reference node
NodePtr node = checked_ptr_cast<NodeReference>(World::getNodeByName("cylinder"))->getReference();
// checking if this node is a movable object by trying to get its ObjMovable component
ObjMovable *obj = ComponentSystem::get()->getComponent<ObjMovable>(node);
if (obj != nullptr)
{
// making the object attachable to the HMD
obj->can_attach_to_head = 1;
}
5. Добавление нового взаимодействия#
Предположим, мы хотим расширить функциональность лазерной указки в нашем проекте, чтобы мы могли захватить , бросить и использовать (включите ) на данный момент, добавив действие альтернативного использования (изменение материала объекта, на который указывает при нажатии определенной кнопки).
Итак, мы собираемся добавить новый метод altUseIt() в класс VRInteractable для этого нового действия и сопоставить его с состоянием определенной кнопки контроллера.
VRInteractable.h
#pragma once
#include <UnigineNode.h>
#include <UniginePhysics.h>
#include "../Framework/ComponentSystem.h"
#include "Players/VRPlayer.h"
using namespace Unigine;
using namespace Math;
class VRPlayer;
class VRInteractable : public ComponentBase
{
public:
// ...
// interact methods
virtual void grabIt(VRPlayer* player, int hand_num) {}
virtual void holdIt(VRPlayer* player, int hand_num) {}
virtual void useIt(VRPlayer* player, int hand_num) {}
virtual void altuseIt(VRPlayer* player, int hand_num) {} //<-- method for new alternative use action
virtual void throwIt(VRPlayer* player, int hand_num) {}
};
Объявите и реализуйте переопределение метода altUseIt() для нашей лазерной указки в файлах ObjLaserPointer.h и ObjLaserPointer.cpp соответственно:
ObjLaserPointer.h
#pragma once
#include <UnigineWorld.h>
#include "../VRInteractable.h"
class ObjLaserPointer : public VRInteractable
{
public:
// ...
// interact methods
// ...
// alternative use method override
void altuseIt(VRPlayer* player, int hand_num) override;
// ...
private:
// ...
int change_material; //<-- "change material" state
// ...
};
ObjLaserPointer.cpp
// ...
void ObjLaserPointer::init()
{
// setting the "change material" state to 0
change_material = 0;
// ...
}
void ObjLaserPointer::update()
{
if (laser->isEnabled())
{
// ...
// show text
if (hit_obj && hit_obj->getProperty() && grabbed)
{
//---------CODE TO BE ADDED TO PERFORM MATERIAL SWITCHING--------------------
if (change_material)// if "alternative use" button was pressed
{
// change object's material to mesh_base
hit_obj->setMaterial("mesh_base", "*");
}
//---------------------------------------------------------------------------
// ...
}
else
obj_text->setEnabled(0);
}
// unsetting the "change material" state
change_material = 0;
}
// ...
// alternative use method override
void ObjLaserPointer::altuseIt(VRPlayer* player, int hand_num)
{
// setting the "change material" state
change_material = 1;
}
// ...
Теперь мы собираемся сопоставить это действие с состоянием кнопки контроллера YB . Для этого мы должны изменить класс VRPlayer (который является базовым классом для всех VR-плееров), добавив следующий код к его методу postUpdate():
VRPlayer.cpp
// ...
void VRPlayer::postUpdate()
{
for (int i = 0; i < getNumHands(); i++)
{
int hand_state = getHandState(i);
if (hand_state != HAND_FREE)
{
auto &components = getGrabComponents(i);
// ...
//-------------CODE TO BE ADDED--------------------------
// alternative use of the grabbed object
if (getControllerButtonDown(i, BUTTON::YB))
{
for (int j = 0; j < components.size(); j++)
components[j]->altuseIt(this, i);
// add callback processing if necessary
}
//--------------------------------------------------------
}
}
update_button_states();
}
// ...
6. Изменение внешнего вида контроллеров VR по умолчанию#
В своем приложении VR вы можете захотеть отображать руки вместо стандартных контроллеров, включенных в образец (например, для HTC Vive).
Это просто - выполните следующие действия:
- Откройте мир в UnigineEditor
- Выберите VR -> spawn_point (узел Dummy Player с компонентом VRPlayerSpawner) в окне World Hierarchy и перейдите в раздел Node Properties в окне Parameters.
Основные настройки VR Player (внешний вид контроллеров и точек телепорта, цвет луча телепорта, множитель силы рук для физических взаимодействий, местоположение точки появления и т. д.) можно настроить с помощью компонента VRPlayerSpawner или полей соответствующего свойства vr_player_spawner.prop.
- Перетащите требуемый узел для каждой руки из окна иерархии World Nodes или Editor Viewport в соответствующее поле свойства vr_player_spawner.prop. Например, мы можем сделать контроллеры Vive похожими наOculus Touchперетаскивая узлы oculus_touch_left и oculus_touch_right в поля Vive Left Controller и Vive Right Controller соответственно.
- Спасите мир и запустите свое приложение VR, ваши контроллеры Vive будут выглядеть как Oculus Touch в VR.
То же самое можно сделать с помощью кода во время выполнения:
// AppWorldLogic.cpp
NodeReferencePtr left_hand_node; // node reference to be used for the left controller
NodeReferencePtr right_hand_node; // node reference to be used for the right controller
// ...
int AppWorldLogic::init()
{
// getting the "spawn_point" node
NodePtr player_node = World::getNodeByName("spawn_point");
// creating nodes to replace default Vive controllers (you can use custom *.node files for left and right hand)
left_hand_node = NodeReference::create("/vr_template/oculus/oculus_touch_left.node");
right_hand_node = NodeReference::create("/vr_template/oculus/oculus_touch_right.node");
// checking if this node is a spawn point by trying to get its VRPlayerSpawner component
VRPlayerSpawner *player_spawner = ComponentSystem::get()->getComponent<VRPlayerSpawner>(player_node);
if (player_spawner != nullptr)
{
// setting new nodes to visualize Vive controllers
player_spawner->vive_controller_0 = left_hand_node->getNode();
player_spawner->vive_controller_1 = right_hand_node->getNode();
}
// ...
}
7. Добавление нового интерактивного объекта#
Следующим шагом в расширении функциональности нашего VR Sample является добавление нового интерактивного объекта.
Давайте добавим новый тип взаимодействующего объекта, который мы можем захватывать, удерживать и бросать с дополнительной функцией: объект изменит свою форму (до определенной предустановки), когда мы захватим его, и восстановим его, когда мы его отпустим. . Он также будет отображать определенный текст в консоли, если соответствующая опция включена.
Итак, мы собираемся использовать следующие компоненты:
- ObjMovable - для включения основных функций захвата и метания
- новый компонент ObjTransformer для включения функции изменения формы и печати сообщений журнала
Необходимо выполнить следующие шаги:
- Добавьте новый класс ObjTransformer, унаследованный отVRInteractable. ВVisual Studioмы можем сделать это, выбрав Проект → Добавить класс в главном меню, нажав Добавить , указав имя класса и базовый класс в в открывшемся окне и нажав Готово :
- Реализовать функциональность преобразования в указанный узел при захвате и восстановление предыдущей формы при освобождении узла
Ниже вы найдете файлы заголовков и реализации для нашего нового класса ObjTransformer:
ObjTransformer.h
#pragma once #include <UnigineNode.h> #include "Components/VRInteractable.h" #include "Framework/Utils.h" class ObjTransformer : public VRInteractable { public: ObjTransformer(const NodePtr &node, int num) : VRInteractable(node, num) {} virtual ~ObjTransformer() {} // property name UNIGINE_INLINE static const char* getPropertyName() { return "transformer"; } // parameters PROPERTY_PARAMETER(Toggle, show_text, 1); // Flag indicating if messages are to be printed to the console PROPERTY_PARAMETER(String, text, "TRANSFORMATION"); // Text to be printed to the console when grabbing or releasing the node PROPERTY_PARAMETER(Node, target_object); // Node to be displayed instead of the transformer-node, when it is grabbed // interact methods void grabIt(VRPlayer* player, int hand_num) override; // override grab action handler void throwIt(VRPlayer* player, int hand_num) override; // override trow action handler void holdIt(VRPlayer* player, int hand_num) override; // override hold action handler protected: void init() override; };
ObjTransformer.cpp
#include "ObjTransformer.h" REGISTER_COMPONENT( ObjTransformer ); // macro for component registration by the Component System // initialization void ObjTransformer::init(){ // hiding the target object (if any) if (target_object){ target_object->setEnabled(0); } } // grab action handler void ObjTransformer::grabIt(VRPlayer* player, int hand_num) { // if a target object is assigned, showing it, hiding the original object and displaying a message in the log if (target_object){ target_object->setEnabled(1); // hide original object's surfaces without disabling components ObjectPtr obj = checked_ptr_cast<Object>(node); for (int i = 0; i < obj->getNumSurfaces(); i++) obj->setEnabled(0, i); if (show_text) Log::message("\n Transformer's message: %s", text.get()); } } // throw action handler void ObjTransformer::throwIt(VRPlayer* player, int hand_num) { // if a target object is assigned, hiding it, and showing back the original object if (target_object){ target_object->setEnabled(0); // show original object's surfaces back ObjectPtr obj = checked_ptr_cast<Object>(node); for (int i = 0; i < obj->getNumSurfaces(); i++) obj->setEnabled(1, i); } } // hold action handler void ObjTransformer::holdIt(VRPlayer* player, int hand_num) { // changing the position of the target object target_object->setWorldPosition(player->getHandNode(hand_num)->getWorldPosition()); }
- Создайте приложение и запустите его, как мы это делали ранее , для нашего нового компонента будет создан новый файл свойств (transformer.prop).
- Откройте мир в UnigineEditor , создайте новый примитив блока (Create -> Primitive -> Box) и поместите его где-нибудь рядом с таблицей, создайте примитив сферы (Create -> Primitive -> Box), который будет использоваться для преобразования.
-
Чтобы добавить компоненты к объекту коробки, выберите его и щелкните Add New Property в разделе Node Properties, затем перетащите свойство movable.prop в появившееся новое пустое поле. Повторите то же самое для свойства transformer.prop и перетащите сферу из окна World Hierarchy в поле Target Object.
- Сохраните свой мир и закройте UnigineEditor.
- Запустите приложение.
8. Ограничение телепортации.#
По умолчанию можно телепортироваться в любую точку сцены. Чтобы избежать ошибок взаимодействия с пользователем в VR (например, телепортации в стены или потолок), вы можете ограничить телепортацию только определенными областями. Для этого выполните следующие действия:
- Создайте сетку, определяющую область, в которую вы хотите ограничить телепортацию пользователя;
- Установитьintersection maskк желаемой поверхности (ям) этой сетки либо в UnigineEditor, либо с помощьюsetIntersectionMask()метод:
// defining the teleportation mask as a hexadecimal value (e.g. with only the last bit enabled) int teleport_mask = 0x80000000; // setting the teleportation mask to the MyAreaMesh object's surface with the num index MyAreaMesh->setIntersectionMask(num, teleport_mask);
- Установите такую же маску пересечения для луча телепорта, используя следующий метод: VRPlayerVR::setTeleportationMask(teleport_mask).
Куда пойти отсюда#
Поздравляю! Теперь вы знаете, как создать свой собственный VR-проект на основе демо VR Sample и расширить его функциональность. Так что вы можете продолжить разработку самостоятельно. Вот несколько рекомендаций, которые могут быть полезны:
- Попытайтесь проанализировать исходный код примера и выяснить, как он работает, используйте его, чтобы написать свой собственный.
- Прочтите статью Virtual Reality Best Practices для получения дополнительной информации и полезных советов по подготовке контента для VR и улучшению взаимодействия с пользователем.
- Прочтите статью Component System для получения дополнительной информации о работе с Component System.
- Ознакомьтесь с Примером использования системы компонентов для получения более подробной информации о реализации логики с использованием системы компонентов.