HoloLens (1.ª generación) y Azure 309: Application Insights
Nota:
Los tutoriales de Mixed Reality Academy se han diseñado teniendo en cuenta HoloLens (1.ª generación) y los cascos envolventes de realidad mixta. Por lo tanto, creemos que es importante conservar estos tutoriales para los desarrolladores que sigan buscando instrucciones sobre el desarrollo para esos dispositivos. Estos tutoriales no se actualizarán con los conjuntos de herramientas o las interacciones más recientes que se usan para HoloLens 2. Se mantendrán para que sigan funcionando en los dispositivos compatibles. Habrá una nueva serie de tutoriales que se publicarán en el futuro que demostrarán cómo desarrollar para HoloLens 2. Este aviso se actualizará con un vínculo a esos tutoriales cuando se publiquen.
En este curso, aprenderá a agregar funcionalidades de Application Insights a una aplicación de realidad mixta mediante la API de información de App de Azure lication para recopilar análisis relacionados con el comportamiento del usuario.
Application Insights es un servicio de Microsoft, lo que permite a los desarrolladores recopilar análisis de sus aplicaciones y administrarlos desde un portal fácil de usar. El análisis puede ser cualquier cosa, desde el rendimiento hasta la información personalizada que le gustaría recopilar. Para obtener más información, visite la página de Application Insights.
Después de completar este curso, tendrá una aplicación de casco envolvente de realidad mixta, que podrá hacer lo siguiente:
- Permitir al usuario mirar y moverse por una escena.
- Desencadene el envío de análisis al servicio Application Insights mediante el uso de gaze y proximidad a objetos en escena.
- La aplicación también llamará al servicio, capturando información sobre qué objeto ha sido abordado más por el usuario en las últimas 24 horas. Ese objeto cambiará su color a verde.
Este curso le enseñará a obtener los resultados del servicio Application Insights en una aplicación de ejemplo basada en Unity. Dependerá de usted aplicar estos conceptos a una aplicación personalizada que pueda compilar.
Compatibilidad con dispositivos
Curso | HoloLens | Cascos envolventes |
---|---|---|
MR y Azure 309: Application Insights | ✔️ | ✔️ |
Nota:
Aunque este curso se centra principalmente en cascos envolventes de Windows Mixed Reality (VR), también puede aplicar lo que aprende en este curso a Microsoft HoloLens. A medida que siga el curso, verá notas sobre los cambios que podría necesitar para admitir HoloLens. Al usar HoloLens, es posible que observe algún eco durante la captura de voz.
Requisitos previos
Nota:
Este tutorial está diseñado para desarrolladores que tienen experiencia básica con Unity y C#. Tenga en cuenta también que los requisitos previos y las instrucciones escritas de este documento representan lo que se ha probado y comprobado en el momento de redactarlo (julio de 2018). Puede usar el software más reciente, como se muestra en el artículo de instalación de las herramientas , aunque no debe asumirse que la información de este curso coincidirá perfectamente con lo que encontrará en el software más reciente que lo que se muestra a continuación.
Se recomienda el siguiente hardware y software para este curso:
- Un equipo de desarrollo, compatible con Windows Mixed Reality para el desarrollo de cascos envolventes (VR)
- Windows 10 Fall Creators Update (o posterior) con el modo desarrollador habilitado
- El SDK de Windows 10 más reciente
- Unity 2017.4
- Visual Studio 2017
- Casco envolvente (VR) de Windows Mixed Reality o Microsoft HoloLens con el modo desarrollador habilitado
- Un conjunto de auriculares con un micrófono integrado (si el casco no tiene un micrófono integrado y altavoces)
- Acceso a Internet para la configuración de Azure y la recuperación de datos de Application Insights
Antes de comenzar
Para evitar problemas al compilar este proyecto, se recomienda encarecidamente crear el proyecto en este tutorial en una carpeta raíz o casi raíz (las rutas de acceso de carpeta largas pueden causar problemas en tiempo de compilación).
Advertencia
Tenga en cuenta que los datos que van a Application Insights tardan tiempo, por lo que debe ser paciente. Si desea comprobar si el Servicio ha recibido sus datos, consulte el capítulo 14, que le mostrará cómo navegar por el portal.
Capítulo 1: Azure Portal
Para usar Application Insights, deberá crear y configurar un servicio de Application Insights en Azure Portal.
Inicie sesión en Azure Portal.
Nota:
Si aún no tiene una cuenta de Azure, deberá crear una. Si sigue este tutorial en una situación de clase o laboratorio, pida a su instructor o a uno de los proctores que le ayuden a configurar la nueva cuenta.
Una vez que haya iniciado sesión, haga clic en Nuevo en la esquina superior izquierda y busque Application Insights y haga clic en Entrar.
Nota:
Es posible que la palabra New se haya reemplazado por Create a resource (Crear un recurso) en portales más recientes.
La nueva página a la derecha proporcionará una descripción del servicio insights de App de Azure lication. En la parte inferior izquierda de esta página, seleccione el botón Crear para crear una asociación con este servicio.
Una vez que haya hecho clic en Crear:
Inserte el nombre deseado para esta instancia de servicio.
Como Tipo de aplicación, seleccione General.
Seleccione una suscripción adecuada.
Elija un grupo de recursos o cree uno nuevo. Un grupo de recursos proporciona una manera de supervisar, controlar el acceso, aprovisionar y administrar la facturación de una colección de recursos de Azure. Se recomienda mantener todos los servicios de Azure asociados a un único proyecto (por ejemplo, como estos cursos) en un grupo de recursos común).
Si desea obtener más información sobre los grupos de recursos de Azure, visite el artículo sobre el grupo de recursos.
Seleccione una ubicación.
También deberá confirmar que ha comprendido los términos y condiciones aplicados a este servicio.
Seleccione Crear.
Una vez que haya hecho clic en Crear, tendrá que esperar a que se cree el servicio, esto puede tardar un minuto.
Aparecerá una notificación en el portal una vez creada la instancia de servicio.
Seleccione las notificaciones para explorar la nueva instancia de servicio.
Haga clic en el botón Ir al recurso de la notificación para explorar la nueva instancia de servicio. Se le mostrará la nueva instancia de Application Insights Service .
Nota:
Mantenga esta página web abierta y fácil de acceder, volverá aquí a menudo para ver los datos recopilados.
Importante
Para implementar Application Insights, deberá usar tres (3) valores específicos: clave de instrumentación, identificador de aplicación y clave de API. A continuación verá cómo recuperar estos valores del servicio. Asegúrese de anotar estos valores en una página del Bloc de notas en blanco, ya que los usará pronto en el código.
Para buscar la clave de instrumentación, deberá desplazarse hacia abajo en la lista de funciones de servicio y seleccionar Propiedades, la pestaña que se muestra mostrará la clave de servicio.
Un poco debajo de Propiedades, encontrará acceso a API, que debe hacer clic. El panel a la derecha proporcionará el identificador de aplicación de la aplicación.
Con el panel Id. de aplicación abierto, haga clic en Crear clave de API, que abrirá el panel Crear clave de API.
En el panel Crear clave de API ahora, escriba una descripción y marque los tres cuadros.
Haga clic en Generar clave. La clave de API se creará y se mostrará.
Advertencia
Esta es la única vez que se mostrará la clave de servicio, así que asegúrese de realizar una copia de ella ahora.
Capítulo 2: Configuración del proyecto de Unity
A continuación se muestra una configuración típica para desarrollar con la realidad mixta y, como tal, es una buena plantilla para otros proyectos.
Abra Unity y haga clic en Nuevo.
Ahora deberá proporcionar un nombre de proyecto de Unity, insertar MR_Azure_Application_Insights. Asegúrese de que la plantilla está establecida en 3D. Establezca la ubicación en algún lugar adecuado para usted (recuerde que más cerca de los directorios raíz es mejor). A continuación, haga clic en Crear proyecto.
Con Unity abierto, vale la pena comprobar que el Editor de scripts predeterminado está establecido en Visual Studio. Vaya a Editar > preferencias y, a continuación, en la nueva ventana, vaya a Herramientas externas. Cambie el Editor de scripts externos a Visual Studio 2017. Cierre la ventana Preferencias.
A continuación, vaya a Configuración > de compilación de archivos y cambie la plataforma a Plataforma universal de Windows haciendo clic en el botón Cambiar plataforma.
Vaya a Configuración de compilación de archivos > y asegúrese de que:
El dispositivo de destino se establece en Cualquier dispositivo
Para Microsoft HoloLens, establezca Dispositivo de destino en HoloLens.
Tipo de compilación se establece en D3D
El SDK se establece en Latest installed (Versión más reciente instalada)
Build and Run (Compilar y ejecutar ) está establecido en Equipo local
Guarde la escena y agréguela a la compilación.
Para ello, seleccione Agregar escenas abiertas. Aparecerá una ventana de guardado.
Cree una nueva carpeta para esto y cualquier escena futura y, a continuación, haga clic en el botón Nueva carpeta para crear una nueva carpeta, asígnela el nombre Scenes.
Abra la carpeta Escenas recién creada y, a continuación, en el campo Nombre de archivo: texto, escriba ApplicationInsightsScene y, a continuación, haga clic en Guardar.
La configuración restante, en Configuración de compilación, debe dejarse como predeterminada por ahora.
En la ventana Configuración de compilación, seleccione Configuración del reproductor; se abrirá el panel relacionado en el espacio donde se encuentra el Inspector .
En este panel, es necesario comprobar algunos valores:
En la pestaña Otros valores :
La versión del entorno de ejecución de scripting debe ser experimental (equivalente a.NET 4.6), lo que desencadenará la necesidad de reiniciar el editor.
El back-end de scripting debe ser .NET
El nivel de compatibilidad de API debe ser .NET 4.6
En la pestaña Configuración de publicación, en Funcionalidades, active:
InternetClient
Más abajo en el panel, en Configuración de XR (que se encuentra a continuación de Configuración de publicación), marque Virtual Reality Supported (Compatible con realidad virtual), asegúrese de que se agrega el SDK de Windows Mixed Reality.
De nuevo en Configuración de compilación, los proyectos de C# de Unity ya no están atenuados; marque la casilla situada junto a esto.
Cierre la ventana Build Settings (Configuración de compilación).
Guarde la escena y el proyecto (FILE>SAVE SCENE /FILE>SAVE PROJECT).
Capítulo 3: Importación del paquete de Unity
Importante
Si desea omitir los componentes de Configuración de Unity de este curso y continuar directamente en el código, no dude en descargar este paquete azure-MR-309.unitypackage, impórtelo en el proyecto como paquete personalizado. Esto también contendrá los archivos DLL del siguiente capítulo. Después de la importación, continúe desde el capítulo 6.
Importante
Para usar Application Insights en Unity, debe importar el archivo DLL para él, junto con el archivo DLL newtonsoft. Actualmente hay un problema conocido en Unity que requiere que los complementos se vuelvan a configurar después de la importación. Estos pasos (4 - 7 en esta sección) ya no serán necesarios después de que se haya resuelto el error.
Para importar Application Insights en su propio proyecto, asegúrese de que ha descargado el archivo ".unitypackage", que contiene los complementos. A continuación, haga lo siguiente:
Agregue el archivo.unitypackage** a Unity mediante la opción de > menú Importar paquete personalizado de paquetes > de activos.
En el cuadro Importar paquete de Unity que aparece, asegúrese de que está seleccionado todo en complementos (e incluidos).
Haga clic en el botón Importar para agregar los elementos al proyecto.
Vaya a la carpeta Insights en Complementos en la vista Proyecto y seleccione solo los siguientes complementos:
- Microsoft.ApplicationInsights
Con este complemento seleccionado, asegúrese de que Cualquier plataforma está desactivada y, a continuación, asegúrese de que WSAPlayer también está desactivada y, a continuación, haga clic en Aplicar. Esto es solo para confirmar que los archivos están configurados correctamente.
Nota:
Marcar los complementos como este, los configura para que solo se usen en el Editor de Unity. Hay un conjunto diferente de archivos DLL en la carpeta WSA que se usará después de exportar el proyecto desde Unity.
A continuación, debe abrir la carpeta WSA dentro de la carpeta Insights . Verá una copia del mismo archivo que configuró. Seleccione este archivo y, después, en el inspector, asegúrese de que Cualquier plataforma está desactivada y, a continuación, asegúrese de que solo WSAPlayer esté activado. Haga clic en Aplicar.
Ahora deberá seguir los pasos 4-6, pero para los complementos newtonsoft en su lugar. Consulte la captura de pantalla siguiente para ver el aspecto que debe tener el resultado.
Capítulo 4: Configuración de la cámara y los controles de usuario
En este capítulo, configurará la cámara y los controles para permitir al usuario ver y moverse en la escena.
Haga clic con el botón derecho en un área vacía en el Panel jerarquía y, a continuación, en Crear>vacío.
Cambie el nombre del nuevo GameObject vacío a Elemento primario de la cámara.
Haga clic con el botón derecho en un área vacía en el Panel jerarquía y, a continuación, en Objeto 3D y, a continuación, en Sphere.
Cambie el nombre de La esfera a mano derecha.
Establezca la escala de transformación de la mano derecha en 0.1, 0.1, 0.1
Quite el componente De colisionador de esfera de la mano derecha haciendo clic en el engranaje del componente Colisionador de esfera y, a continuación , quite componente.
En el Panel de jerarquía, arrastre los objetos Cámara principal y Mano derecha al objeto Primario de la cámara.
Establezca la posición de transformación de la cámara principal y el objeto Right Hand en 0, 0, 0.
Capítulo 5: Configuración de los objetos en la escena de Unity
Ahora creará algunas formas básicas para la escena, con las que el usuario puede interactuar.
Haga clic con el botón derecho en un área vacía en el Panel jerarquía y, después, en El objeto 3D y, a continuación, seleccione Plano.
Establezca la posición de transformación del plano en 0, -1, 0.
Establezca la escala de transformación del plano en 5, 1, 5.
Cree un material básico para usarlo con el objeto Plane , de modo que las otras formas sean más fáciles de ver. Vaya al Panel del proyecto, haga clic con el botón derecho y, a continuación , en Crear, seguido de Carpeta, para crear una carpeta. Asígne un nombre a materiales.
Abra la carpeta Materiales y, a continuación, haga clic con el botón derecho, haga clic en Crear y, a continuación, en Material, para crear un nuevo material. Asígnelo el nombre Azul.
Con el nuevo material azul seleccionado, examine el Inspector y haga clic en la ventana rectangular junto a Albedo. Seleccione un color azul (la siguiente imagen es Color hexadecimal: #3592FFFF). Haga clic en el botón Cerrar una vez que haya elegido.
Arrastre el nuevo material desde la carpeta Materiales hasta el plano recién creado, dentro de la escena (o colóquelo en el objeto Plane dentro de la jerarquía).
Haga clic con el botón derecho en un área vacía en el Panel jerarquía y, a continuación, en el objeto 3D, cápsula.
- Con la cápsula seleccionada, cambie su posición de transformación a: -10, 1, 0.
Haga clic con el botón derecho en un área vacía en el Panel jerarquía y, a continuación, en el objeto 3D, cubo.
- Con el cubo seleccionado, cambie su posición de transformación a: 0, 0, 10.
Haga clic con el botón derecho en un área vacía en el Panel jerarquía y, a continuación, en Objeto 3D, Esfera.
- Con la esfera seleccionada, cambie su posición de transformación a: 10, 0, 0.
Nota:
Estos valores position son sugerencias. Puede establecer las posiciones de los objetos en lo que quiera, aunque es más fácil para el usuario de la aplicación si los objetos no están demasiado lejos de la cámara.
Cuando se ejecuta la aplicación, debe poder identificar los objetos dentro de la escena para lograr esto, deben etiquetarse. Seleccione uno de los objetos y, en el panel Inspector , haga clic en Agregar etiqueta..., que intercambiará el Inspector con el panel Etiquetas y capas .
Haga clic en el símbolo + (más) y escriba el nombre de etiqueta como ObjectInScene.
Advertencia
Si usa un nombre diferente para la etiqueta, deberá asegurarse de que este cambio también se realice en DataFromAnalytics, ObjectTrigger y Gaze, scripts más adelante, de modo que los objetos se encuentren y detecten, dentro de la escena.
Con la etiqueta creada, ahora debe aplicarla a los tres objetos. En la jerarquía, mantenga presionada la tecla Mayús y, a continuación, haga clic en los objetos Capsule, Cube y Sphere y, a continuación, en inspector, haga clic en el menú desplegable junto a Tag y, a continuación, haga clic en la etiqueta ObjectInScene que creó.
Capítulo 6: Creación de la clase ApplicationInsightsTracker
El primer script que debe crear es ApplicationInsightsTracker, que es responsable de:
Creación de eventos basados en interacciones del usuario que se van a enviar a App de Azure lication Insights.
Crear nombres de evento adecuados, en función de la interacción del usuario.
Envío de eventos a la instancia de Application Insights Service.
Para crear esta clase:
Haga clic con el botón derecho en el Panel del proyecto y, a continuación, en Crear>carpeta. Asigne un nombre a la carpeta Scripts.
Con la carpeta Scripts creada, haga doble clic en ella para abrirla. A continuación, dentro de esa carpeta, haga clic con el botón derecho en Crear>script de C#. Asigne al script el nombre ApplicationInsightsTracker.
Haga doble clic en el nuevo script ApplicationInsightsTracker para abrirlo con Visual Studio.
Actualice los espacios de nombres en la parte superior del script para que sean los siguientes:
using Microsoft.ApplicationInsights; using Microsoft.ApplicationInsights.DataContracts; using Microsoft.ApplicationInsights.Extensibility; using UnityEngine;
Dentro de la clase, inserte las variables siguientes:
/// <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;
Nota:
Establezca los valores instrumentationKey, applicationId y API_Key adecuadamente, mediante las claves de servicio de Azure Portal, como se mencionó en el capítulo 1, paso 9 y versiones posteriores.
A continuación, agregue los métodos Start() y Awake(), al que se llamará cuando se inicialice la clase:
/// <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; }
Agregue los métodos responsables de enviar los eventos y métricas registrados por la aplicación:
/// <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); }
Asegúrese de guardar los cambios en Visual Studio antes de volver a Unity.
Capítulo 7: Creación del script de mirada
El siguiente script que se va a crear es el script de mirada . Este script es responsable de crear un Raycast que se proyectará hacia delante desde la cámara principal, para detectar qué objeto está viendo el usuario. En este caso, Raycast tendrá que identificar si el usuario está mirando un objeto con la etiqueta ObjectInScene y, a continuación, contar cuánto tiempo mira el usuario en ese objeto.
Haga doble clic en la carpeta Scripts para abrirla.
Haga clic con el botón derecho en la carpeta Scripts y haga clic en Crear>script de C#. Asigne al script el nombre Gaze.
Haga doble clic en el script para abrirlo con Visual Studio.
Reemplace el código existente por el siguiente:
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; }
Ahora es necesario agregar código para los métodos Awake() e 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; }
Dentro de la clase Gaze , agregue el código siguiente en el método Update() para proyectar un Raycast y detectar la acierto de destino:
/// <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; } }
Agregue el método ResetFocusedObject() para enviar datos a Application Insights cuando el usuario haya examinado un objeto.
/// <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; } } }
Ya ha completado el script de mirada . Guarde los cambios en Visual Studio antes de volver a Unity.
Capítulo 8: Creación de la clase ObjectTrigger
El siguiente script que debe crear es ObjectTrigger, que es responsable de:
- Agregar componentes necesarios para colisión a la cámara principal.
- Detección de si la cámara está cerca de un objeto etiquetado como ObjectInScene.
Para crear el script:
Haga doble clic en la carpeta Scripts para abrirla.
Haga clic con el botón derecho en la carpeta Scripts y haga clic en Crear>script de C#. Asigne al script el nombre ObjectTrigger.
Haga doble clic en el script para abrirlo con Visual Studio. Reemplace el código existente por el siguiente:
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); } } }
Asegúrese de guardar los cambios en Visual Studio antes de volver a Unity.
Capítulo 9: Creación de la clase DataFromAnalytics
Ahora deberá crear el script DataFromAnalytics , que es responsable de:
- Capturar datos de análisis sobre qué objeto ha sido abordado más por la cámara.
- Con las claves de servicio, que permiten la comunicación con la instancia del servicio insights de App de Azure lication.
- Ordenar los objetos de la escena, según cuál tiene el recuento de eventos más alto.
- Cambiar el color del material, del objeto más abordado, a verde.
Para crear el script:
Haga doble clic en la carpeta Scripts para abrirla.
Haga clic con el botón derecho en la carpeta Scripts y haga clic en Crear>script de C#. Asigne al script el nombre DataFromAnalytics.
Haga doble clic en el script para abrirlo con Visual Studio.
Inserte los siguientes espacios de nombres:
using Newtonsoft.Json; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.Networking;
Dentro del script, inserte lo siguiente:
/// <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(); }
En la clase DataFromAnalytics , justo después del método Start(), agregue el siguiente método denominado FetchAnalytics(). Este método es responsable de rellenar la lista de pares clave-valor, con un GameObject y un número de recuento de eventos de marcador de posición. A continuación, inicializa la corrutina GetWebRequest(). La estructura de consulta de la llamada a Application Insights también se puede encontrar en este método, como punto de conexión de dirección URL de consulta.
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)); } }
Justo debajo del método FetchAnalytics(), agregue un método denominado GetWebRequest(), que devuelve un IEnumerator. Este método es responsable de solicitar el número de veces que se ha llamado a un evento, correspondiente a un gameObject específico, en Application Insights. Cuando se devuelven todas las consultas enviadas, se llama al método 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(); } } } }
El siguiente método es DetermineWinner(), que ordena la lista de pares GameObject e Int , según el recuento de eventos más alto. A continuación, cambia el color de material de ese GameObject a verde (como comentarios para que tenga el recuento más alto). Esto muestra un mensaje con los resultados de análisis.
/// <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); }
Agregue la estructura de clase que se usará para deserializar el objeto JSON, recibido de Application Insights. Agregue estas clases en la parte inferior del archivo de clase DataFromAnalytics , fuera de la definición de clase.
/// <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; } }
Asegúrese de guardar los cambios en Visual Studio antes de volver a Unity.
Capítulo 10: Crear la clase Movement
El script de movimiento es el siguiente script que deberá crear. Es responsable de:
- Mover la cámara principal según la dirección hacia la que está mirando la cámara.
- Agregar todos los demás scripts a objetos de escena.
Para crear el script:
Haga doble clic en la carpeta Scripts para abrirla.
Haga clic con el botón derecho en la carpeta Scripts y haga clic en Crear>script de C#. Asigne al script el nombre Movimiento.
Haga doble clic en el script para abrirlo con Visual Studio.
Reemplace el código existente por el siguiente:
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() { } }
Dentro de la clase Movement, debajo del método Update() vacío, inserte los métodos siguientes que permiten al usuario usar el controlador de mano para moverse en el espacio virtual:
/// <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); }
Por último, agregue la llamada al método Update().
// Update is called once per frame void Update() { UpdateControllerState(); }
Asegúrese de guardar los cambios en Visual Studio antes de volver a Unity.
Capítulo 11: Configuración de las referencias de scripts
En este capítulo debe colocar el script de movimiento en el elemento primario de la cámara y establecer sus destinos de referencia. A continuación, ese script controlará la colocación de los otros scripts en los que necesiten.
En la carpeta Scripts del Panel de proyectos, arrastre el script De movimiento al objeto Primario de la cámara, ubicado en el Panel de jerarquía.
Haga clic en el elemento primario de la cámara. En el Panel de jerarquía, arrastre el objeto Mano derecha desde el Panel jerarquía al destino de referencia, Controlador, en el Panel de inspectores. Establezca la velocidad del usuario en 5, como se muestra en la imagen siguiente.
Capítulo 12: Compilación del proyecto de Unity
Todo lo necesario para la sección Unity de este proyecto ya se ha completado, por lo que es el momento de compilarlo desde Unity.
Vaya a Configuración de compilación (Configuración de compilación de archivos>).
En la ventana Configuración de compilación, haga clic en Compilar.
Aparecerá una ventana Explorador de archivos, que le pedirá una ubicación para la compilación. Cree una nueva carpeta (haciendo clic en Nueva carpeta en la esquina superior izquierda) y asígnela el nombre BUILDS.
Abra la nueva carpeta BUILDS y cree otra carpeta (con Nueva carpeta una vez más) y asígnela el nombre MR_Azure_Application_Insights.
Con la carpeta MR_Azure_Application_Insights seleccionada, haga clic en Seleccionar carpeta. El proyecto tardará un minuto en compilarse.
Después de Compilar, Explorador de archivos aparecerá en la que se muestra la ubicación del nuevo proyecto.
Capítulo 13: Implementación de MR_Azure_Application_Insights aplicación en la máquina
Para implementar la aplicación de MR_Azure_Application_Insights en la máquina local:
Abra el archivo de solución de la aplicación MR_Azure_Application_Insights en Visual Studio.
En la Plataforma de soluciones, seleccione x86, Máquina local.
En Configuración de la solución, seleccione Depurar.
Vaya al menú Compilar y haga clic en Implementar solución para transferir localmente la aplicación a la máquina.
La aplicación debería aparecer ahora en la lista de aplicaciones instaladas, lista para iniciarse.
Inicie la aplicación de realidad mixta.
Desplazarse por la escena, acercarse a los objetos y mirarlos, cuando Azure Insight Service ha recopilado suficientes datos de eventos, establecerá el objeto que se ha acercado más a verde.
Importante
Aunque el tiempo medio de espera para que el servicio recopile los eventos y las métricas tarda aproximadamente 15 minutos, en algunas ocasiones puede tardar hasta 1 hora.
Capítulo 14: Portal del servicio Application Insights
Una vez que haya recorrido por la escena y mirando varios objetos, puede ver los datos recopilados en el portal de Application Insights Service .
Vuelva al portal de Application Insights Service.
Seleccione Explorador de métricas.
Se abrirá en una pestaña que contiene el gráfico, que representa los eventos y las métricas relacionados con la aplicación. Como se mencionó anteriormente, los datos pueden tardar un tiempo (hasta 1 hora) en mostrarse en el gráfico.
Seleccione la barra Eventos en total de eventos por versión de la aplicación para ver un desglose detallado de los eventos con sus nombres.
La aplicación de Application Insights Service finalizada
Enhorabuena, ha creado una aplicación de realidad mixta que aprovecha Application Insights Service para supervisar la actividad del usuario dentro de la aplicación.
Ejercicios de bonificación
Ejercicio 1
Pruebe a generar, en lugar de crear manualmente, los objetos ObjectInScene y establezca sus coordenadas en el plano dentro de los scripts. De este modo, podría preguntar a Azure cuál era el objeto más popular (ya sea a partir de los resultados de mirada o proximidad) y generar uno adicional de esos objetos.
Ejercicio 2
Ordene los resultados de Application Insights por tiempo, de modo que obtenga los datos más relevantes e implemente esa información confidencial en la aplicación.