HoloLens (1.ª generación) y Azure 309: Application Insights
Nota:
Los tutoriales de Mixed Reality Academy se diseñaron con HoloLens (1.ª generación) y Mixed Reality cascos inmersivos en mente. Por lo tanto, creemos que es importante dejar estos tutoriales en su lugar para los desarrolladores que siguen buscando orientación en 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 seguir trabajando 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 Aplicación de Azure Insights para recopilar análisis con respecto al comportamiento del usuario.
Application Insights es un servicio de Microsoft que permite a los desarrolladores recopilar análisis de sus aplicaciones y administrarlo desde un portal fácil de usar. El análisis puede ser cualquier cosa, desde el rendimiento hasta la información personalizada que quiera recopilar. Para obtener más información, visite la página Application Insights.
Una vez completado este curso, tendrá una aplicación de casco envolvente de realidad mixta, que podrá hacer lo siguiente:
- Permitir que el usuario mire y se mueva alrededor de una escena.
- Desencadene el envío de análisis al servicio Application Insights mediante Mirada y proximidad a objetos en la escena.
- La aplicación también llamará al servicio, capturando información sobre qué objeto se ha acercado más al 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 podría estar compilando.
Compatibilidad con dispositivos
Curso | HoloLens | Cascos envolventes |
---|---|---|
MR y Azure 309: Application Insights | ✔️ | ✔️ |
Nota:
Aunque este curso se centra principalmente en Windows Mixed Reality cascos envolventes (VR), también puedes aplicar lo que aprendes en este curso a Microsoft HoloLens. A medida que siga el curso, verá notas sobre los cambios que pueda 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 la escritura (julio de 2018). Usted es libre de usar el software más reciente, como se muestra en el artículo instalar las herramientas , aunque no se debe suponer que la información de este curso coincidirá perfectamente con lo que encontrará en el software más reciente de 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
- Auriculares envolventes Windows Mixed Reality (VR) o Microsoft HoloLens con el modo desarrollador habilitado
- Un conjunto de auriculares con un micrófono integrado (si el auricular no tiene un micrófono y altavoces integrados)
- Acceso a Internet para la configuración de Azure y la recuperación de datos de Application Insights
Antes de empezar
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, así que sea 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 el 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 aula o laboratorio, pida ayuda a su instructor o a uno de los responsables para configurar la nueva cuenta.
Una vez que haya iniciado sesión, haga clic en Nuevo en la esquina superior izquierda, busque Application Insights y haga clic en Entrar.
Nota:
Es posible que la palabra Nuevo se haya reemplazado por Crear un recurso en portales más recientes.
La nueva página de la derecha proporcionará una descripción del servicio Aplicación de Azure Insights. 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, 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 del 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, lo que puede tardar un minuto.
Una vez creada la instancia del servicio, aparecerá una notificación en el portal.
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 llevará a la nueva instancia del servicio Application Insights .
Nota:
Mantenga esta página web abierta y de fácil acceso, volverá aquí con frecuencia 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 por la lista de funciones de servicio y seleccionar Propiedades; la pestaña mostrada mostrará la clave de servicio.
A continuación, propiedades, encontrará acceso de API, en el que debe hacer clic. El panel de la derecha proporcionará el identificador de aplicación de la aplicación.
Con el panel Id. de aplicación todavía abierto, haga clic en Crear clave de API, que abrirá el panel Crear clave de API .
En el panel Crear clave de API ahora abierto, escriba una descripción y marque las tres casillas.
Haga clic en Generar clave. La clave de API se creará y mostrará.
Advertencia
Esta es la única vez que se mostrará la clave de servicio , por lo 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 tendrá que 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 es mejor estar más cerca de los directorios raíz). A continuación, haga clic en Crear proyecto.
Con Unity abierto, vale la pena comprobar que el script predeterminado Editor está establecido en Visual Studio. Vaya a Editar > preferencias y, a continuación, en la nueva ventana, vaya a Herramientas externas. Cambie Editor de script externo 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 el Microsoft HoloLens, establezca Dispositivo de destino en HoloLens.
Tipo de compilación se establece en D3D
El SDK está establecido en Latest installed (Instalación más reciente)
Compilación y ejecución se establecen en Máquina local
Guarde la escena y agréguela a la compilación.
Para ello, seleccione Agregar escenas abiertas. Aparecerá una ventana guardar.
Cree una nueva carpeta para esta y cualquier escena futura y, a continuación, haga clic en el botón Nueva carpeta para crear una nueva carpeta y asígnele el nombre Escenas.
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 Otras configuraciones :
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, compruebe lo siguiente:
InternetClient
Más abajo en el panel, en Configuración de XR (que se encuentra debajo de Configuración de publicación), marque Realidad virtual compatible y asegúrese de que se agrega el SDK de Windows Mixed Reality.
De nuevo en Configuración de compilación, Proyectos de C# de Unity ya no está atenuado; active la casilla situada junto a esta.
Cierre la ventana Configuración de compilación.
Guarde la escena y el proyecto (ARCHIVO>GUARDAR ESCENA /ARCHIVO>GUARDAR PROYECTO).
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, descargue este paquete Azure-MR-309.unity, impórtelo en el proyecto como un paquete personalizado. Esto también contendrá los archivos DLL del capítulo siguiente. 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 de 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 (del 4 al 7 de 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 '.unitypackage', que contiene los complementos. A continuación, haga lo siguiente:
Agregue the.unitypackage** a Unity mediante la opción de menú Paquete > personalizado de importación de recursos>.
En el cuadro Import Unity Package (Importar paquete de Unity ) que aparece, asegúrese de que todo en (e including) Plugins (Incluir) complementos esté seleccionado.
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é desactivado y, a continuación, haga clic en Aplicar. Esto es solo para confirmar que los archivos están configurados correctamente.
Nota:
Al marcar los complementos de este modo, 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, a continuación, en el inspector, asegúrese de que Cualquier plataforma esté desactivada y, a continuación, asegúrese de que soloWSAPlayer esté activado. Haga clic en Aplicar.
Ahora tendrá que seguir los pasos del 4 al 6, pero para los complementos de Newtonsoft en su lugar. Vea la captura de pantalla siguiente para ver el aspecto que debería 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 que el usuario vea y se mueva 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, luego en Objeto 3D y, a continuación, en Esfera.
Cambie el nombre de la esfera a la derecha.
Establezca la escala de transformación de la mano derecha en 0.1, 0.1, 0.1
Quite el componente Colisionador de esfera de la mano derecha haciendo clic en engranaje en el componente Colisionador de esfera y, a continuación, quitar componente.
En el panel Jerarquía, arrastre los objetos Cámara principal y Mano derecha al objeto Principal de la cámara .
Establezca la posición de transformación de la cámara principal y el objeto de la mano derecha 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, luego en Objeto 3D y, a continuación, seleccione Plano.
Establezca la posición de transformación de plano en 0, -1, 0.
Establezca la escala de transformación de 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, haga clic en Crear, seguido de Carpeta, para crear una nueva carpeta. Asígnele el nombre Materiales.
Abra la carpeta Materiales , haga clic con el botón derecho, haga clic en Crear y, después, en Material, para crear un nuevo material. Asígnele 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 imagen siguiente 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 Plano 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 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 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ónde transformación a: 10, 0, 0.
Nota:
Estos valores de posición son sugerencias. Puede establecer las posiciones de los objetos en lo que desee, aunque es más fácil para el usuario de la aplicación si las distancias de los objetos no están demasiado lejos de la cámara.
Cuando se ejecuta la aplicación, debe ser capaz de identificar los objetos dentro de la escena y, para ello, deben etiquetarse. Seleccione uno de los objetos y, en el panel Inspector , haga clic en Agregar etiqueta..., que intercambiará el Inspector por el panel Etiquetas & capas .
Haga clic en el símbolo + (más) y escriba el nombre de la etiqueta como ObjectInScene.
Advertencia
Si usa un nombre diferente para la etiqueta, deberá asegurarse de que este cambio también se realice en los scripts DataFromAnalytics, ObjectTrigger y Gaze, más adelante, para que los objetos se encuentren y detecten dentro de la escena.
Con la etiqueta creada, ahora debe aplicarla a los tres objetos. En 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 con Etiqueta y, a continuación, haga clic en la etiqueta ObjectInScene que ha creado.
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 para enviar a Aplicación de Azure Insights.
Crear nombres de evento adecuados, en función de la interacción del usuario.
Envío de eventos a la instancia del servicio Application Insights.
Para crear esta clase:
Haga clic con el botón derecho en el Panel del proyecto y luego en Crear>carpeta. Asigne a la carpeta el nombre Scripts.
Con la carpeta Scripts creada, haga doble clic en ella para abrirla. A continuación, en 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 siguientes variables:
/// <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 correctamente, mediante las claves de servicio de Azure Portal, como se menciona en el capítulo 1, paso 9 en adelante.
A continuación, agregue los métodos Start() y Awake(), a los que se llamará cuando la clase se inicialice:
/// <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 las 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 Gaze
El siguiente script que se va a crear es el script Gaze . Este script es responsable de crear un Raycast que se proyectará hacia delante desde la cámara principal, para detectar qué objeto está mirando 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 a ese objeto.
Haga doble clic en la carpeta Scripts para abrirlo.
Haga clic con el botón derecho dentro de 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 lo 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() y 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 el 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 Gaze . 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 los componentes necesarios para la colisión a la cámara principal.
- Detectar si la cámara está cerca de un objeto etiquetado como ObjectInScene.
Para crear el script:
Haga doble clic en la carpeta Scripts para abrirlo.
Haga clic con el botón derecho dentro de 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 lo 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 tendrá que crear el script DataFromAnalytics , que es responsable de:
- Captura de datos de análisis sobre qué objeto se ha acercado más a la cámara.
- Con las claves de servicio, que permiten la comunicación con la instancia del servicio Aplicación de Azure Insights.
- Ordenar los objetos de la escena, según el cual tiene el mayor recuento de eventos.
- Cambiar el color del material, del objeto más abordado, a verde.
Para crear el script:
Haga doble clic en la carpeta Scripts para abrirlo.
Haga clic con el botón derecho dentro de 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;
En el 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 de valores clave, 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 material de ese GameObject a verde (como comentarios para que tenga el recuento más alto). Esto muestra un mensaje con los resultados del 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: Creación de la clase Movement
El script Movimiento es el siguiente script que tendrá que crear. Es responsable de:
- Mover la cámara principal según la dirección hacia la que mira 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 abrirlo.
Haga clic con el botón derecho dentro de 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 lo 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() { } }
En 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 dentro del 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, deberá colocar el script 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 demás scripts donde necesiten estar.
En la carpeta Scripts del Panel de proyectos, arrastre el script 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 Right Hand desde el Panel de jerarquía hasta el destino de referencia, Controller, en el panel Inspector. Establezca la velocidad del usuario en 5, como se muestra en la imagen siguiente.
Capítulo 12: Compilación del proyecto de Unity
Ahora se ha completado todo lo necesario para la sección Unity de este proyecto, 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ígnele el nombre BUILDS.
Abra la nueva carpeta BUILDS y cree otra carpeta (con Nueva carpeta una vez más) y asígnele el nombre MR_Azure_Application_Insights.
Con la carpeta MR_Azure_Application_Insights seleccionada, haga clic en Seleccionar carpeta. El proyecto tardará un minuto más o menos en compilarse.
Después de Compilar, aparecerá Explorador de archivos que muestra la ubicación del nuevo proyecto.
Capítulo 13: Implementación de MR_Azure_Application_Insights aplicación en el equipo
Para implementar la aplicación MR_Azure_Application_Insights en la máquina local:
Abra el archivo de solución de la aplicación de MR_Azure_Application_Insights en Visual Studio.
En 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, listas para iniciarse.
Inicie la aplicación de realidad mixta.
Moverse por la escena, acercarse a los objetos y mirarlos, cuando Azure Insight Service haya recopilado suficientes datos de eventos, establecerá el objeto que más se haya acercado a verde.
Importante
Aunque el tiempo medio de espera para que el servicio recopile los eventos y métricas tarda unos 15 minutos, en algunas ocasiones puede tardar hasta una hora.
Capítulo 14: Portal del servicio Application Insights
Una vez que haya recorrido la escena y haya mirado varios objetos, puede ver los datos recopilados en el portal del servicio Application Insights .
Volver al portal del servicio Application Insights.
Seleccione Explorador de métricas.
Se abrirá en una pestaña que contiene el gráfico, que representa los eventos y métricas relacionados con la aplicación. Como se mencionó anteriormente, los datos pueden tardar algún tiempo (hasta 1 hora) en mostrarse en el gráfico.
Seleccione la barra Eventosen Total de eventos por versión de aplicación para ver un desglose detallado de los eventos con sus nombres.
Finalizó la aplicación del servicio Application Insights
Enhorabuena, ha creado una aplicación de realidad mixta que aprovecha el servicio Application Insights para supervisar la actividad del usuario dentro de la aplicación.
Ejercicios de bonificación
Ejercicio 1
Intente 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 por mirada o por resultados de 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 de tiempo en la aplicación.