Поделиться через



СЕНТЯБРЬ 2016

ТОМ 31, НОМЕР 9

Unity - Создание приложений виртуальной реальности

Тим Кульп | Сентябрь 2016

Продукты и технологии:

Unity, C#, виртуальная реальность

В статье рассматриваются:

  • создание базового UI в виртуальной реальности;
  • подключение к внешним данным в приложении виртуальной реальности;
  • связывание с данными игровых объектов в Unity.

Исходный код можно скачать по ссылке

Виртуальная реальность (VR) предоставляет не только уникальные возможности разработчикам, но и ставит перед ними трудные задачи. С одной стороны, есть возможность обеспечить погружение в VR, не связанную существующей парадигмой двухмерных UI. С другой стороны, возникает проблема создания впечатляющей VR, которая является чем-то большим простого трехмерного представления двухмерного экрана. К счастью, некоторые фундаментальные основы разработки приложений и дизайна UI похожи, например создание представления «основные/подробные данные» (master/details), получение ввода от пользователя и взаимодействие между приложениями через сервисы. В этой статье я исследую эти области в контексте создания бизнес-приложения виртуальной реальности, чтобы вы поняли, как приступить к разработке собственных VR-приложений.

Почему VR?

Сейчас самое время заняться разработкой VR-приложений из-за появления отличного инструментария вроде Unity и сравнительно небольшого рынка VR. Подумайте о современном рынке мобильных приложений. На нем предлагаются миллионы приложений, и новые приложения сталкиваются с колоссальной конкуренцией. Но рынок VR все еще невелик, что упрощает задачу обратить на себя внимание. Помимо размера рынка, в настоящее время ощущается значительная нехватка VR-контента для потребителей, которые уже приобрели аппаратные средства виртуальной реальности. Создание приложений в этой области пока остается новой трудной задачей, а рекомендации по их созданию еще только нарабатываются. Первопроходцы в VR-разработке помогут сформировать будущее этой платформы.

Сейчас самое время заняться разработкой VR-приложений из-за появления отличного инструментария вроде Unity и сравнительно небольшого рынка VR.

Создание приложений для таких VR-устройств, как Samsung Gear VR и Google Cardboard, — хорошая отправная точка для разработчиков, начинающих подготавливать свой контент для каналов сбыта бюджетной продукции. При разработке в расчете на Mixed Reality VR-приложения можно портировать на HoloLens, поскольку инструментарий одинаков и контент тоже может быть одинаков. Единственное различие — код, выполняющий задачи (например, Gaze или Raycast, которые мы рассмотрим далее в этой статье), и использование некоторых уникальных функций HoloLens, таких как речь, пространственное картирование (spatial mapping) и жесты. Немного исследований и экспериментов, и вы сможете преобразовать свое VR-приложение в приложение HoloLens.

Я не стану вдаваться в чрезмерные подробности подготовки проекта VR. Вместо этого я советую изучить проект Unity VR Samples в Unity Asset Store (bit.ly/1qYLBX9). Проект в этой статье я создам с нуля, но буду использовать скрипты из проекта Unity VR Samples, чтобы задействовать существующий код для стандартных задач (например, чтобы сделать объект интерактивным). Я рекомендую создать пустой проект Unity и импортировать Unity VR Samples для ссылки вне создаваемого в этой статье проекта. Unity VR Samples имеет массу великолепного контента, но построен как автономное приложение и требует некоторого дополнительного конфигурирования для выполнения сцены вне включенных сцен. Держите Unity VR Samples открытым и доступным, так как в нем есть несколько скриптов, которые понадобятся в моем проекте. Для этой статьи я буду использовать Unity 5.3 с поддержкой инструментария Android, чтобы иметь возможность создать приложение для устройства Samsung Gear VR.

Система Contoso Travel

Contoso Travel — корпоративная система управления командировками (travel system), позволяющая администратору видеть заявки на бронирование для членов его группы. Администраторы могут просматривать командированных сотрудников (travelers) по аэропорту, искать конкретный город и изучать подробные сведения о поездке сотрудника. В приложении я хочу создать богатую среду, показывающую пространственное взаиморасположение командированных сотрудников Contoso. Обычно это реализуется как специализированное веб- или мобильное бизнес-приложение (line-of-business, LOB), доставляемое пользователям через браузер. Для этой статьи я создам такое приложение в VR, чтобы обеспечить пользователям более глубокие впечатления, применять аватары, представляющие командированных сотрудников, и отображать, где находятся люди относительно друг друга в трехмерном пространстве.

VR открывает много возможностей для подобного приложения. С точки зрения администратора, VR позволяет получить более выраженный личный опыт благодаря контакту с аватарами командированных вместо их отображения в виде точек на карте или имен в списке. Для командированных VR может дать предварительное представление об офисе, в который они направляются, или о местах, которые нужно посмотреть. Здесь идея в том, чтобы создать эффект погружения, которым смогут наслаждаться пользователи.

Приступаем

Вам нужно подготовить этот проект как мобильный. Чтобы начать, создайте 3D Unity Project с именем ContosoTravelVR. Первым делом настройте временной режим мира под более медленную частоту кадров. В Edit | Project Settings | Time inspector задайте Fixed Timestep = 0.01666666, Maximum Allowed Timestep = 0.1666666 и Timescale = 1. Maximum Allowed Timestep замедляет физическое время в Unity, чтобы расчеты, связанные с физикой, не влияли на частоту кадров. Хотя время вычислений физики и частота кадров прямо не связаны, нагрузка на процессор, вызываемая расчетами физики, может создавать проблемы с обработкой кадров. Задание Maximum Allowed Timestep накладывает ограничение на то, сколько времени должно тратиться на обновление физики. Помните, что в мире мобильной VR устройство рисует два изображения на каждый кадр: одно для левого глаза и другое для правого. Сложные вычисления при более высоких частотах кадров могут привести к рассогласованию кадров, выдаваемых устройством, таким как смартфон, из-за чего каждый глаз будет видеть разные изображения. Это приводит к дезориентации и вызывает тошноту у пользователей. Очевидно, что крайне важно предотвратить это. Избегайте всего, что могло бы отрицательно сказаться на производительности, например большого количества полигонов для объектов, длительно выполняемых скриптов или блокирования потока.

Затем обновите Player Settings для мобильной VR. Для этого откройте Edit | Project Settings | Player и выберите раздел Other Settings. В этом разделе есть параметр Virtual Reality Supported. Установите флажок рядом с ним, чтобы мир распознавался как VR-приложение. Благодаря этому объект Main Camera, уже существующий в Unity будет автоматически преобразован в камеру VR.

Для командированных VR может дать предварительное представление об офисе, в который они направляются, или о местах, которые нужно посмотреть.

Теперь, настроив приложение, подготовьте сцену. В окне Hierarchy добавьте куб с масштабом x=10, y=0.1 и z=10. Сбросьте позицию куба, чтобы центрировать его по координатам x=0, y=0 и z=0 как уплощенный куб в мире. За счет этого у вас появится нижняя стенка для мира, что даст пользователям ощущение пространства и основы (grounding), а также место для сущностей в этом мире, где они будут располагаться. Как и в двухмерных приложениях, вы должны позаботиться о том, чтобы пользователь понимал, где будет появляться контент и происходить взаимодействие с ним. Это помогает пользователям ориентироваться в приложении. В случае ContosoTravel все действия будут происходить в границах этого объекта стенок мира.

В этом мире вам потребуется объект, который будет выполнять маршалинг данных через приложение. Создайте Empty Game Object в окне Hierarchy и переименуйте этот объект в TravelerManager. Он будет источником всех данных о командированных в приложении. Выделив игровой объект TravelerManager, щелкните Add Component | New Script. Назовите скрипт TravelerManager и щелкните Create and Add. Это приведет к созданию нового скрипта для обработки логики TravelerManager.

Приложение для управления командировками должно видеть всех командированных от данной организации. Для этого добавьте новый игровой объект (Game Object) к иерархии (Hierarchy) и назовите его TravelerTemplate. Это будет объект, отображаемый для каждого командированного, представленного в системе. Если вы знакомы с заготовками (prefabs), используйте персонаж, сконструированный вами, или возьмите что-то из Asset Store. Для своего приложения я буду использовать низкополигональные персонажи, найденные в Asset Store, придающие миру забавный вид. Для TravelerTemplate подойдет любой игровой объект — заготовка, куб, сфера и др. Добавив TravelerTemplate, отключите этот игровой объект, щелкнув флажок рядом с именем игрового объекта в окне Inspector. После этого TravelerTemplate исчезнет с экрана.

Наконец, добавьте в свой проект следующие скрипты из проекта Unity VR Samples: VREyeRaycast, VRInput, VRInteractiveItem и Reticle. Конкретное предназначение этих скриптов мы обсудим, когда я буду использовать их далее в этой статье, но они избавили меня от немалой части работы.

Подготовка класса TravelerManager

В Unity дважды щелкните скрипт TravelerManager, чтобы открыть его в MonoDevelop или Visual Studio. Добавьте в компонент код с рис. 1.

Рис. 1. Скриптовый компонент TravelerManager

public GameObject TravelerTemplate;
public List<Traveler> TravelerList;
void Awake () {
  TravelerList = new List<Traveler>();
}
public void LoadTravelers() {
  try
  {
    // Здесь добавьте GetTravelers
  }
  catch(Exception ex)
  {
    Debug.Log(ex.ToString());
  }
}

TravelerTemplate будет хранить Prefab, используемый мной для создания командированных сотрудников на экране. Помните: TravelerTemplate может быть любым игровым объектом: кубом, сферой, персонажем-заготовкой и т. д. Кроме того, если сделать эту переменную открытой, ее значение можно задавать средствами дизайна в Unity, что позволяет группе дизайнеров управлять шаблоном командированного (traveler template) без переписывания кода для TravelerManager. Вы также формируете два списка: один для хранения всех командированных, созданных как GameObject, а другой для поддержания данных по каждому командированному. Затем вы добавите механизмы ввода для загрузки командированных и дадите возможность пользователям что-то делать в приложении.

Подключение кнопки

Подготовив базовый мир, вы должны принимать ввод от пользователя, чтобы приложение что-то делало. Для начала добавьте кнопку, которая будет запускать метод LoadTravelers из скриптового компонента TravelerManager. Почему объекты командированных не загружаются просто по умолчанию? В данном случае вам требуется предоставить некую ориентирную точку, чтобы пользователь понимал, что происходит в приложении. VR слишком нова для пользователей, и у них пока не сложились четкие ожидания того, что должно происходить после того, как они загрузили мир. Инструкции в UI — хорошее средство, помогающее пользователям ориентироваться в новом мире и приспособиться к нему.

Получить данные по URL — очень просто в Unity, что позволяет приложениям оперировать богатыми данными.

Создайте новый Empty Game Object в корне приложения и назовите его GUI. Этот игровой объект будет хранить UI-компоненты для приложения. Выделив игровой объект GUI, добавьте новый объект Canvas. Этот объект хранит все UI-компоненты на экране. Создав объект Canvas, перейдите в Inspector и смените Render Mode на World Space. Это отсоединит холст от камеры и позволит размещать контент в мире. Теперь добавьте новый объект Button в Canvas. Задайте текст кнопки как «Load Travelers». Измените преобразование дисплея (display transform) кнопки на x=1.87, y=.05, z=0 и width = 9, height = 3 с масштабом x=0.25, y=0.25, что уменьшит размеры кнопки и поместит ее в поле зрения пользователя.

Выберите объект кнопки в окне Hierarchy, а затем щелкните знак плюса в списке On Click (в окне Inspector), чтобы добавить новые событие щелчка. У кнопок может быть столько событий щелчков, сколько нужно, что позволяет вам использовать одну кнопку для инициации множества других событий. Это удобно для запуска какой-то операции и последующего отключения или скрытия кнопки. Каждое событие щелчка требует определения игрового объекта и доступной для него функции. После добавления события щелчка перетащите игровой объект TravelerManager в поле Object для события щелчка. После настройки TravelerManager в списке Function будет показан метод LoadTravelers. Если в этом списке нет LoadTravelers, вы должны скомпилировать свой проект кода в Visual Studio и попытаться снова выбрать LoadTravelers. Вновь нажмите на знак плюса в списке событий щелчка, чтобы добавить другое событие. Перетащите объект кнопки из окна Hierarchy в поле Object нового события щелчка, выберите GameObject.isActive и оставьте флажок сброшенным. В этом случае при щелчке данной кнопки Unity сначала выполнит LoadTravelers, а затем присвоит значению isActive кнопки false для ее скрытия. С точки зрения пользователя, эта кнопка выдает начальные инструкции. Как только потребность в них отпадает, вы удаляете их.

Получение данных от сервиса

Современные приложения получают свои данные из множества разных мест. В этом приложении данные по командированным предоставляются API от travel.contoso.com. Получить данные по URL — очень просто в Unity, что позволяет приложениям оперировать богатыми данными. В конец скрипта TravelerManager добавьте новый класс с именем Traveler. Это будет ваш объект данных, принятых от сервиса:

public class Traveler{
  public int Id { get; set; }
  public string Name { get; set; }
  public string Title { get; set; }
  public string DepartureCity { get; set; }
  public string ArrivalCity { get; set; }
}

Теперь вам нужно получить данные, чтобы заполнить объект этого класса. Создайте новый метод под LoadTravelers и назовите его GetTravelers. Этот метод будет подключаться к конечной точке API, скачивать JSON-объект, предоставляемый API, а затем отправлять результаты другому методу для отображения на экране:

private IEnumerator GetTravelers(){
  var www = new WWW("https://travel.contoso.com/api/Traveler");
  yield return www;
  if (www.isDone)
  DisplayTravelers(www.text);
}

Теперь замените комментарий «Здесь добавьте GetTravelers» в LoadTravelers следующей строкой кода:

StartCoroutine(GetTravelers());

LoadTravelers будет вызывать GetTravelers, который получает данные от API. Данные от API посылаются методу DisplayTravelers для дальнейшей обработки. Чтобы запустить этот процесс, используйте StartCoroutine, который выполняет GetTravelers без блокировки потока и замораживания приложения до окончания выполнения. Создание плавного потока управления в коде — один из шагов, которые вы должны сделать для того, чтобы в приложении не выпадали кадры. Сопрограммы (coroutines) запускают и приостанавливают метод с каждым кадром, позволяя этому методу выполняться на протяжении множества кадров без блокировки приложения. StartCoroutine очень полезен для методов, которые могут длительно выполняться, например ожидая возврата данных от веб-сервиса. Сопрограммы также можно использовать для методов в больших количествах объектов. Так, если бы сервис с данными о командированных вернул информацию о сотнях сотрудниках в поездках, сопрограммы были бы удобны для вызова операций над всеми объектами таких сотрудников, возможно, для эмуляции внешнего вида путешественников. Подробнее о сопрограммах см. в документации Unity по ссылке bit.ly/2ahxSBu.

В GetTravelers объект WWW из Unity применяется для подключения к предоставленному URL. При использовании ключевого слова yield объект WWW возвращается, пока не завершится проверкой свойства isDone. По окончании загрузки по URL свойство isDone будет равно true, после чего текст, возвращенный API-вызовом, передается методу DisplayTravelers. Объект WWW можно использовать для асинхронной отправки и получения данных, чтобы в будущем у приложения была функциональность для добавления объектов командированных сотрудников. Полную документацию на объект WWW см. на сайте Unity по ссылке bit.ly/2alFoML.

Затем вы принимаете результат от API и преобразуете его в нечто, что пользователь сможет увидеть в приложении. Прежде чем показывать командированных в разных местах на карте, вы должны создать эти места в мире. Вернитесь в дизайнер Unity и создайте пустой игровой объект в корне дерева Hierarchy. Назовите этот пустой игровой объект Location Container. Установите позицию Location Container как X=0, Y=0, Z=0 (в центре мира). При выделенном Location Container создайте несколько пустых дочерних игровых объектов, разбросанных по нижней стороне ранее созданного куба. Эти пустые игровые объекты представляют аэропорты, а нижняя сторона куба подобна карте. В методе DisplayTravelers вы ищете город назначения для командированного и соотносите его с одним из этих пустых игровых объектов. Переименуйте пустые игровые объекты в названия каких-либо аэропортов. Я использовал BWI, MSP и SFO. Чтобы закончить с аэропортами, создайте тег для Game Objects с именем Airport — тогда вы сможете находить все аэропорты по значению одного тега. Для создания тега перейдите в левую верхнюю часть окна Inspector для объекта, щелкните раскрывающийся список и выберите New Tag. Вы можете создать новый тег и в варианте, когда выделены все пустые игровые объекты, что позволит разом задать для всех этих объектов один тег.

После метода getTravelers добавьте код, показанный на рис. 2.

Рис. 2. Преобразование JSON-объектов Traveler в игровые объекты Unity

private void DisplayTravelers(string json){
  var jsonTravelers = new JSONObject(json);
  foreach (var jsonTraveler in jsonTravelers.list){
    // Преобразуем json-объект в объект traveler
    Traveler traveler = ConvertJSON(jsonTraveler);
    GameObject travelerRep =
      Instantiate(TravelerTemplate,
      new Vector3(0f, 0.05f, 0f),
      TravelerTemplate.transform.rotation) as GameObject;
    travelerRep.name = traveler.Name;
    GameObject[] airports = GameObject.FindGameObjectsWithTag("Airport");
    foreach (var airport in airports) {
      if (airport.name == traveler.ArrivalCity) {
        travelerRep.transform.position =
          new Vector3(airport.transform.position.x, 0.05f,
          airport.transform.position.z);
    break;
  }
  }
  TravelerList.Add(traveler);
  }
}

Используя бесплатный скрипт JSONObject из Unity Asset Store (bit.ly/2akN9p9), вы можете преобразовать JSON-текст в объект C#-кода Traveler. Затем с помощью метода Instantiate вы создаете копию TravelerTemplate, присваиваете ей координаты по умолчанию и задаете игровому объекту имя командированного. Это имя понадобится в приложении в дальнейшем, поскольку оно будет использоваться для идентификации командированного в списке. Далее вы находите все игровые объекты, помеченные тегом Airport. Это дает вам набор объектов Airport, которые вы можете перебирать в цикле и находить подходящий объект по имени, чтобы поместить командированного в соответствующий аэропорт. Используя ранее подготовленные теги, вы легко находите нужные игровые объекты Airport. В конце метода добавьте объект traveler в TravelerList, чтобы вы могли потом ссылаться на данные об этом командированном. Итак, у вас есть основной список командированных, отображаемых в вашем мире. Теперь самое время разрешить пользователям выбирать командированного, чтобы вы могли показывать подробную информацию об этом сотруднике.

Выбор объекта командированного сотрудника

Если поразмыслить о мире, созданном на данный момент, то хороший способ вывода представления с подробной информацией (details view) — облачко с текстом (speech bubble) от аватара, создающее впечатление, будто командированный сообщает, куда он направляется. Это интересный вариант отображения подробной информации, в то же время обеспечивающий UI в непосредственной близости к выбранному сотруднику.

В VR выбор командированного сотрудника осуществляется взглядом на объект. Чтобы узнать, смотрит ли пользователь на какого-либо командированного сотрудника, вы должны использовать объект Raycast. Raycast — это невидимый луч, исходящий из камеры и позволяющий системе обнаруживать, когда объект с компонентом-коллайдером (collider component) в нем пересекается с лучом. Используя Raycast, ваш код может распознавать, когда пользователь смотрит на какой-нибудь объект, который должен на это отреагировать. Ранее вы импортировали скрипт VREyeRaycast, реализующий всю логику, которая необходимы для создания Raycast в камере. Добавьте скрипт VREyeRaycast в Main Camera. Вам также понадобится добавить скрипт Reticle в эту камеру, чтобы предоставить UI-компонент, показывающий, куда смотрит пользователь.

Используя Raycast, ваш код может распознавать, когда пользователь смотрит на какой-нибудь объект, который должен на это отреагировать.

Поместив Raycast в камеру, нужно обновить TravelerTemplate, чтобы знать, как реагировать на пересечение с Raycast. Для этого добавьте скрипт VRInteractiveItem в TravelerTemplate. Этот скрипт обеспечивает быструю настройку для работы с событиям, генерируемым, когда Raycast пересекается с объектом, покидает объект или даже захватывает событие щелчка.

Далее вы создаете скрипт с именем TravelerInteraction, который реализует события в скрипте VRInteractiveItem. Для этого приложения вы будете использовать события OnOver, OnOut и OnClick. Когда пользователь смотрит на объект Traveler, тот будет менять свой внешний вид, а когда пользователь отводит от него взгляд, внешний вид этого объекта будет восстанавливаться. Если пользователь смотрит на объект Traveler и делает щелчок, появляется диалог, показывающий имя командированного сотрудника, должность и место назначения. Вот скрипт TravelerInteraction:

public class TravelerInteraction : MonoBehavior{
  public VRInteractiveItem InteractiveItem;
  public Shader Highlight;
  public TravelerManager TravelerManager;
  public Canvas Dialog;
  private Shader standardShader;
  private bool isDialogShowing;
}

VRInteractiveItem должен взаимодействовать с VREyeRaycast и подключать методы к событиям VRInteractiveItem.

VRInteractiveItem должен взаимодействовать с VREyeRaycast и подключать методы к событиям VRInteractiveItem. Highlight — это пользовательский шейдер, который будет применяться для выделения объекта Traveler, когда на него смотрит пользователь. Я не стану описывать здесь пользовательские шейдеры, так как это весьма сложная тема. Чтобы узнать больше, см. документацию на сайте Unity по ссылке bit.ly/2agfb7q. TravelerManager — это ссылка на скрипт TravelerManager, который служит для поиска подробной информации о выбранном Traveler из набора TravelerManager.TravelerList. Последнее открытое свойство здесь — игровой объект Canvas; с его помощью при щелчке отображаются детальные сведения о командированном сотруднике. Закрытые переменные включают ссылку на стандартный шейдер для возврата внешнего вида объекта Traveler после отвода взгляда от него и булево значение, определяющее, показывается ли диалог.

Для начала добавьте метод Enabled в класс TravelerInteraction, чтобы подключить события класса VRInteractiveItem к методам в классе TravelerInteraction:

private void OnEnable(){
  InteractiveItem.OnOver += HandleOver;
  InteractiveItem.OnOut += HandleOut;
  InteractiveItem.OnClick += HandleClick;
}

HandleOver использует компонент Renderer для замены стандартного шейдера на пользовательский. Свойство Highlight позволяет дизайнеру задать Shader в Unity Editor для выделения объекта Traveler:

private void HandleOver(){
  var renderer =
    gameObject.GetComponentInChildren<Renderer>();
  renderer.material.shader = Highlight;
}
private void HandleOut(){
  var renderer =
    gameObject.GetComponentInChildren<Renderer>();
  renderer.material.shader = standardShader;
}

Когда пользователь смотрит на Traveler, применяется шейдер Highlight, чтобы выделить этот объект Traveler среди других. А когда пользователь отводит взгляд, срабатывает метод HandleOut и возвращает шейдеру объекта Traveler значение standardShader.

Теперь вам нужно показывать информацию о командированном при щелчке его объекта Traveler. Для этого сначала добавьте System.Linq в выражения using для класса TravelerInteractive, чтобы вы могли находить командированного сотрудника по имени игрового объекта. Кроме того, вам нужно добавить пространство имен UnityEngine.UI для работы с компонентом Text в диалоге (рис. 3).

Рис. 3. Вывод диалога при щелчке объекта

private void HandleClick(){
  if (!isDialogShowing){
    var textComponent =
      Dialog.gameObject
      .GetComponentInChildren<Text>();
    var travelerInfo =
      TravelerManager.TravelerList.SingleOrDefault(x => x.Name ==
      this.gameObject.name);
    textComponent.text = String.Format(“{0}\n{1}”,
      travelerInfo.Name, travelerInfo.DestCity);
    Dialog.gameObject.SetActive(true);
    isDialogShowing = true;}
  else{
    Dialog.gameObject.SetActive(false);
    isDialogShowing = false;}
}

В этом блоке кода вы сначала определяете, показывается ли диалог. Если да, вы отключаете диалог, чтобы скрыть его. Нет — получаете объект текста и присваиваете его значение имени и месту назначения Traveler. Когда вы изначально создавали объект Traveler, вы указали имя командированного сотрудника в качестве имени игрового объекта, представляющего этого сотрудника. С помощью LINQ вы можете вести поиск в списке командированных по этому имени, чтобы получить подробную информацию о данном сотруднике. Простоты ради было задействовано имя командированного, но в более крупных и сложных системах гораздо лучше применять уникальный идентификатор каждого командированного. По поводу LINQ помните, что производительность — это главное в мобильном VR-приложении. LINQ способна делать очень многое, но ценой немалых издержек, так что будьте внимательны при использовании LINQ и применяйте ее только там, где без нее не обойтись, а не просто для удобства. Здесь LINQ используется для краткости кода.

Заключение

В этой статье я рассмотрел основы создания UI, получения данных от сервиса и взаимодействие с объектами в мире виртуальной реальности. Используя полученные знания, вы можете расширить мир ContosoTravel и превратить его в богатое приложение с поддержкой поиска, большего числа уровней для представления регионов мира и анимаций для оживления аватаров (например, для имитации того, что они куда-то двигаются). Исследуйте эти фундаментальные основы, что создавать VR-миры сегодня и приготовиться к будущей смешанной реальности.


Тим Кульп (Tim Kulp) — ведущий инженер b.well в Балтиморе (штат Мериленд). Занимается разработкой для Web, мобильных устройств и UWP, а также является автором, художником и папой. Вы найдете его в Twitter (@seccode) или через LinkedIn (linkedin.com/in/timkulp).

Выражаю благодарность за рецензирование статьи эксперту Microsoft Адаму Тьюлиперу (Adam Tuliper).