HoloLens (prima generazione) e Azure 309: Application Insights
Nota
Le esercitazioni di Mixed Reality Academy sono state progettate in base a HoloLens (prima generazione) e ai visori VR immersive di realtà mista. Pertanto, riteniamo importante lasciarle a disposizione degli sviluppatori a cui serve ancora materiale sussidiario per lo sviluppo di questi dispositivi. Queste esercitazioni non verranno aggiornate con i set di strumenti o le interazioni più recenti usati per HoloLens 2. Rimarranno invariate per consentire di continuare a lavorare sui dispositivi supportati. Ci sarà una nuova serie di esercitazioni che verranno pubblicate in futuro che dimostreranno come sviluppare per HoloLens 2. Questo avviso verrà aggiornato con un collegamento a tali esercitazioni quando vengono pubblicate.
In questo corso si apprenderà come aggiungere funzionalità di Application Insights a un'applicazione di realtà mista, usando l'API app Azure lication Insights per raccogliere analisi relative al comportamento dell'utente.
Application Insights è un servizio Microsoft che consente agli sviluppatori di raccogliere analisi dalle applicazioni e gestirle da un portale facile da usare. L'analisi può essere qualsiasi elemento, dalle prestazioni alle informazioni personalizzate che si vuole raccogliere. Per altre informazioni, visitare la pagina di Application Insights.
Dopo aver completato questo corso, si avrà un'applicazione visore VR immersive di realtà mista, che sarà in grado di eseguire le operazioni seguenti:
- Consentire all'utente di guardare e spostarsi intorno a una scena.
- Attivare l'invio di analisi al servizio Application Insights usando sguardo fisso e prossimità agli oggetti nella scena.
- L'app chiamerà anche il servizio, recuperando informazioni su quale oggetto è stato avvicinato di più dall'utente, nelle ultime 24 ore. L'oggetto cambierà il colore in verde.
Questo corso illustra come ottenere i risultati dal servizio Application Insights in un'applicazione di esempio basata su Unity. Sarà necessario applicare questi concetti a un'applicazione personalizzata che si sta creando.
Supporto di dispositivi
Corso | HoloLens | Visori VR immersive |
---|---|---|
MR e Azure 309: Application Insights | ✔️ | ✔️ |
Nota
Anche se questo corso è incentrato principalmente sui visori VR immersive di Windows Realtà mista, puoi anche applicare ciò che impari in questo corso a Microsoft HoloLens. Mentre segui il corso, vedrai le note su eventuali modifiche che potresti dover usare per supportare HoloLens. Quando si usa HoloLens, è possibile notare un'eco durante l'acquisizione vocale.
Prerequisiti
Nota
Questa esercitazione è progettata per gli sviluppatori che hanno esperienza di base con Unity e C#. Tenere presente anche che i prerequisiti e le istruzioni scritte all'interno di questo documento rappresentano ciò che è stato testato e verificato al momento della scrittura (luglio 2018). Sei libero di usare il software più recente, come elencato all'interno dell'articolo installare gli strumenti , anche se non si deve presumere che le informazioni in questo corso corrisponderanno perfettamente a ciò che troverai nel software più recente rispetto a quello elencato di seguito.
Per questo corso è consigliabile usare l'hardware e il software seguenti:
- Un PC di sviluppo, compatibile con Windows Realtà mista per lo sviluppo di visori VR immersive
- Windows 10 Fall Creators Update (o versione successiva) con la modalità sviluppatore abilitata
- La versione più recente di Windows 10 SDK
- Unity 2017.4
- Visual Studio 2017
- Visore VR immersive di Windows Realtà mista o Microsoft HoloLens con la modalità sviluppatore abilitata
- Un set di cuffie con un microfono incorporato (se l'auricolare non ha un microfono e altoparlanti incorporati)
- Accesso a Internet per la configurazione di Azure e il recupero dei dati di Application Insights
Prima di iniziare
Per evitare problemi durante la compilazione di questo progetto, è consigliabile creare il progetto in questa esercitazione in una cartella radice o quasi radice (i percorsi delle cartelle lunghe possono causare problemi in fase di compilazione).
Avviso
Tenere presente che i dati che passano ad Application Insights richiedono tempo, quindi essere pazienti. Per verificare se il servizio ha ricevuto i dati, consultare il capitolo 14, che mostrerà come navigare nel portale.
Capitolo 1 - Portale di Azure
Per usare Application Insights, è necessario creare e configurare un servizio Application Insights nel portale di Azure.
Accedere al portale di Azure.
Nota
Se non si ha già un account Azure, è necessario crearne uno. Se si segue questa esercitazione in una classe o in una situazione di laboratorio, chiedere all'insegnante o a uno dei prottori di assistenza per configurare il nuovo account.
Dopo aver eseguito l'accesso, fare clic su Nuovo nell'angolo superiore sinistro e cercare Application Insights e fare clic su INVIO.
Nota
La parola New potrebbe essere stata sostituita con Crea una risorsa, nei portali più recenti.
La nuova pagina a destra fornirà una descrizione del servizio app Azure lication Insights. Nella parte inferiore sinistra di questa pagina selezionare il pulsante Crea per creare un'associazione con questo servizio.
Dopo aver fatto clic su Crea:
Inserire il nome desiderato per questa istanza del servizio.
In Tipo di applicazione selezionare Generale.
Selezionare una sottoscrizione appropriata.
Scegliere un gruppo di risorse o crearne uno nuovo. Un gruppo di risorse consente di monitorare, controllare l'accesso, effettuare il provisioning e gestire la fatturazione per una raccolta di asset di Azure. È consigliabile mantenere tutti i servizi di Azure associati a un singolo progetto (ad esempio, ad esempio questi corsi) in un gruppo di risorse comune.
Per altre informazioni sui gruppi di risorse di Azure, vedere l'articolo relativo al gruppo di risorse.
Selezionare una località.
È anche necessario verificare di aver compreso le condizioni e le condizioni applicate al servizio.
Seleziona Crea.
Dopo aver fatto clic su Crea, è necessario attendere che il servizio venga creato, l'operazione potrebbe richiedere un minuto.
Una notifica verrà visualizzata nel portale dopo la creazione dell'istanza del servizio.
Selezionare le notifiche per esplorare la nuova istanza del servizio.
Fare clic sul pulsante Vai alla risorsa nella notifica per esplorare la nuova istanza del servizio. Verrà visualizzata la nuova istanza del servizio Application Insights.
Nota
Mantenere aperta e facile l'accesso a questa pagina Web, si tornerà qui spesso per visualizzare i dati raccolti.
Importante
Per implementare Application Insights, è necessario usare tre (3) valori specifici: Chiave di strumentazione, ID applicazione e Chiave API. Di seguito verrà illustrato come recuperare questi valori dal servizio. Assicurarsi di annotare questi valori in una pagina vuota del Blocco note, perché verranno usati a breve nel codice.
Per trovare la chiave di strumentazione, è necessario scorrere verso il basso l'elenco delle funzioni del servizio e selezionare Proprietà, la scheda visualizzata visualizzerà la chiave del servizio.
Di seguito sono riportate le proprietà disponibili per l'accesso all'API, che è necessario fare clic. Il pannello a destra fornirà l'ID applicazione dell'app.
Con il pannello ID applicazione ancora aperto, fare clic su Crea chiave API, che aprirà il pannello Crea chiave API.
All'interno del pannello Crea chiave API aprire il pannello Crea chiave API, digitare una descrizione e selezionare le tre caselle.
Fare clic su Genera chiave. La chiave API verrà creata e visualizzata.
Avviso
Questa è l'unica volta che verrà visualizzata la chiave del servizio, quindi assicurarsi di crearne una copia ora.
Capitolo 2 - Configurare il progetto Unity
Di seguito è riportata una configurazione tipica per lo sviluppo con la realtà mista e, di conseguenza, è un modello valido per altri progetti.
Aprire Unity e fare clic su Nuovo.
A questo punto è necessario specificare un nome di progetto Unity, inserire MR_Azure_Application_Insights. Assicurarsi che il modello sia impostato su 3D. Impostare La posizione su un punto appropriato per l'utente (tenere presente che più vicino alle directory radice è preferibile). Fare quindi clic su Crea progetto.
Con Unity aperto, vale la pena controllare che l'editor di script predefinito sia impostato su Visual Studio. Passare a Modifica > preferenze e quindi dalla nuova finestra passare a Strumenti esterni. Modificare l'editor di script esterni in Visual Studio 2017. Chiudere la finestra Preferenze .
Passare quindi a Impostazioni compilazione file > e impostare la piattaforma su piattaforma UWP (Universal Windows Platform), facendo clic sul pulsante Cambia piattaforma.
Passare a Impostazioni di compilazione file > e assicurarsi che:
Il dispositivo di destinazione è impostato su Qualsiasi dispositivo
Per Microsoft HoloLens impostare Dispositivo di destinazione su HoloLens.
Il tipo di compilazione è impostato su D3D
L'SDK è impostato su Latest installed (Versione più recente installata)
Compilazione ed esecuzione è impostata su Computer locale
Salvare la scena e aggiungerla alla compilazione.
A tale scopo, selezionare Aggiungi scene aperte. Verrà visualizzata una finestra di salvataggio.
Creare una nuova cartella per questa e qualsiasi scena futura, quindi fare clic sul pulsante Nuova cartella per creare una nuova cartella, denominarla Scene.
Aprire la cartella Scenes appena creata e quindi nel campo Nome file: testo digitare ApplicationInsightsScene, quindi fare clic su Salva.
Le impostazioni rimanenti, in Impostazioni di compilazione, devono essere lasciate come predefinite per il momento.
Nella finestra Impostazioni di compilazione selezionare Impostazioni lettore, verrà aperto il pannello correlato nello spazio in cui si trova il controllo.
In questo pannello è necessario verificare alcune impostazioni:
Nella scheda Altre impostazioni :
La versione del runtime di scripting deve essere sperimentale (equivalente a .NET 4.6), che attiverà la necessità di riavviare l'editor.
Il back-end di scripting deve essere .NET
Il livello di compatibilità api deve essere .NET 4.6
Nella scheda Impostazioni di pubblicazione, in Funzionalità selezionare:
InternetClient
Più in basso nel pannello, in Impostazioni XR (disponibile sotto Impostazioni di pubblicazione), selezionare Virtual Reality Supported (Realtà virtuale supportata), assicurarsi che Windows Realtà mista SDK sia stato aggiunto.
Tornare in Impostazioni di compilazione, i progetti C# di Unity non sono più disattivati. Selezionare la casella di controllo accanto a questa.
Chiudere la finestra Build Settings (Impostazioni di compilazione).
Salvare la scena e il progetto (FILE>SAVE SCENE/FILE>SAVE PROJECT).
Capitolo 3 - Importare il pacchetto Unity
Importante
Se si vuole ignorare i componenti di configurazione di Unity di questo corso e continuare direttamente nel codice, è possibile scaricare questo pacchetto Azure-MR-309.unitypackage, importarlo nel progetto come pacchetto personalizzato. Questo conterrà anche le DLL del capitolo successivo. Dopo l'importazione, continuare dal capitolo 6.
Importante
Per usare Application Insights in Unity, è necessario importare la DLL, insieme alla DLL Newtonsoft. Esiste attualmente un problema noto in Unity che richiede la riconfigurazione dei plug-in dopo l'importazione. Questi passaggi (da 4 a 7 in questa sezione) non saranno più necessari dopo la risoluzione del bug.
Per importare Application Insights nel proprio progetto, assicurarsi di aver scaricato il file '.unitypackage', contenente i plug-in. Procedere quindi come segue:
Aggiungere the.unitypackage** a Unity usando l'opzione di menu Asset > Importa pacchetto > personalizzato pacchetto.
Nella casella Importa pacchetto Unity visualizzata verificare che tutti gli elementi in (e inclusi) Plug-in siano selezionati.
Fare clic sul pulsante Importa per aggiungere gli elementi al progetto.
Passare alla cartella Insights in Plug-in nella visualizzazione Progetto e selezionare solo i plug-in seguenti:
- Microsoft.ApplicationInsights
Con questo plug-in selezionato, assicurarsi che Qualsiasi piattaforma sia deselezionata, quindi assicurarsi che WSAPlayer sia deselezionato, quindi fare clic su Applica. In questo modo è sufficiente verificare che i file siano configurati correttamente.
Nota
Contrassegnando i plug-in come questo, li configura in modo che vengano usati solo nell'editor di Unity. Nella cartella WSA è presente un set diverso di DLL che verranno usate dopo l'esportazione del progetto da Unity.
Successivamente, è necessario aprire la cartella WSA all'interno della cartella Insights . Verrà visualizzata una copia dello stesso file configurato. Selezionare questo file e quindi nel controllo verificare che Qualsiasi piattaforma sia deselezionata, quindi assicurarsi che sia selezionato solo WSAPlayer. Fare clic su Applica.
Ora dovrai seguire i passaggi da 4 a 6, ma per i plug-in Newtonsoft . Vedere lo screenshot seguente per l'aspetto del risultato.
Capitolo 4 - Configurare la fotocamera e i controlli utente
In questo capitolo si configureranno la fotocamera e i controlli per consentire all'utente di vedere e spostarsi nella scena.
Fare clic con il pulsante destro del mouse in un'area vuota nel pannello Gerarchia, quindi scegliere Crea>vuoto.
Rinominare il nuovo GameObject vuoto in Elemento padre fotocamera.
Fare clic con il pulsante destro del mouse in un'area vuota nel pannello gerarchia, quindi su Oggetto 3D, quindi su Sphere.
Rinominare la sfera in mano destra.
Impostare la scala di trasformazione della mano destra su 0.1, 0.1, 0.1
Rimuovere il componente Collisore Sphere dalla mano destra facendo clic sull'ingranaggio nel componente Collisore sphere e quindi su Rimuovi componente.
Nel pannello Hierarchy (Gerarchia) trascinare la fotocamera principale e gli oggetti Right Hand nell'oggetto Camera Parent .
Impostare la posizione di trasformazione sia della fotocamera principale che dell'oggetto Mano destra su 0, 0, 0.
Capitolo 5 - Configurare gli oggetti nella scena unity
A questo punto si creeranno alcune forme di base per la scena, con cui l'utente può interagire.
Fare clic con il pulsante destro del mouse in un'area vuota nel pannello Gerarchia, quindi su Oggetto 3D, quindi scegliere Piano.
Impostare La posizione della trasformazione del piano su 0, -1, 0.
Impostare Scala trasformazione piano su 5, 1, 5.
Creare un materiale di base da usare con l'oggetto Plane , in modo che le altre forme siano più facili da vedere. Passare al pannello del progetto, fare clic con il pulsante destro del mouse, quindi scegliere Crea, quindi Cartella, per creare una nuova cartella. Denominarlo Materials.
Aprire la cartella Materiali, quindi fare clic con il pulsante destro del mouse, scegliere Crea, quindi Materiale per creare un nuovo materiale. Denominarlo Blu.
Con il nuovo materiale Blu selezionato, guarda il controllo e fai clic sulla finestra rettangolare accanto ad Albedo. Selezionare un colore blu (l'immagine seguente è Colore esadecimale: #3592FFFF). Fare clic sul pulsante chiudi dopo aver scelto.
Trascinare il nuovo materiale dalla cartella Materiali, nel piano appena creato, all'interno della scena o rilasciarlo nell'oggetto Plane all'interno della gerarchia.
Fare clic con il pulsante destro del mouse in un'area vuota nel pannello Gerarchia, quindi su Oggetto 3D Capsule.
- Con l'opzione Capsule selezionata, modifica la relativa posizione di trasformazione in: -10, 1, 0.
Fare clic con il pulsante destro del mouse in un'area vuota nel pannello Gerarchia, quindi su Oggetto 3D, Cubo.
- Con l'opzione Cube selezionata, modifica la relativa posizione trasforma in: 0, 0, 10.
Fare clic con il pulsante destro del mouse in un'area vuota nel pannello Gerarchia, quindi su Oggetto 3D, Sphere.
- Con l'opzione Sphere selezionata, modifica la relativa posizione di trasformazione in: 10, 0, 0.
Nota
Questi valori Position sono suggerimenti. Sei libero di impostare le posizioni degli oggetti su qualsiasi cosa vuoi, anche se è più facile per l'utente dell'applicazione se le distanze degli oggetti non sono troppo lontane dalla fotocamera.
Quando l'applicazione è in esecuzione, deve essere in grado di identificare gli oggetti all'interno della scena, per ottenere questo risultato, devono essere contrassegnati. Selezionare uno degli oggetti e nel pannello Inspector (Controllo) fare clic su Add Tag (Aggiungi tag). In questo modo il controllo verrà scambiato con il pannello Tag e livelli .
Fare clic sul simbolo + (più) e quindi digitare il nome del tag come ObjectInScene.
Avviso
Se si usa un nome diverso per il tag, sarà necessario assicurarsi che questa modifica venga apportata anche agli script DataFromAnalytics, ObjectTrigger e Gaze, in modo che gli oggetti vengano trovati e rilevati all'interno della scena.
Dopo aver creato il tag, è ora necessario applicarlo a tutti e tre gli oggetti. Nella gerarchia tenere premuto MAIUSC, quindi fare clic sugli oggetti Capsule, Cube e Sphere, quindi nel controllo fare clic sul menu a discesa accanto a Tag, quindi fare clic sul tag ObjectInScene creato.
Capitolo 6 - Creare la classe ApplicationInsightsTracker
Il primo script da creare è ApplicationInsightsTracker, responsabile di:
Creazione di eventi in base alle interazioni dell'utente da inviare a app Azure lication Insights.
Creazione di nomi di evento appropriati, a seconda dell'interazione dell'utente.
Invio di eventi all'istanza del servizio Application Insights.
Per creare questa classe:
Fare clic con il pulsante destro del mouse nel pannello del progetto, quindi scegliere Crea>cartella. Denominare la cartella Scripts.
Dopo la creazione della cartella Scripts , fare doppio clic su di essa per aprirla. Quindi, all'interno di tale cartella, fare clic con il pulsante destro del mouse su Crea>script C#. Assegnare allo script il nome ApplicationInsightsTracker.
Fare doppio clic sul nuovo script ApplicationInsightsTracker per aprirlo con Visual Studio.
Aggiornare gli spazi dei nomi nella parte superiore dello script come indicato di seguito:
using Microsoft.ApplicationInsights; using Microsoft.ApplicationInsights.DataContracts; using Microsoft.ApplicationInsights.Extensibility; using UnityEngine;
All'interno della classe inserire le variabili seguenti:
/// <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
Impostare i valori instrumentationKey, applicationId e API_Key in modo appropriato, usando le chiavi del servizio dal portale di Azure, come indicato nel capitolo 1, passaggio 9 e versioni successive.
Aggiungere quindi i metodi Start() e Awake(), che verranno chiamati quando la classe inizializza:
/// <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; }
Aggiungere i metodi responsabili dell'invio degli eventi e delle metriche registrati dall'applicazione:
/// <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); }
Assicurarsi di salvare le modifiche in Visual Studio prima di tornare a Unity.
Capitolo 7 - Creare lo script sguardo fisso
Lo script successivo da creare è lo script gaze . Questo script è responsabile della creazione di un Raycast che verrà proiettato in avanti dalla fotocamera principale, per rilevare l'oggetto che l'utente sta esaminando. In questo caso, Raycast dovrà identificare se l'utente sta esaminando un oggetto con il tag ObjectInScene e quindi contare per quanto tempo l'utente guarda l'oggetto.
Fare doppio clic sulla cartella Scripts per aprirla.
Fare clic con il pulsante destro del mouse all'interno della cartella Scripts e scegliere Crea>script C#. Assegnare allo script il nome Gaze.
Fare doppio clic sullo script per aprirlo con Visual Studio.
Sostituire il codice esistente con quello seguente:
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; }
È ora necessario aggiungere il codice per i metodi 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; }
All'interno della classe Gaze aggiungere il codice seguente nel metodo Update() per proiettare un Raycast e rilevare l'hit di destinazione:
/// <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; } }
Aggiungere il metodo ResetFocusedObject() per inviare dati ad Application Insights quando l'utente ha esaminato un oggetto.
/// <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; } } }
A questo punto è stato completato lo script sguardo fisso . Salvare le modifiche in Visual Studio prima di tornare a Unity.
Capitolo 8 - Creare la classe ObjectTrigger
Lo script successivo che è necessario creare è ObjectTrigger, che è responsabile di:
- Aggiunta di componenti necessari per la collisione alla fotocamera principale.
- Rilevamento se la fotocamera si trova vicino a un oggetto contrassegnato come ObjectInScene.
Per creare lo script:
Fare doppio clic sulla cartella Scripts per aprirla.
Fare clic con il pulsante destro del mouse all'interno della cartella Scripts e scegliere Crea>script C#. Assegnare allo script il nome ObjectTrigger.
Fare doppio clic sullo script per aprirlo con Visual Studio. Sostituire il codice esistente con quello seguente:
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); } } }
Assicurarsi di salvare le modifiche in Visual Studio prima di tornare a Unity.
Capitolo 9 - Creare la classe DataFromAnalytics
A questo punto è necessario creare lo script DataFromAnalytics , responsabile di:
- Recupero dei dati di analisi su quale oggetto è stato avvicinato dalla fotocamera il più possibile.
- Uso delle chiavi del servizio che consentono la comunicazione con l'istanza del servizio app Azure lication Insights.
- Ordinamento degli oggetti nella scena, in base al quale ha il numero di eventi più alto.
- Modifica del colore del materiale, dell'oggetto più avvicinato, in verde.
Per creare lo script:
Fare doppio clic sulla cartella Scripts per aprirla.
Fare clic con il pulsante destro del mouse all'interno della cartella Scripts e scegliere Crea>script C#. Denominare lo script DataFromAnalytics.
Fare doppio clic sullo script per aprirlo con Visual Studio.
Inserire gli spazi dei nomi seguenti:
using Newtonsoft.Json; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.Networking;
All'interno dello script inserire quanto segue:
/// <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(); }
All'interno della classe DataFromAnalytics , subito dopo il metodo Start(), aggiungere il metodo seguente denominato FetchAnalytics(). Questo metodo è responsabile del popolamento dell'elenco di coppie chiave-valore, con un GameObject e un numero di conteggio eventi segnaposto. Inizializza quindi la coroutine GetWebRequest(). La struttura di query della chiamata ad Application Insights è disponibile anche all'interno di questo metodo, come endpoint dell'URL di query.
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)); } }
Sotto il metodo FetchAnalytics() aggiungere un metodo denominato GetWebRequest(), che restituisce un IEnumerator. Questo metodo è responsabile della richiesta del numero di volte in cui un evento, corrispondente a un GameObject specifico, è stato chiamato in Application Insights. Quando vengono restituite tutte le query inviate, viene chiamato il metodo 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(); } } } }
Il metodo successivo è DetermineWinner(), che ordina l'elenco di coppie GameObject e Int, in base al numero di eventi più alto. Cambia quindi il colore del materiale di tale GameObject in verde (come feedback per il conteggio più alto). Verrà visualizzato un messaggio con i risultati dell'analisi.
/// <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); }
Aggiungere la struttura di classe che verrà usata per deserializzare l'oggetto JSON, ricevuto da Application Insights. Aggiungere queste classi nella parte inferiore del file di classe DataFromAnalytics , all'esterno della definizione della classe.
/// <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; } }
Assicurarsi di salvare le modifiche in Visual Studio prima di tornare a Unity.
Capitolo 10 - Creare la classe Movimento
Lo script di spostamento è lo script successivo che sarà necessario creare. È responsabile delle operazioni seguenti:
- Spostando la fotocamera principale in base alla direzione verso cui la fotocamera sta guardando.
- Aggiunta di tutti gli altri script agli oggetti scena.
Per creare lo script:
Fare doppio clic sulla cartella Scripts per aprirla.
Fare clic con il pulsante destro del mouse all'interno della cartella Scripts e scegliere Crea>script C#. Assegnare allo script il nome Movimento.
Fare doppio clic sullo script per aprirlo con Visual Studio.
Sostituire il codice esistente con quello seguente:
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() { } }
All'interno della classe Movement, sotto il metodo Update() vuoto, inserire i metodi seguenti che consentono all'utente di usare il controller della mano per spostarsi nello spazio virtuale:
/// <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); }
Aggiungere infine la chiamata al metodo all'interno del metodo Update().
// Update is called once per frame void Update() { UpdateControllerState(); }
Assicurarsi di salvare le modifiche in Visual Studio prima di tornare a Unity.
Capitolo 11 - Configurazione dei riferimenti agli script
In questo capitolo è necessario posizionare lo script movimento nell'elemento padre della fotocamera e impostarne le destinazioni di riferimento. Tale script gestirà quindi l'inserimento degli altri script in cui devono trovarsi.
Dalla cartella Scripts nel pannello progetto trascinare lo script di spostamento nell'oggetto Padre fotocamera, che si trova nel pannello Gerarchia.
Fare clic sul padre della fotocamera. Nel pannello Hierarchy (Gerarchia) trascinare l'oggetto Right Hand dal Pannello gerarchia alla destinazione di riferimento Controller nel pannello di controllo. Impostare Velocità utente su 5, come illustrato nell'immagine seguente.
Capitolo 12 - Compilare il progetto Unity
Tutto ciò che serve per la sezione Unity di questo progetto è stato completato, quindi è il momento di compilarlo da Unity.
Passare a Build Settings (Impostazioni compilazione file>).
Nella finestra Build Settings (Impostazioni compilazione) fare clic su Build (Compila).
Verrà visualizzata una finestra di Esplora file che richiede una posizione per la compilazione. Creare una nuova cartella (facendo clic su Nuova cartella nell'angolo superiore sinistro) e denominarla BUILD.
Aprire la nuova cartella BUILD e creare un'altra cartella (usando una nuova cartella) e denominarla MR_Azure_Application_Insights.
Con la MR_Azure_Application_Insights cartella selezionata, fare clic su Seleziona cartella. La compilazione del progetto richiederà un minuto.
Dopo la compilazione, Esplora file verrà visualizzata la posizione del nuovo progetto.
Capitolo 13 - Distribuire MR_Azure_Application_Insights'app nel computer
Per distribuire l'app MR_Azure_Application_Insights nel computer locale:
Aprire il file della soluzione dell'app MR_Azure_Application_Insights in Visual Studio.
Nella piattaforma della soluzione selezionare x86, Computer locale.
Nella configurazione della soluzione selezionare Debug.
Passare al menu Compila e fare clic su Distribuisci soluzione per trasferire localmente l'applicazione nel computer.
L'app dovrebbe ora essere visualizzata nell'elenco delle app installate, pronte per l'avvio.
Avviare l'applicazione di realtà mista.
Spostarsi nella scena, avvicinando gli oggetti e esaminandoli, quando il servizio Insight di Azure ha raccolto dati di eventi sufficienti, imposta l'oggetto che è stato avvicinato al più verde.
Importante
Mentre il tempo medio di attesa per gli eventi e le metriche da raccogliere dal servizio richiede circa 15 minuti, in alcune occasioni potrebbe richiedere fino a 1 ora.
Capitolo 14 - Portale del servizio Application Insights
Dopo aver eseguito il roaming della scena e aver guardato diversi oggetti, è possibile visualizzare i dati raccolti nel portale del servizio Application Insights.
Tornare al portale del servizio Application Insights.
Selezionare Esplora metriche.
Verrà aperta in una scheda contenente il grafico, che rappresenta gli eventi e le metriche correlate all'applicazione. Come accennato in precedenza, potrebbero essere necessari alcuni minuti (fino a 1 ora) per visualizzare i dati nel grafico
Selezionare la barra Eventi nella sezione Totale eventi per versione applicazione per visualizzare una suddivisione dettagliata degli eventi con i relativi nomi.
Applicazione del servizio Application Insights completata
È stata creata un'app di realtà mista che sfrutta il servizio Application Insights per monitorare l'attività dell'utente all'interno dell'app.
Esercizi bonus
Esercizio 1
Provare a generare, anziché creare manualmente, gli oggetti ObjectInScene e impostarne le coordinate sul piano all'interno degli script. In questo modo, è possibile chiedere ad Azure quale fosse l'oggetto più diffuso (dai risultati dello sguardo fisso o della prossimità) e generare uno di questi oggetti aggiuntivo .
Esercizio 2
Ordinare i risultati di Application Insights in base al tempo, in modo da ottenere i dati più rilevanti e implementare i dati sensibili al tempo nell'applicazione.