HoloLens (1-го поколения) и Azure 309: Application Insights
Примечание.
Руководства Mixed Reality Academy были разработаны для иммерсивных гарнитур HoloLens (1-го поколения) и иммерсивных гарнитур Mixed Reality. Поэтому мы считаем, что важно оставить эти руководства для разработчиков, которые ищут рекомендации по разработке для этих устройств. Данные руководства не будут обновляться с учетом последних наборов инструментов или возможностей взаимодействия для HoloLens 2. Они будут сохранены для работы на поддерживаемых устройствах. В будущем будет появиться новая серия учебников, которые будут размещены в будущем, которые продемонстрировали, как разрабатывать для HoloLens 2. Это уведомление будет обновлено со ссылкой на эти учебники при публикации.
В этом курсе вы узнаете, как добавить возможности Application Insights в приложение смешанной реальности с помощью API приложение Azure Insights для сбора аналитики в отношении поведения пользователей.
Application Insights — это служба Майкрософт, которая позволяет разработчикам собирать аналитические данные из своих приложений и управлять ими с помощью удобного портала. Аналитика может быть любой из производительности в пользовательскую информацию, которую вы хотите собрать. Дополнительные сведения см. на странице Application Insights.
Завершив этот курс, вы получите иммерсивное приложение гарнитуры смешанной реальности, которое сможет сделать следующее:
- Разрешить пользователю смотреть и перемещаться по сцене.
- Активируйте отправку аналитики в службу Application Insights с помощью взгляда и близости к объектам в сцене.
- Приложение также вызовет службу, извлекая сведения о том, какой объект был приближен к пользователю в течение последних 24 часов. Этот объект изменит цвет на зеленый.
В этом курсе вы узнаете, как получить результаты из службы Application Insights в примере приложения на основе Unity. Это будет до вас, чтобы применить эти понятия к пользовательскому приложению, которое вы можете создать.
Поддержка устройств
Курс | HoloLens | Иммерсивные гарнитуры |
---|---|---|
MR и Azure 309: Application Insights | ✔️ | ✔️ |
Примечание.
Хотя этот курс в основном ориентирован на гарнитуры Windows Смешанная реальность иммерсивные (VR), вы также можете применить то, что вы узнаете в этом курсе к Microsoft HoloLens. При выполнении курса вы увидите заметки о любых изменениях, которые могут потребоваться для поддержки HoloLens. При использовании HoloLens вы можете заметить некоторое эхо во время записи голоса.
Предварительные требования
Примечание.
Это руководство предназначено для разработчиков, имеющих базовый опыт работы с Unity и C#. Также помните, что предварительные требования и письменные инструкции в этом документе представляют тестируемые и проверенные на момент написания статьи (июль 2018 г.). Вы можете использовать последнее программное обеспечение, как указано в статье об установке инструментов , хотя не следует предполагать, что информация в этом курсе будет идеально соответствовать тому, что вы найдете в новом программном обеспечении, чем указано ниже.
Для этого курса рекомендуется использовать следующее оборудование и программное обеспечение:
- Компьютер разработки, совместимый с Windows Смешанная реальность для разработки иммерсивной гарнитуры (VR)
- Windows 10 Fall Creators Update (или более поздней версии) с включенным режимом разработчика
- Последний пакет SDK для Windows 10
- Unity 2017.4
- Visual Studio 2017
- Гарнитура Windows Смешанная реальность иммерсивной (VR) или Microsoft HoloLens с включенным режимом разработчика
- Набор наушников со встроенным микрофоном (если гарнитура не имеет встроенного микрофона и динамиков)
- Доступ к Интернету для установки Azure и получения данных Application Insights
Перед началом работы
Чтобы избежать проблем при создании этого проекта, настоятельно рекомендуется создать проект в этом руководстве в корневой или почти корневой папке (длинные пути к папкам могут вызвать проблемы во время сборки).
Предупреждение
Помните, что данные, которые собираются в Application Insights , занимает время, поэтому быть терпеливым. Если вы хотите проверить, получила ли служба данные, ознакомьтесь с главой 14, в которой показано, как перейти на портал.
Глава 1. Портал Azure
Чтобы использовать Application Insights, необходимо создать и настроить службу Application Insights в портал Azure.
Войдите на портал Azure.
Примечание.
Если у вас еще нет учетной записи Azure, необходимо создать ее. Если вы используете это руководство в классе или лаборатории, попросите преподавателя или одного из прокторов, чтобы помочь настроить новую учетную запись.
После входа нажмите кнопку "Создать " в левом верхнем углу и найдите Application Insights и нажмите клавишу ВВОД.
Примечание.
Возможно, слово New было заменено на создание ресурса на более новых порталах.
Новая страница справа предоставит описание службы приложение Azure Insights. В нижней левой части этой страницы нажмите кнопку "Создать ", чтобы создать связь с этой службой.
После нажатия кнопки "Создать":
Вставьте требуемое имя для этого экземпляра службы.
В качестве типа приложения выберите "Общие".
Выберите соответствующую подписку.
Выберите группу ресурсов или создайте новую. Группа ресурсов предоставляет способ мониторинга, контроля доступа, подготовки и управления выставлением счетов для коллекции ресурсов Azure. Рекомендуется сохранить все службы Azure, связанные с одним проектом (например, такими, как эти курсы) в общей группе ресурсов.
Если вы хотите узнать больше о группах ресурсов Azure, посетите статью группы ресурсов.
Выберите расположение.
Кроме того, необходимо подтвердить, что вы поняли условия, примененные к этой службе.
Нажмите кнопку создания.
После нажатия кнопки "Создать" вам придется ждать создания службы, это может занять минуту.
Уведомление появится на портале после создания экземпляра службы.
Выберите уведомления для изучения нового экземпляра службы.
Нажмите кнопку "Перейти к ресурсу " в уведомлении, чтобы изучить новый экземпляр службы. Вы перейдете в новый экземпляр службы Application Insights.
Примечание.
Оставить эту веб-страницу открытой и легкой для доступа, вы часто вернеесь сюда, чтобы увидеть собранные данные.
Внимание
Для реализации Application Insights необходимо использовать три (3) конкретных значения: ключ инструментирования, идентификатор приложения и ключ API. Ниже вы узнаете, как получить эти значения из службы. Не забудьте заметить эти значения на пустой странице Блокнота , так как они будут использоваться в ближайшее время в коде.
Чтобы найти ключ инструментирования, необходимо прокрутить список функций службы и выбрать пункт "Свойства", на вкладке будет отображаться ключ службы.
Немного ниже свойств вы найдете доступ к API, который необходимо щелкнуть. На панели справа будет указан идентификатор приложения.
При открытии панели идентификатора приложения нажмите кнопку "Создать ключ API", который откроет панель ключей API "Создать API".
В открываемой панели ключей API введите описание и укажите три поля.
Нажмите кнопку "Создать ключ". Ключ API будет создан и отображен.
Предупреждение
Это единственный раз, когда будет отображаться ключ службы, поэтому убедитесь, что вы скопируете его.
Глава 2. Настройка проекта Unity
Ниже приведена типичная настройка для разработки с смешанной реальностью, и таким образом является хорошим шаблоном для других проектов.
Откройте Unity и нажмите кнопку "Создать".
Теперь необходимо указать имя проекта Unity, вставить MR_Azure_Application_Insights. Убедитесь, что шаблон имеет значение 3D. Задайте расположение в нужном месте (помните, что ближе к корневым каталогам лучше). Затем нажмите кнопку "Создать проект".
При открытии Unity стоит проверить, установлен ли редактор скриптов по умолчанию в Visual Studio. Перейдите к разделу "Изменить > параметры", а затем в новом окне перейдите к внешним средствам. Измените внешний редактор скриптов на Visual Studio 2017. Закройте окно параметров.
Затем перейдите к параметрам сборки файлов > и переключите платформу на универсальная платформа Windows, нажав кнопку "Переключить платформу".
Перейдите к параметрам сборки файлов > и убедитесь, что:
Целевое устройство имеет значение Any device
Для Microsoft HoloLens задайте для целевого устройства значение HoloLens.
Тип сборки имеет значение D3D
Для пакета SDK установлено значение "Последняя версия"
Для сборки и запуска задано значение Local Machine
Сохраните сцену и добавьте ее в сборку.
Для этого выберите "Добавить открытые сцены". Откроется окно сохранения.
Создайте новую папку для этого и любую будущую сцену, а затем нажмите кнопку "Создать папку", чтобы создать новую папку, назовите ее Сцены.
Откройте только что созданную папку "Сцены" , а затем в текстовом поле "Имя файла" , введите ApplicationInsightsScene и нажмите кнопку "Сохранить".
Остальные параметры в параметрах сборки должны оставаться по умолчанию.
В окне "Параметры сборки" выберите "Параметры проигрывателя", откроется связанная панель в пространстве, где находится инспектор.
На этой панели необходимо проверить несколько параметров:
На вкладке "Другие параметры" :
Версия среды выполнения сценариев должна быть экспериментальной (эквивалентной .NET 4.6), которая активирует необходимость перезапуска редактора.
Серверная часть скриптов должна быть .NET
Уровень совместимости API должен быть .NET 4.6
На вкладке "Параметры публикации" в разделе "Возможности" проверьте:
InternetClient;
Далее вниз по панели в параметрах XR (приведенных ниже параметров публикации), установите флажок "Поддержка виртуальной реальности", убедитесь, что пакет SDK для Windows Смешанная реальность добавлен.
Вернувшись в параметры сборки, проекты C# Unity больше не будут серыми. Установите флажок рядом с этим.
Закройте окно Build Settings (Параметры сборки).
Сохраните сцену и проект (ФАЙЛ>СОХРАНИТЬ СЦЕНУ или ФАЙЛ>СОХРАНИТЬ ПРОЕКТ).
Глава 3. Импорт пакета Unity
Внимание
Если вы хотите пропустить компоненты настройки Unity этого курса и перейти прямо в код, вы можете скачать этот пакет Azure-MR-309.unitypackage, импортировать его в проект в виде пользовательского пакета. Это также будет содержать библиотеки DLL из следующей главы. После импорта перейдите из главы 6.
Внимание
Чтобы использовать Application Insights в Unity, необходимо импортировать библиотеку DLL для него вместе с библиотекой DLL Newtonsoft. В настоящее время в Unity существует известная проблема, которая требует перенастройки подключаемых модулей после импорта. Эти действия (4 – 7 в этом разделе) больше не потребуются после устранения ошибки.
Чтобы импортировать Application Insights в собственный проект, убедитесь, что вы скачали файл unitypackage, содержащий подключаемые модули. После этого выполните следующее:
Добавьте пакет unitypackage** в Unity с помощью параметра меню "Импорт > пакетов" пользовательского пакета ресурсов>.
В появившемся окне "Импорт пакета Unity" убедитесь, что выбраны все подключаемые модули (и в том числе).
Нажмите кнопку "Импорт ", чтобы добавить элементы в проект.
Перейдите в папку Insights в разделе "Подключаемые модули" в представлении проекта и выберите только следующие подключаемые модули:
- Microsoft.ApplicationInsights
Выбрав этот подключаемый модуль, убедитесь, что любая платформа снята, а затем убедитесь, что WSAPlayer также снят, а затем нажмите кнопку "Применить". Это делается, чтобы убедиться, что файлы настроены правильно.
Примечание.
Помечая такие подключаемые модули, настраивает их только в редакторе Unity. В папке WSA есть другой набор БИБЛИОТЕК DLL, который будет использоваться после экспорта проекта из Unity.
Затем необходимо открыть папку WSA в папке Insights . Вы увидите копию того же файла, который вы настроили. Выберите этот файл, а затем в инспекторе убедитесь, что любая платформа снята, а затем убедитесь, что установлен только WSAPlayer. Щелкните Применить.
Теперь вам потребуется выполнить шаги 4-6, но для подключаемых модулей Newtonsoft . См. снимок экрана ниже, чтобы узнать, как должен выглядеть результат.
Глава 4. Настройка элементов управления камерой и пользователем
В этой главе вы настроите камеру и элементы управления, чтобы разрешить пользователю видеть и перемещаться в сцене.
Щелкните правой кнопкой мыши пустую область на панели иерархии, а затем в разделе "Создать>пусто".
Переименуйте новый пустой GameObject в "Родитель камеры".
Щелкните правой кнопкой мыши пустую область на панели иерархии, а затем на трехмерном объекте, а затем в Sphere.
Переименуйте sphere вправо.
Задайте для шкалы преобразования правой руки значение 0.1, 0.1, 0.1
Удалите компонент Collider Sphere из правой руки, щелкнув шестеренку в компоненте Collider Sphere, а затем удалите компонент.
На панели иерархии перетащите объекты Main Camera и Right Hand в родительский объект камеры.
Задайте положение преобразования основной камеры и объекта Right Hand значение 0, 0, 0, 0.
Глава 5. Настройка объектов в сцене Unity
Теперь вы создадите некоторые основные фигуры для сцены, с которыми пользователь может взаимодействовать.
Щелкните правой кнопкой мыши пустую область на панели иерархии, а затем на трехмерном объекте и выберите "Плоскость".
Задайте положение преобразования плоскости 0, –1, 0.
Задайте для масштаба преобразования плоскости значение 5, 1, 5.
Создайте базовый материал для использования с объектом Plane , чтобы другие фигуры были проще видеть. Перейдите на панель проекта, щелкните правой кнопкой мыши и создайте папку, а затем папку. Назовите его Материалы.
Откройте папку "Материалы", а затем щелкните правой кнопкой мыши, нажмите кнопку "Создать", а затем "Материал", чтобы создать новый материал. Назовите его синим цветом.
Выбрав новый синий материал, просмотрите инспектор и щелкните прямоугольное окно рядом с Albedo. Выберите синий цвет (один рисунок ниже — шестнадцатеричный цвет: #3592FFFF). Нажмите кнопку закрытия после выбора.
Перетащите новый материал из папки "Материалы" в только что созданный плоскость в сцене (или удалите его на объект "Плоскость" в иерархии).
Щелкните правой кнопкой мыши пустую область на панели иерархии, а затем в трехмерном объекте капсулы.
- Выбрав капсулу, измените положение преобразования на :10, 1, 0.
Щелкните правой кнопкой мыши пустую область на панели иерархии, а затем на трехмерном объекте кубе.
- При выборе куба измените положение преобразования на: 0, 0, 10.
Щелкните правой кнопкой мыши пустую область на панели иерархии, а затем в трехмерном объекте Sphere.
- При выборе Sphere измените положение преобразования на: 10, 0, 0.
Примечание.
Эти значения позиции являются предложениями. Вы можете задать позиции объектов независимо от того, что вы хотите, хотя это проще для пользователя приложения, если расстояния объектов не слишком далеко от камеры.
Когда приложение запущено, он должен иметь возможность идентифицировать объекты в сцене, чтобы добиться этого, они должны быть помечены. Выберите один из объектов и на панели инспектора нажмите кнопку "Добавить тег...", которая переключится на панель "Теги и слои".
Щелкните символ +(плюс), а затем введите имя тега в качестве ObjectInScene.
Предупреждение
Если вы используете другое имя тега, необходимо убедиться, что это изменение также будет сделано DataFromAnalytics, ObjectTrigger и Gaze, скрипты позже, чтобы ваши объекты были найдены и обнаружены в сцене.
После создания тега теперь необходимо применить его ко всем трем объектам. В иерархии удерживайте клавишу SHIFT, а затем щелкните капсулу, куб и сферу, объекты, а затем в инспекторе щелкните раскрывающееся меню рядом с тегом, а затем щелкните созданный тег ObjectInScene.
Глава 6. Создание класса ApplicationInsightsTracker
Первый скрипт, который необходимо создать, — ApplicationInsightsTracker, который отвечает за:
Создание событий на основе взаимодействия пользователей для отправки в приложение Azure Insights.
Создание соответствующих имен событий в зависимости от взаимодействия с пользователем.
Отправка событий в экземпляр службы Application Insights.
Чтобы создать этот класс, выполните указанные ниже действия.
Щелкните правой кнопкой мыши панель проекта и создайте>папку. Назовите скрипты папок.
Создав папку "Скрипты" , дважды щелкните ее, чтобы открыть ее. Затем в этой папке щелкните правой кнопкой мыши сценарий>C#. Назовите скрипт ApplicationInsightsTracker.
Дважды щелкните новый скрипт ApplicationInsightsTracker, чтобы открыть его с помощью Visual Studio.
Обновите пространства имен в верхней части скрипта следующим образом:
using Microsoft.ApplicationInsights; using Microsoft.ApplicationInsights.DataContracts; using Microsoft.ApplicationInsights.Extensibility; using UnityEngine;
Внутри класса вставляются следующие переменные:
/// <summary> /// Allows this class to behavior like a singleton /// </summary> public static ApplicationInsightsTracker Instance; /// <summary> /// Insert your Instrumentation Key here /// </summary> internal string instrumentationKey = "Insert Instrumentation Key here"; /// <summary> /// Insert your Application Id here /// </summary> internal string applicationId = "Insert Application Id here"; /// <summary> /// Insert your API Key here /// </summary> internal string API_Key = "Insert API Key here"; /// <summary> /// Represent the Analytic Custom Event object /// </summary> private TelemetryClient telemetryClient; /// <summary> /// Represent the Analytic object able to host gaze duration /// </summary> private MetricTelemetry metric;
Примечание.
Задайте значения инструментирования, applicationId и API_Key соответствующим образом, используя ключи службы на портале Azure, как упоминалось в главе 1, шаг 9.
Затем добавьте методы Start() и Awake( ), которые будут вызываться при инициализации класса:
/// <summary> /// Sets this class instance as a singleton /// </summary> void Awake() { Instance = this; } /// <summary> /// Use this for initialization /// </summary> void Start() { // Instantiate telemetry and metric telemetryClient = new TelemetryClient(); metric = new MetricTelemetry(); // Assign the Instrumentation Key to the Event and Metric objects TelemetryConfiguration.Active.InstrumentationKey = instrumentationKey; telemetryClient.InstrumentationKey = instrumentationKey; }
Добавьте методы, ответственные за отправку событий и метрик, зарегистрированных приложением:
/// <summary> /// Submit the Event to Azure Analytics using the event trigger object /// </summary> public void RecordProximityEvent(string objectName) { telemetryClient.TrackEvent(CreateEventName(objectName)); } /// <summary> /// Uses the name of the object involved in the event to create /// and return an Event Name convention /// </summary> public string CreateEventName(string name) { string eventName = $"User near {name}"; return eventName; } /// <summary> /// Submit a Metric to Azure Analytics using the metric gazed object /// and the time count of the gaze /// </summary> public void RecordGazeMetrics(string objectName, int time) { // Output Console information about gaze. Debug.Log($"Finished gazing at {objectName}, which went for <b>{time}</b> second{(time != 1 ? "s" : "")}"); metric.Name = $"Gazed {objectName}"; metric.Value = time; telemetryClient.TrackMetric(metric); }
Не забудьте сохранить изменения в Visual Studio , прежде чем вернуться в Unity.
Глава 7. Создание скрипта взгляда
Следующий скрипт для создания — это скрипт Gaze . Этот скрипт отвечает за создание Raycast , который будет проецирован с основной камеры, чтобы определить, какой объект смотрит пользователь. В этом случае raycast потребуется определить, смотрит ли пользователь на объект с тегом ObjectInScene, а затем подсчитывает, сколько времени пользователь смотрит на этот объект.
Дважды щелкните папку "Скрипты" , чтобы открыть ее.
Щелкните правой кнопкой мыши в папке "Скрипты", нажмите кнопку "Создать>скрипт C#". Назовите скрипт Gaze.
Дважды щелкните скрипт, чтобы открыть его с помощью Visual Studio.
Замените существующий код следующим кодом:
using UnityEngine; public class Gaze : MonoBehaviour { /// <summary> /// Provides Singleton-like behavior to this class. /// </summary> public static Gaze Instance; /// <summary> /// Provides a reference to the object the user is currently looking at. /// </summary> public GameObject FocusedGameObject { get; private set; } /// <summary> /// Provides whether an object has been successfully hit by the raycast. /// </summary> public bool Hit { get; private set; } /// <summary> /// Provides a reference to compare whether the user is still looking at /// the same object (and has not looked away). /// </summary> private GameObject _oldFocusedObject = null; /// <summary> /// Max Ray Distance /// </summary> private float _gazeMaxDistance = 300; /// <summary> /// Max Ray Distance /// </summary> private float _gazeTimeCounter = 0; /// <summary> /// The cursor object will be created when the app is running, /// this will store its values. /// </summary> private GameObject _cursor; }
Теперь необходимо добавить код для методов Awake() и Start( ).
private void Awake() { // Set this class to behave similar to singleton Instance = this; _cursor = CreateCursor(); } void Start() { FocusedGameObject = null; } /// <summary> /// Create a cursor object, to provide what the user /// is looking at. /// </summary> /// <returns></returns> private GameObject CreateCursor() { GameObject newCursor = GameObject.CreatePrimitive(PrimitiveType.Sphere); // Remove the collider, so it does not block raycast. Destroy(newCursor.GetComponent<SphereCollider>()); newCursor.transform.localScale = new Vector3(0.1f, 0.1f, 0.1f); newCursor.GetComponent<MeshRenderer>().material.color = Color.HSVToRGB(0.0223f, 0.7922f, 1.000f); newCursor.SetActive(false); return newCursor; }
В классе Gaze добавьте следующий код в метод Update() для проекта Raycast и обнаружения целевого попадания:
/// <summary> /// Called every frame /// </summary> void Update() { // Set the old focused gameobject. _oldFocusedObject = FocusedGameObject; RaycastHit hitInfo; // Initialize Raycasting. Hit = Physics.Raycast(Camera.main.transform.position, Camera.main.transform.forward, out hitInfo, _gazeMaxDistance); // Check whether raycast has hit. if (Hit == true) { // Check whether the hit has a collider. if (hitInfo.collider != null) { // Set the focused object with what the user just looked at. FocusedGameObject = hitInfo.collider.gameObject; // Lerp the cursor to the hit point, which helps to stabilize the gaze. _cursor.transform.position = Vector3.Lerp(_cursor.transform.position, hitInfo.point, 0.6f); _cursor.SetActive(true); } else { // Object looked on is not valid, set focused gameobject to null. FocusedGameObject = null; _cursor.SetActive(false); } } else { // No object looked upon, set focused gameobject to null. FocusedGameObject = null; _cursor.SetActive(false); } // Check whether the previous focused object is this same object. If so, reset the focused object. if (FocusedGameObject != _oldFocusedObject) { ResetFocusedObject(); } // If they are the same, but are null, reset the counter. else if (FocusedGameObject == null && _oldFocusedObject == null) { _gazeTimeCounter = 0; } // Count whilst the user continues looking at the same object. else { _gazeTimeCounter += Time.deltaTime; } }
Добавьте метод ResetFocusedObject(), чтобы отправить данные в Application Insights, когда пользователь посмотрел на объект.
/// <summary> /// Reset the old focused object, stop the gaze timer, and send data if it /// is greater than one. /// </summary> public void ResetFocusedObject() { // Ensure the old focused object is not null. if (_oldFocusedObject != null) { // Only looking for objects with the correct tag. if (_oldFocusedObject.CompareTag("ObjectInScene")) { // Turn the timer into an int, and ensure that more than zero time has passed. int gazeAsInt = (int)_gazeTimeCounter; if (gazeAsInt > 0) { //Record the object gazed and duration of gaze for Analytics ApplicationInsightsTracker.Instance.RecordGazeMetrics(_oldFocusedObject.name, gazeAsInt); } //Reset timer _gazeTimeCounter = 0; } } }
Теперь вы выполнили скрипт Gaze . Сохраните изменения в Visual Studio перед возвращением в Unity.
Глава 8. Создание класса ObjectTrigger
Следующий скрипт, который необходимо создать, — ObjectTrigger, который отвечает за:
- Добавление компонентов, необходимых для столкновения в основную камеру.
- Обнаружение того, находится ли камера рядом с объектом, помеченным как ObjectInScene.
Чтобы создать скрипт, выполните следующие действия.
Дважды щелкните папку "Скрипты" , чтобы открыть ее.
Щелкните правой кнопкой мыши в папке "Скрипты", нажмите кнопку "Создать>скрипт C#". Назовите скрипт ObjectTrigger.
Дважды щелкните скрипт, чтобы открыть его с помощью Visual Studio. Замените существующий код следующим кодом:
using UnityEngine; public class ObjectTrigger : MonoBehaviour { private void Start() { // Add the Collider and Rigidbody components, // and set their respective settings. This allows for collision. gameObject.AddComponent<SphereCollider>().radius = 1.5f; gameObject.AddComponent<Rigidbody>().useGravity = false; } /// <summary> /// Triggered when an object with a collider enters this objects trigger collider. /// </summary> /// <param name="collision">Collided object</param> private void OnCollisionEnter(Collision collision) { CompareTriggerEvent(collision, true); } /// <summary> /// Triggered when an object with a collider exits this objects trigger collider. /// </summary> /// <param name="collision">Collided object</param> private void OnCollisionExit(Collision collision) { CompareTriggerEvent(collision, false); } /// <summary> /// Method for providing debug message, and sending event information to InsightsTracker. /// </summary> /// <param name="other">Collided object</param> /// <param name="enter">Enter = true, Exit = False</param> private void CompareTriggerEvent(Collision other, bool enter) { if (other.collider.CompareTag("ObjectInScene")) { string message = $"User is{(enter == true ? " " : " no longer ")}near <b>{other.gameObject.name}</b>"; if (enter == true) { ApplicationInsightsTracker.Instance.RecordProximityEvent(other.gameObject.name); } Debug.Log(message); } } }
Не забудьте сохранить изменения в Visual Studio , прежде чем вернуться в Unity.
Глава 9. Создание класса DataFromAnalytics
Теперь вам потребуется создать скрипт DataFromAnalytics , который отвечает за:
- Получение аналитических данных о том, какой объект был приблизился к камере чаще всего.
- Используя ключи службы, которые позволяют обмен данными с экземпляром службы приложение Azure Insights.
- Сортировка объектов в сцене, в соответствии с которой имеет наибольшее число событий.
- Изменение цвета материала, наиболее приближенного объекта, на зеленый.
Чтобы создать скрипт, выполните следующие действия.
Дважды щелкните папку "Скрипты" , чтобы открыть ее.
Щелкните правой кнопкой мыши в папке "Скрипты", нажмите кнопку "Создать>скрипт C#". Назовите скрипт DataFromAnalytics.
Дважды щелкните скрипт, чтобы открыть его с помощью Visual Studio.
Вставьте следующие пространства имен:
using Newtonsoft.Json; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.Networking;
В скрипт вставьте следующее:
/// <summary> /// Number of most recent events to be queried /// </summary> private int _quantityOfEventsQueried = 10; /// <summary> /// The timespan with which to query. Needs to be in hours. /// </summary> private int _timepspanAsHours = 24; /// <summary> /// A list of the objects in the scene /// </summary> private List<GameObject> _listOfGameObjectsInScene; /// <summary> /// Number of queries which have returned, after being sent. /// </summary> private int _queriesReturned = 0; /// <summary> /// List of GameObjects, as the Key, with their event count, as the Value. /// </summary> private List<KeyValuePair<GameObject, int>> _pairedObjectsWithEventCount = new List<KeyValuePair<GameObject, int>>(); // Use this for initialization void Start() { // Find all objects in scene which have the ObjectInScene tag (as there may be other GameObjects in the scene which you do not want). _listOfGameObjectsInScene = GameObject.FindGameObjectsWithTag("ObjectInScene").ToList(); FetchAnalytics(); }
В классе DataFromAnalytics сразу после метода Start() добавьте следующий метод FetchAnalytics(). Этот метод отвечает за заполнение списка пар значений ключей с числом событий GameObject и числом событий заполнителя. Затем он инициализирует корутин GetWebRequest(). Структура запроса вызова Application Insights также находится в этом методе в качестве конечной точки URL-адреса запроса.
private void FetchAnalytics() { // Iterate through the objects in the list for (int i = 0; i < _listOfGameObjectsInScene.Count; i++) { // The current event number is not known, so set it to zero. int eventCount = 0; // Add new pair to list, as placeholder, until eventCount is known. _pairedObjectsWithEventCount.Add(new KeyValuePair<GameObject, int>(_listOfGameObjectsInScene[i], eventCount)); // Set the renderer of the object to the default color, white _listOfGameObjectsInScene[i].GetComponent<Renderer>().material.color = Color.white; // Create the appropriate object name using Insights structure string objectName = _listOfGameObjectsInScene[i].name; // Build the queryUrl for this object. string queryUrl = Uri.EscapeUriString(string.Format( "https://api.applicationinsights.io/v1/apps/{0}/events/$all?timespan=PT{1}H&$search={2}&$select=customMetric/name&$top={3}&$count=true", ApplicationInsightsTracker.Instance.applicationId, _timepspanAsHours, "Gazed " + objectName, _quantityOfEventsQueried)); // Send this object away within the WebRequest Coroutine, to determine it is event count. StartCoroutine("GetWebRequest", new KeyValuePair<string, int>(queryUrl, i)); } }
Прямо под методом FetchAnalytics() добавьте метод GetWebRequest(),который возвращает IEnumerator. Этот метод отвечает за запрос количества событий, соответствующих определенному gameObject, вызывается в Application Insights. При возврате всех отправленных запросов вызывается метод DetermineWinner( ).
/// <summary> /// Requests the data count for number of events, according to the /// input query URL. /// </summary> /// <param name="webQueryPair">Query URL and the list number count.</param> /// <returns></returns> private IEnumerator GetWebRequest(KeyValuePair<string, int> webQueryPair) { // Set the URL and count as their own variables (for readability). string url = webQueryPair.Key; int currentCount = webQueryPair.Value; using (UnityWebRequest unityWebRequest = UnityWebRequest.Get(url)) { DownloadHandlerBuffer handlerBuffer = new DownloadHandlerBuffer(); unityWebRequest.downloadHandler = handlerBuffer; unityWebRequest.SetRequestHeader("host", "api.applicationinsights.io"); unityWebRequest.SetRequestHeader("x-api-key", ApplicationInsightsTracker.Instance.API_Key); yield return unityWebRequest.SendWebRequest(); if (unityWebRequest.isNetworkError) { // Failure with web request. Debug.Log("<color=red>Error Sending:</color> " + unityWebRequest.error); } else { // This query has returned, so add to the current count. _queriesReturned++; // Initialize event count integer. int eventCount = 0; // Deserialize the response with the custom Analytics class. Analytics welcome = JsonConvert.DeserializeObject<Analytics>(unityWebRequest.downloadHandler.text); // Get and return the count for the Event if (int.TryParse(welcome.OdataCount, out eventCount) == false) { // Parsing failed. Can sometimes mean that the Query URL was incorrect. Debug.Log("<color=red>Failure to Parse Data Results. Check Query URL for issues.</color>"); } else { // Overwrite the current pair, with its actual values, now that the event count is known. _pairedObjectsWithEventCount[currentCount] = new KeyValuePair<GameObject, int>(_pairedObjectsWithEventCount[currentCount].Key, eventCount); } // If all queries (compared with the number which was sent away) have // returned, then run the determine winner method. if (_queriesReturned == _pairedObjectsWithEventCount.Count) { DetermineWinner(); } } } }
Следующий метод — DetermineWinner(), который сортирует список пар GameObject и Int в соответствии с самым высоким числом событий. Затем он изменяет цвет материала этого GameObject на зеленый (как обратная связь для него с наибольшим числом). Откроется сообщение с результатами аналитики.
/// <summary> /// Call to determine the keyValue pair, within the objects list, /// with the highest event count. /// </summary> private void DetermineWinner() { // Sort the values within the list of pairs. _pairedObjectsWithEventCount.Sort((x, y) => y.Value.CompareTo(x.Value)); // Change its colour to green _pairedObjectsWithEventCount.First().Key.GetComponent<Renderer>().material.color = Color.green; // Provide the winner, and other results, within the console window. string message = $"<b>Analytics Results:</b>\n " + $"<i>{_pairedObjectsWithEventCount.First().Key.name}</i> has the highest event count, " + $"with <i>{_pairedObjectsWithEventCount.First().Value.ToString()}</i>.\nFollowed by: "; for (int i = 1; i < _pairedObjectsWithEventCount.Count; i++) { message += $"{_pairedObjectsWithEventCount[i].Key.name}, " + $"with {_pairedObjectsWithEventCount[i].Value.ToString()} events.\n"; } Debug.Log(message); }
Добавьте структуру классов, которая будет использоваться для десериализации объекта JSON, полученного из Application Insights. Добавьте эти классы в самом нижнем углу файла класса DataFromAnalytics за пределами определения класса.
/// <summary> /// These classes represent the structure of the JSON response from Azure Insight /// </summary> [Serializable] public class Analytics { [JsonProperty("@odata.context")] public string OdataContext { get; set; } [JsonProperty("@odata.count")] public string OdataCount { get; set; } [JsonProperty("value")] public Value[] Value { get; set; } } [Serializable] public class Value { [JsonProperty("customMetric")] public CustomMetric CustomMetric { get; set; } } [Serializable] public class CustomMetric { [JsonProperty("name")] public string Name { get; set; } }
Не забудьте сохранить изменения в Visual Studio , прежде чем вернуться в Unity.
Глава 10. Создание класса Movement
Скрипт "Перемещение " — это следующий скрипт, который необходимо создать. Он отвечает за:
- Перемещение основной камеры в соответствии с направление камеры смотрит в сторону.
- Добавление всех остальных скриптов в объекты сцены.
Чтобы создать скрипт, выполните следующие действия.
Дважды щелкните папку "Скрипты" , чтобы открыть ее.
Щелкните правой кнопкой мыши в папке "Скрипты", нажмите кнопку "Создать>скрипт C#". Присвойте имени смещения скрипта.
Дважды щелкните скрипт, чтобы открыть его с помощью Visual Studio.
Замените существующий код следующим кодом:
using UnityEngine; using UnityEngine.XR.WSA.Input; public class Movement : MonoBehaviour { /// <summary> /// The rendered object representing the right controller. /// </summary> public GameObject Controller; /// <summary> /// The movement speed of the user. /// </summary> public float UserSpeed; /// <summary> /// Provides whether source updates have been registered. /// </summary> private bool _isAttached = false; /// <summary> /// The chosen controller hand to use. /// </summary> private InteractionSourceHandedness _handness = InteractionSourceHandedness.Right; /// <summary> /// Used to calculate and proposes movement translation. /// </summary> private Vector3 _playerMovementTranslation; private void Start() { // You are now adding components dynamically // to ensure they are existing on the correct object // Add all camera related scripts to the camera. Camera.main.gameObject.AddComponent<Gaze>(); Camera.main.gameObject.AddComponent<ObjectTrigger>(); // Add all other scripts to this object. gameObject.AddComponent<ApplicationInsightsTracker>(); gameObject.AddComponent<DataFromAnalytics>(); } // Update is called once per frame void Update() { } }
В классе Move под пустым методом Update() вставьте следующие методы, позволяющие пользователю использовать контроллер руки для перемещения в виртуальное пространство:
/// <summary> /// Used for tracking the current position and rotation of the controller. /// </summary> private void UpdateControllerState() { #if UNITY_WSA && UNITY_2017_2_OR_NEWER // Check for current connected controllers, only if WSA. string message = string.Empty; if (InteractionManager.GetCurrentReading().Length > 0) { foreach (var sourceState in InteractionManager.GetCurrentReading()) { if (sourceState.source.kind == InteractionSourceKind.Controller && sourceState.source.handedness == _handness) { // If a controller source is found, which matches the selected handness, // check whether interaction source updated events have been registered. if (_isAttached == false) { // Register events, as not yet registered. message = "<color=green>Source Found: Registering Controller Source Events</color>"; _isAttached = true; InteractionManager.InteractionSourceUpdated += InteractionManager_InteractionSourceUpdated; } // Update the position and rotation information for the controller. Vector3 newPosition; if (sourceState.sourcePose.TryGetPosition(out newPosition, InteractionSourceNode.Pointer) && ValidPosition(newPosition)) { Controller.transform.localPosition = newPosition; } Quaternion newRotation; if (sourceState.sourcePose.TryGetRotation(out newRotation, InteractionSourceNode.Pointer) && ValidRotation(newRotation)) { Controller.transform.localRotation = newRotation; } } } } else { // Controller source not detected. message = "<color=blue>Trying to detect controller source</color>"; if (_isAttached == true) { // A source was previously connected, however, has been lost. Disconnected // all registered events. _isAttached = false; InteractionManager.InteractionSourceUpdated -= InteractionManager_InteractionSourceUpdated; message = "<color=red>Source Lost: Detaching Controller Source Events</color>"; } } if(message != string.Empty) { Debug.Log(message); } #endif }
/// <summary> /// This registered event is triggered when a source state has been updated. /// </summary> /// <param name="obj"></param> private void InteractionManager_InteractionSourceUpdated(InteractionSourceUpdatedEventArgs obj) { if (obj.state.source.handedness == _handness) { if(obj.state.thumbstickPosition.magnitude > 0.2f) { float thumbstickY = obj.state.thumbstickPosition.y; // Vertical Input. if (thumbstickY > 0.3f || thumbstickY < -0.3f) { _playerMovementTranslation = Camera.main.transform.forward; _playerMovementTranslation.y = 0; transform.Translate(_playerMovementTranslation * UserSpeed * Time.deltaTime * thumbstickY, Space.World); } } } }
/// <summary> /// Check that controller position is valid. /// </summary> /// <param name="inputVector3">The Vector3 to check</param> /// <returns>The position is valid</returns> private bool ValidPosition(Vector3 inputVector3) { return !float.IsNaN(inputVector3.x) && !float.IsNaN(inputVector3.y) && !float.IsNaN(inputVector3.z) && !float.IsInfinity(inputVector3.x) && !float.IsInfinity(inputVector3.y) && !float.IsInfinity(inputVector3.z); } /// <summary> /// Check that controller rotation is valid. /// </summary> /// <param name="inputQuaternion">The Quaternion to check</param> /// <returns>The rotation is valid</returns> private bool ValidRotation(Quaternion inputQuaternion) { return !float.IsNaN(inputQuaternion.x) && !float.IsNaN(inputQuaternion.y) && !float.IsNaN(inputQuaternion.z) && !float.IsNaN(inputQuaternion.w) && !float.IsInfinity(inputQuaternion.x) && !float.IsInfinity(inputQuaternion.y) && !float.IsInfinity(inputQuaternion.z) && !float.IsInfinity(inputQuaternion.w); }
Наконец, добавьте вызов метода в метод Update().
// Update is called once per frame void Update() { UpdateControllerState(); }
Не забудьте сохранить изменения в Visual Studio , прежде чем вернуться в Unity.
Глава 11. Настройка ссылок на скрипты
В этой главе необходимо поместить скрипт перемещения на родительский элемент камеры и задать его эталонные целевые объекты. Затем этот скрипт будет обрабатывать размещение других скриптов, где они должны быть.
Из папки "Скрипты" на панели проекта перетащите скрипт перемещения в родительский объект камеры, расположенный на панели иерархии.
Щелкните родительский элемент камеры. На панели иерархии перетащите объект Right Hand из панели иерархии в эталонный целевой объект, контроллер в панели инспектора. Задайте для скорости пользователя значение 5, как показано на рисунке ниже.
Глава 12. Создание проекта Unity
Все необходимое для раздела Unity этого проекта завершено, поэтому пришло время создать его из Unity.
Перейдите к параметрам сборки (параметры сборки файлов>).
В окне "Параметры сборки" нажмите кнопку "Сборка".
Откроется окно проводник, запрашивающее расположение сборки. Создайте новую папку (нажав кнопку "Создать папку " в левом верхнем углу) и присвойте ей имя BUILDS.
Откройте новую папку BUILDS и создайте другую папку (с помощью новой папки еще раз) и присвойте ей имя MR_Azure_Application_Insights.
Выбрав папку MR_Azure_Application_Insights , щелкните " Выбрать папку". Для сборки проекта потребуется несколько минут.
После сборки проводник отобразится расположение нового проекта.
Глава 13. Развертывание приложения MR_Azure_Application_Insights на компьютере
Чтобы развернуть приложение MR_Azure_Application_Insights на локальном компьютере, выполните следующие действия.
Откройте файл решения приложения MR_Azure_Application_Insights в Visual Studio.
На платформе решения выберите x86, Локальный компьютер.
В разделе "Конфигурация решения" выберите "Отладка".
Перейдите в меню "Сборка" и нажмите кнопку "Развернуть решение ", чтобы загрузить неопубликованное приложение на компьютер.
Теперь приложение должно появиться в списке установленных приложений, готовых к запуску.
Запустите приложение смешанной реальности.
Перемещайтесь по сцене, приближаясь к объектам и просматривая их, когда Служба Аналитики Azure собрала достаточно данных о событиях, она установит объект, который приблизился к самому зеленому цвету.
Внимание
Хотя среднее время ожидания для сбора событий и метрик службой занимает около 15 минут, в некоторых случаях может потребоваться до 1 часа.
Глава 14. Портал службы Application Insights
После перемещения по сцене и просмотра нескольких объектов можно просмотреть данные, собранные на портале службы Application Insights.
Вернитесь на портал службы Application Insights.
Выберите Обозреватель метрик.
Откроется на вкладке, содержащей граф, который представляет события и метрики , связанные с приложением. Как упоминалось выше, может потребоваться некоторое время (до 1 часа) для отображения данных в графе
Выберите панель событий в общей сложности событий по версии приложения, чтобы просмотреть подробные сведения о событиях с их именами.
Готовое приложение службы Application Insights
Поздравляем, вы создали приложение смешанной реальности, которое использует службу Application Insights для мониторинга активности пользователя в приложении.
Бонусные упражнения
Упражнение 1
Попробуйте создать не вручную объекты ObjectInScene и задать их координаты на плоскости в скриптах. Таким образом, вы можете попросить Azure, какой самый популярный объект был (из взгляда или результатов близкого взаимодействия) и создать дополнительный из этих объектов.
Упражнение 2
Отсортируйте результаты Application Insights по времени, чтобы получить наиболее релевантные данные и реализовать эти конфиденциальные данные в приложении.