HoloLens (första generationen) och Azure 309: Application Insights
Anteckning
Självstudierna Mixed Reality Academy har utformats med HoloLens (första generationen) och Mixed Reality integrerande headset i åtanke. Därför tycker vi att det är viktigt att låta de här självstudierna vara kvar för utvecklare som fortfarande behöver vägledning för att utveckla för dessa enheter. De här självstudierna uppdateras inte med de senaste verktygsuppsättningarna eller interaktionerna som används för HoloLens 2. De kommer att underhållas för att fortsätta arbeta på de enheter som stöds. Det kommer att finnas en ny serie självstudier som kommer att publiceras i framtiden som visar hur man utvecklar för HoloLens 2. Det här meddelandet uppdateras med en länk till dessa självstudier när de publiceras.
I den här kursen får du lära dig hur du lägger till Application Insights-funktioner i ett mixed reality-program med hjälp av api:et Azure Application Insights för att samla in analyser om användarbeteende.
Application Insights är en Microsoft-tjänst som gör det möjligt för utvecklare att samla in analyser från sina program och hantera dem från en lätthanterad portal. Analysen kan vara allt från prestanda till anpassad information som du vill samla in. Mer information finns på sidan Application Insights.
Efter att ha slutfört den här kursen har du ett integrerande headsetprogram för mixad verklighet, som kommer att kunna göra följande:
- Tillåt att användaren tittar och rör sig i en scen.
- Utlös sändningen av analys till Application Insights-tjänsten med hjälp av Gaze och Proximity to in-scene objects (Blick och närhet till scenobjekt).
- Appen anropar också tjänsten och hämtar information om vilket objekt som har kontaktats mest av användaren under de senaste 24 timmarna. Objektet ändrar färgen till grönt.
Den här kursen lär dig hur du hämtar resultaten från Application Insights-tjänsten till ett Unity-baserat exempelprogram. Det är upp till dig att tillämpa dessa begrepp på ett anpassat program som du kanske skapar.
Stöd för enheter
Kurs | HoloLens | Integrerande headset |
---|---|---|
MR och Azure 309: Application Insights | ✔️ | ✔️ |
Anteckning
Den här kursen fokuserar främst på Windows Mixed Reality integrerande (VR) headset, men du kan också använda det du lär dig i den här kursen för att Microsoft HoloLens. När du följer kursen visas anteckningar om eventuella ändringar som du kan behöva använda för att stödja HoloLens. När du använder HoloLens kan du märka eko under röstinspelningen.
Förutsättningar
Anteckning
Den här självstudien är utformad för utvecklare som har grundläggande erfarenhet av Unity och C#. Tänk också på att kraven och de skriftliga instruktionerna i det här dokumentet representerar det som har testats och verifierats i skrivande stund (juli 2018). Du är fri att använda den senaste programvaran, som anges i artikeln installera verktyg , men det bör inte antas att informationen i denna kurs kommer att matcha perfekt vad du hittar i nyare programvara än vad som anges nedan.
Vi rekommenderar följande maskinvara och programvara för den här kursen:
- En utvecklingsdator som är kompatibel med Windows Mixed Reality för integrerande (VR) headsetutveckling
- Windows 10 Fall Creators Update (eller senare) med utvecklarläge aktiverat
- Senaste Windows 10 SDK
- Unity 2017.4
- Visual Studio 2017
- Ett Windows Mixed Reality integrerande (VR)-headset eller Microsoft HoloLens med utvecklarläge aktiverat
- En uppsättning hörlurar med inbyggd mikrofon (om headsetet inte har en inbyggd mikrofon och högtalare)
- Internetåtkomst för Azure-installation och Application Insights-datahämtning
Innan du börjar
För att undvika problem med att skapa det här projektet rekommenderar vi starkt att du skapar projektet i den här självstudien i en rotmapp eller nära rotmapp (långa mappsökvägar kan orsaka problem vid byggtiden).
Varning
Tänk på att data som går till Application Insights tar tid, så ha tålamod. Om du vill kontrollera om tjänsten har tagit emot dina data kan du läsa kapitel 14, som visar hur du navigerar i portalen.
Kapitel 1 – Azure-portalen
Om du vill använda Application Insights måste du skapa och konfigurera en Application Insights-tjänst i Azure Portal.
Logga in på Azure Portal.
Anteckning
Om du inte redan har ett Azure-konto måste du skapa ett. Om du följer den här självstudien i ett klassrum eller en labbsituation ber du din lärare eller någon av rektorerna om hjälp med att konfigurera ditt nya konto.
När du är inloggad klickar du på Nytt i det övre vänstra hörnet och söker efter Application Insights och klickar på Retur.
Anteckning
Ordet Ny kan ha ersatts med Skapa en resurs i nyare portaler.
Den nya sidan till höger innehåller en beskrivning av tjänsten Azure Application Insights. Längst ned till vänster på den här sidan väljer du knappen Skapa för att skapa en association med den här tjänsten.
När du har klickat på Skapa:
Infoga önskat namn för den här tjänstinstansen.
Som Programtyp väljer du Allmänt.
Välj en lämplig prenumeration.
Välj en resursgrupp eller skapa en ny. En resursgrupp är ett sätt att övervaka, kontrollera åtkomst, etablera och hantera fakturering för en samling Azure-tillgångar. Vi rekommenderar att du behåller alla Azure-tjänster som är associerade med ett enda projekt (till exempel dessa kurser) under en gemensam resursgrupp).
Om du vill läsa mer om Azure-resursgrupper kan du läsa artikeln om resursgrupper.
Välj en plats.
Du måste också bekräfta att du har förstått de villkor som tillämpas på den här tjänsten.
Välj Skapa.
När du har klickat på Skapa måste du vänta tills tjänsten har skapats. Det kan ta en minut.
Ett meddelande visas i portalen när tjänstinstansen har skapats.
Välj meddelandena för att utforska din nya tjänstinstans.
Klicka på knappen Gå till resurs i meddelandet för att utforska din nya tjänstinstans. Du kommer till din nya Application Insights Service-instans .
Anteckning
Håll den här webbsidan öppen och enkel att komma åt. Du kommer ofta tillbaka hit för att se de data som samlas in.
Viktigt
För att implementera Application Insights måste du använda tre (3) specifika värden: Instrumentationsnyckel, Program-ID och API-nyckel. Nedan visas hur du hämtar dessa värden från din tjänst. Se till att anteckna dessa värden på en tom sida i Anteckningar , eftersom du snart kommer att använda dem i koden.
För att hitta instrumenteringsnyckeln måste du rulla nedåt i listan över tjänstfunktioner och välja Egenskaper. Fliken som visas visar tjänstnyckeln.
Lite under Egenskaper hittar du API-åtkomst, som du måste klicka på. Panelen till höger anger appens program-ID .
När panelen Program-ID fortfarande är öppen klickar du på Skapa API-nyckel, som öppnar panelen Skapa API-nyckel .
I den nu öppna panelen Skapa API-nyckel skriver du en beskrivning och markerar de tre rutorna.
Klicka på Generera nyckel. Din API-nyckel skapas och visas.
Varning
Det här är den enda gången din tjänstnyckel visas, så se till att du gör en kopia av den nu.
Kapitel 2 – Konfigurera Unity-projektet
Följande är en typisk konfiguration för utveckling med mixad verklighet och är därför en bra mall för andra projekt.
Öppna Unity och klicka på Nytt.
Nu måste du ange ett Unity-projektnamn, infoga MR_Azure_Application_Insights. Kontrollera att mallen är inställd på 3D. Ange plats till någonstans som passar dig (kom ihåg att närmare rotkataloger är bättre). Klicka sedan på Skapa projekt.
När Unity är öppet är det värt att kontrollera att standardskriptredigeraren är inställd på Visual Studio. Gå till Redigera > inställningar och gå sedan till Externa verktyg från det nya fönstret. Ändra extern skriptredigerare till Visual Studio 2017. Stäng fönstret Inställningar .
Gå sedan till Inställningar för filbygge > och växla plattformen till Universell Windows-plattform genom att klicka på knappen Växla plattform.
Gå till Inställningar för filbygge > och kontrollera att:
Målenheten är inställd på Valfri enhet
För Microsoft HoloLens anger du Målenhet till HoloLens.
Byggtyp är inställt på D3D
SDK är inställt på Senaste installerat
Build and Run är inställt på Lokal dator
Spara scenen och lägg till den i bygget.
Gör detta genom att välja Lägg till öppna scener. Ett spara-fönster visas.
Skapa en ny mapp för detta, och eventuella framtida scenarier, klicka sedan på knappen Ny mapp , för att skapa en ny mapp, ge den namnet Scener.
Öppna mappen Scener och skriv ApplicationInsightsScene i textfältet Filnamn: och klicka sedan på Spara.
De återstående inställningarna i Bygginställningar bör vara kvar som standard för tillfället.
I fönstret Build Settings (Skapa inställningar ) väljer du Spelarinställningar. Då öppnas den relaterade panelen i det utrymme där inspektören finns.
I den här panelen måste några inställningar verifieras:
På fliken Andra inställningar :
Skriptkörningsversionen bör vara experimentell (.NET 4.6 Motsvarande), vilket utlöser ett behov av att starta om redigeraren.
Skriptserverdelen ska vara .NET
API-kompatibilitetsnivån ska vara .NET 4.6
På fliken Publiceringsinställningar går du till Funktioner och kontrollerar:
InternetClient
Längre ned på panelen, i XR-inställningar (finns nedan Publiceringsinställningar), markerar du Virtual Reality Supported (Virtual Reality Supported) och kontrollerar att Windows Mixed Reality SDK har lagts till.
I Bygginställningar är Unity C# Projects inte längre nedtonat. markera kryssrutan bredvid detta.
Stäng fönstret Build Settings (Bygginställningar).
Spara din scen och ditt projekt (FILE>SAVE SCENE/FILE>SAVE PROJECT).
Kapitel 3 – Importera Unity-paketet
Viktigt
Om du vill hoppa över Unity Set up-komponenterna i den här kursen och fortsätta direkt till koden kan du ladda ned den här Azure-MR-309.unitypackage genom att importera den till ditt projekt som ett anpassat paket. Detta kommer också att innehålla DLL:er från nästa kapitel. Efter importen fortsätter du från kapitel 6.
Viktigt
Om du vill använda Application Insights i Unity måste du importera DLL-filen för den, tillsammans med Newtonsoft DLL. Det finns för närvarande ett känt problem i Unity som kräver att plugin-program konfigureras om efter importen. De här stegen (4–7 i det här avsnittet) krävs inte längre när felet har lösts.
Om du vill importera Application Insights till ditt eget projekt kontrollerar du att du har laddat ned ".unitypackage", som innehåller plugin-programmen. Gör sedan följande:
Lägg till.unitypackage** i Unity med menyalternativet Importera paket > för tillgångar>.
I rutan Importera Unity-paket som visas kontrollerar du att allt under (och inklusive) plugin-program är markerat.
Klicka på knappen Importera för att lägga till objekten i projektet.
Gå till mappen Insights under Plugin-program i projektvyn och välj endast följande plugin-program:
- Microsoft.ApplicationInsights
När du har valt det här plugin-programmet kontrollerar du att Alla plattformar är avmarkerade och kontrollerar sedan att WSAPlayer också är avmarkerat och klickar sedan på Använd. Detta är bara för att bekräfta att filerna är korrekt konfigurerade.
Anteckning
Om plugin-programmen markeras så här konfigureras de så att de endast används i Unity-redigeraren. Det finns en annan uppsättning DLL:er i WSA-mappen som ska användas när projektet har exporterats från Unity.
Därefter måste du öppna WSA-mappen i mappen Insights . Du ser en kopia av samma fil som du har konfigurerat. Välj den här filen och kontrollera sedan att Alla plattformar är avmarkerade i inspektören och se sedan till att endastWSAPlayerär markerat. Klicka på Applicera.
Nu måste du följa steg 4–6, men för Newtonsoft-plugin-programmet i stället. Se skärmbilden nedan för hur resultatet ska se ut.
Kapitel 4 – Konfigurera kamera- och användarkontroller
I det här kapitlet konfigurerar du kameran och kontrollerna så att användaren kan se och flytta i scenen.
Högerklicka i ett tomt område på hierarkipanelen och sedan på Skapa>tom.
Byt namn på den nya tomma GameObject till Kamera överordnad.
Högerklicka i ett tomt område i hierarkipanelen och sedan på 3D-objekt och sedan på Sphere.
Byt namn på sfären till höger.
Ange transformeringsskalan för den högra handen till 0,1, 0,1, 0,1
Ta bort komponenten Sphere Collider från höger hand genom att klicka på kugghjulet i komponenten Sphere Collider och sedan ta bort komponenten.
Dra huvudkameran och objekten till höger på hierarkipanelen till objektet Kamera överordnad .
Ange transformeringspositionen för både huvudkameran och objektet Till höger till 0, 0, 0.
Kapitel 5 – Konfigurera objekten i Unity-scenen
Nu ska du skapa några grundläggande former för scenen, som användaren kan interagera med.
Högerklicka i ett tomt område på hierarkipanelen och välj sedan Plane på 3D-objekt.
Ange planets transformeringsposition till 0, -1, 0.
Ange Skala för plantransformering till 5, 1, 5.
Skapa ett grundläggande material att använda med plane-objektet , så att de andra formerna blir lättare att se. Gå till projektpanelen, högerklicka och skapa sedan Skapa, följt av Mapp, för att skapa en ny mapp. Ge den namnet Material.
Öppna mappen Material , högerklicka, klicka på Skapa och sedan på Material för att skapa ett nytt material. Ge den namnet Blå.
När det nya blå materialet har valts tittar du på Inspector och klickar på det rektangulära fönstret tillsammans med Albedo. Välj en blå färg (den enda bilden nedan är Hexfärg: #3592FFFF). Klicka på knappen Stäng när du har valt.
Dra ditt nya material från mappen Material till det nya planet i scenen (eller släpp det på Plane-objektet i hierarkin).
Högerklicka i ett tomt område på hierarkipanelen och sedan på 3D-objekt, Capsule.
- Med kapseln vald ändrar du dess transformeringsposition till: -10, 1, 0.
Högerklicka i ett tomt område på hierarkipanelen och sedan på 3D-objekt, kub.
- När kuben är markerad ändrar du dess transformeringsposition till: 0, 0, 10.
Högerklicka i ett tomt område på hierarkipanelen och sedan på 3D-objekt, sfär.
- Ändra dess transformeringsposition till: 10, 0, 0 med sfären markerad.
Anteckning
Dessa positionsvärden är förslag. Du kan ställa in positionerna för objekten till vad du vill, även om det är lättare för användaren av programmet om objekt avstånd inte är för långt från kameran.
När programmet körs måste det kunna identifiera objekten i scenen. För att uppnå detta måste de taggas. Välj ett av objekten och klicka på Lägg till tagg i panelen Kontroll. Då växlar du Kontrollanten mot panelen Taggar & Lager.
Klicka på symbolen + (plus) och skriv sedan taggnamnet ObjectInScene.
Varning
Om du använder ett annat namn för taggen måste du se till att den här ändringen även görs till skripten DataFromAnalytics, ObjectTrigger och Gaze senare, så att objekten hittas och identifieras i scenen.
När taggen har skapats måste du tillämpa den på alla tre objekten. I hierarkin håller du ned Skift-tangenten och klickar sedan på objekten Capsule, Cube och Sphere. I Inspector klickar du sedan på listrutemenyn tillsammans med Tagg och sedan på taggen ObjectInScene som du skapade.
Kapitel 6 – Skapa klassen ApplicationInsightsTracker
Det första skriptet du behöver skapa är ApplicationInsightsTracker, som ansvarar för:
Skapa händelser baserat på användarinteraktioner som ska skickas till Azure Application Insights.
Skapa lämpliga händelsenamn, beroende på användarinteraktion.
Skicka händelser till Application Insights Service-instansen.
Så här skapar du den här klassen:
Högerklicka på projektpanelen och sedan på Skapa>mapp. Ge mappen namnet Skript.
När mappen Skript har skapats dubbelklickar du på den för att öppna den. Högerklicka sedan på Skapa>C#-skript i mappen. Ge skriptet namnet ApplicationInsightsTracker.
Dubbelklicka på det nya ApplicationInsightsTracker-skriptet för att öppna det med Visual Studio.
Uppdatera namnrymder överst i skriptet så att de ser ut så här:
using Microsoft.ApplicationInsights; using Microsoft.ApplicationInsights.DataContracts; using Microsoft.ApplicationInsights.Extensibility; using UnityEngine;
Infoga följande variabler i klassen:
/// <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;
Anteckning
Ange värdena instrumentationKey, applicationId och API_Key på rätt sätt med hjälp av tjänstnycklarna från Azure-portalen enligt kapitel 1, steg 9 och senare.
Lägg sedan till metoderna Start() och Awake(), som anropas när klassen initieras:
/// <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; }
Lägg till de metoder som ansvarar för att skicka händelser och mått som registrerats av ditt program:
/// <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); }
Se till att spara ändringarna i Visual Studio innan du återgår till Unity.
Kapitel 7 – Skapa Gaze-skriptet
Nästa skript att skapa är Gaze-skriptet . Det här skriptet ansvarar för att skapa en Raycast som ska projiceras framåt från huvudkameran för att identifiera vilket objekt användaren tittar på. I det här fallet måste Raycast identifiera om användaren tittar på ett objekt med taggen ObjectInScene och sedan räkna hur länge användaren tittar på objektet.
Dubbelklicka på mappen Skript för att öppna den.
Högerklicka i mappen Skript och klicka på Skapa>C#-skript. Ge skriptet namnet Gaze.
Dubbelklicka på skriptet för att öppna det med Visual Studio.
Ersätt den befintliga koden med följande:
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; }
Kod för metoderna Awake() och Start() måste nu läggas till.
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; }
I klassen Gaze lägger du till följande kod i metoden Update() för att projicera en Raycast och identifiera målträffen:
/// <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; } }
Lägg till metoden ResetFocusedObject() för att skicka data till Application Insights när användaren har tittat på ett objekt.
/// <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; } } }
Nu har du slutfört Gaze-skriptet . Spara ändringarna i Visual Studio innan du återgår till Unity.
Kapitel 8 – Skapa klassen ObjectTrigger
Nästa skript som du behöver skapa är ObjectTrigger, som ansvarar för:
- Lägga till komponenter som behövs för kollision till huvudkameran.
- Identifierar om kameran är nära ett objekt som är taggat som ObjectInScene.
Så här skapar du skriptet:
Dubbelklicka på mappen Skript för att öppna den.
Högerklicka i mappen Skript och klicka på Skapa>C#-skript. Ge skriptet namnet ObjectTrigger.
Dubbelklicka på skriptet för att öppna det med Visual Studio. Ersätt den befintliga koden med följande:
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); } } }
Se till att spara ändringarna i Visual Studio innan du återgår till Unity.
Kapitel 9 – Skapa klassen DataFromAnalytics
Nu måste du skapa skriptet DataFromAnalytics , som ansvarar för:
- Hämtar analysdata om vilket objekt som har använts mest av kameran.
- Med hjälp av tjänstnycklar, som tillåter kommunikation med din Azure Application Insights Service-instans.
- Sortera objekten i scenen, enligt vilka har det högsta antalet händelser.
- Ändra materialfärgen, för det mest använda objektet, till grön.
Så här skapar du skriptet:
Dubbelklicka på mappen Skript för att öppna den.
Högerklicka i mappen Skript och klicka på Skapa>C#-skript. Ge skriptet namnet DataFromAnalytics.
Dubbelklicka på skriptet för att öppna det med Visual Studio.
Infoga följande namnområden:
using Newtonsoft.Json; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.Networking;
Infoga följande i skriptet:
/// <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(); }
I klassen DataFromAnalytics , direkt efter metoden Start(), lägger du till följande metod med namnet FetchAnalytics(). Den här metoden ansvarar för att fylla i listan över nyckel/värde-par med ett GameObject och ett antal platshållarhändelser. Sedan initieras Coroutine för GetWebRequest(). Frågestrukturen för anropet till Application Insights finns också i den här metoden som fråge-URL-slutpunkt .
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)); } }
Precis under metoden FetchAnalytics() lägger du till en metod med namnet GetWebRequest(), som returnerar en IEnumerator. Den här metoden ansvarar för att begära hur många gånger en händelse, motsvarande en specifik GameObject, har anropats i Application Insights. När alla skickade frågor har returnerats anropas metoden 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(); } } } }
Nästa metod är DetermineWinner(), som sorterar listan över GameObject - och Int-par , enligt det högsta antalet händelser. Det ändrar sedan materialfärgen för det GameObject till grönt (som feedback för att det har det högsta antalet). Då visas ett meddelande med analysresultatet.
/// <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); }
Lägg till den klassstruktur som ska användas för att deserialisera JSON-objektet som tas emot från Application Insights. Lägg till dessa klasser längst ned i din DataFromAnalytics-klassfil, utanför klassdefinitionen.
/// <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; } }
Se till att spara ändringarna i Visual Studio innan du återvänder till Unity.
Kapitel 10 – Skapa klassen Rörelse
Rörelseskriptet är nästa skript som du behöver skapa. Den ansvarar för:
- Flytta huvudkameran enligt den riktning som kameran tittar mot.
- Lägga till alla andra skript i scenobjekt.
Så här skapar du skriptet:
Dubbelklicka på mappen Skript för att öppna den.
Högerklicka i mappen Skript och klicka på Skapa>C#-skript. Ge skriptet namnet Movement.
Dubbelklicka på skriptet för att öppna det med Visual Studio.
Ersätt den befintliga koden med följande:
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() { } }
I klassen Rörelse, under den tomma metoden Update(), infogar du följande metoder som gör att användaren kan använda handstyrenheten för att flytta i det virtuella utrymmet:
/// <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); }
Lägg slutligen till metodanropet i metoden Update().
// Update is called once per frame void Update() { UpdateControllerState(); }
Se till att spara ändringarna i Visual Studio innan du återvänder till Unity.
Kapitel 11 – Konfigurera skriptreferenser
I det här kapitlet måste du placera rörelseskriptet på kameraparentesen och ange dess referensmål. Skriptet hanterar sedan att placera de andra skripten där de behöver vara.
Dra rörelseskriptet till objektet Kamera överordnat i hierarkipanelen från mappen Skript i projektpanelen.
Klicka på Kamera överordnad. I hierarkipanelen drar du objektet Höger från hierarkipanelen till referensmålet Controller på kontrollpanelen. Ange användarhastigheten till 5, enligt bilden nedan.
Kapitel 12 – Skapa Unity-projektet
Allt som behövs för Unity-avsnittet i det här projektet har nu slutförts, så det är dags att bygga det från Unity.
Gå till Build Settings (File>Build Settings).
I fönstret Build Settings (Skapa inställningar ) klickar du på Skapa.
Ett Utforskaren fönster visas och du uppmanas att ange en plats för bygget. Skapa en ny mapp (genom att klicka på Ny mapp i det övre vänstra hörnet) och ge den namnet BUILDS.
Öppna den nya BUILDS-mappen och skapa en annan mapp (med ny mapp igen) och ge den namnet MR_Azure_Application_Insights.
När MR_Azure_Application_Insights mapp är markerad klickar du på Välj mapp. Det tar ungefär en minut att skapa projektet.
Efter Build visas Utforskaren som visar platsen för det nya projektet.
Kapitel 13 – Distribuera MR_Azure_Application_Insights app till din dator
Så här distribuerar du MR_Azure_Application_Insights-appen på din lokala dator:
Öppna lösningsfilen för din MR_Azure_Application_Insights-app i Visual Studio.
I Lösningsplattform väljer du x86, Lokal dator.
I Lösningskonfiguration väljer du Felsök.
Gå till menyn Skapa och klicka på Distribuera lösning för att separat läsa in programmet på datorn.
Din app bör nu visas i listan över installerade appar som är redo att startas.
Starta mixed reality-programmet.
Flytta runt i scenen, närma dig objekt och titta på dem, när Azure Insight Service har samlat in tillräckligt med händelsedata, kommer det att ange det objekt som har kontaktats mest till grönt.
Viktigt
Den genomsnittliga väntetiden för händelser och mått som ska samlas in av tjänsten tar cirka 15 minuter, men i vissa fall kan det ta upp till 1 timme.
Kapitel 14 – Application Insights-tjänstportalen
När du har vandrat runt i scenen och tittat på flera objekt kan du se de data som samlas in i Application Insights Service-portalen .
Gå tillbaka till Application Insights Service-portalen.
Välj Metrics Explorer.
Den öppnas på en flik som innehåller grafen, som representerar händelser och mått som är relaterade till ditt program. Som nämnts ovan kan det ta en stund (upp till 1 timme) innan data visas i diagrammet
Välj fältet Händelser i Total of Events by Application Version ( Totalt antal händelser efter programversion) för att se en detaljerad uppdelning av händelserna med deras namn.
Ditt program för Application Insights-tjänsten har slutförts
Grattis, du har skapat en mixed reality-app som använder Application Insights Service för att övervaka användarens aktivitet i din app.
Bonusövningar
Övning 1
Prova att skapa objekten ObjectInScene i stället för att skapa objekten manuellt och ange deras koordinater på planet i skripten. På så sätt kan du fråga Azure vad det mest populära objektet var (antingen från blick eller närhetsresultat) och skapa ett extra objekt.
Övning 2
Sortera dina Application Insights-resultat efter tid, så att du får de mest relevanta data och implementerar tidskänsliga data i ditt program.