HoloLens (första generationen) och Azure 309: Application Insights
Kommentar
Självstudierna för Mixed Reality Academy har utformats med HoloLens (första generationen) och Mixed Reality Immersive Headsets i åtanke. Därför anser vi att det är viktigt att låta de här självstudierna vara kvar för utvecklare som fortfarande letar efter 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 underhålls för att fortsätta arbeta med de enheter som stöds. Det kommer att finnas en ny serie självstudier som kommer att publiceras i framtiden som visar hur du utvecklar för HoloLens 2. Det här meddelandet uppdateras med en länk till de självstudierna 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 Azure Application Insights-API:et 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ätthanterlig portal. Analysen kan vara allt från prestanda till anpassad information som du vill samla in. Mer information finns på sidan Application Insights.
När du har slutfört den här kursen har du en avancerad headsetapp för mixad verklighet, som kommer att kunna göra följande:
- Låt användaren titta och flytta runt i en scen.
- Utlös sändningen av analys till Application Insights Service med hjälp av Gaze and 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 Service 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 | Uppslukande headset |
---|---|---|
MR och Azure 309: Application Insights | ✔️ | ✔️ |
Kommentar
Den här kursen fokuserar främst på Windows Mixed Reality-headset (VR) men du kan även använda det du lär dig i den här kursen för 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 ett eko under röstinspelningen.
Förutsättningar
Kommentar
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 den här kursen perfekt matchar 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 avancerad (VR) headsetutveckling
- Windows 10 Fall Creators Update (eller senare) med utvecklarläge aktiverat
- Den senaste Windows 10 SDK
- Unity 2017.4
- Visual Studio 2017
- Ett Windows Mixed Reality-headset (VR) 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-konfiguration och Application Insights-datahämtning
Innan du börjar
För att undvika problem när du skapar 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 byggtid).
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 Portal
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.
Kommentar
Om du inte redan har ett Azure-konto måste du skapa ett. Om du följer den här självstudien i en klassrums- eller labbsituation ber du din instruktör eller någon av rektorerna om hjälp med att konfigurera ditt nya konto.
När du har loggat in klickar du på Nytt i det övre vänstra hörnet och söker efter Application Insights och klickar på Retur.
Kommentar
Ordet Nytt kan ha ersatts med Skapa en resurs i nyare portaler.
Den nya sidan till höger innehåller en beskrivning av Azure Application Insights Service. 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 enskilt projekt (till exempel sådana kurser) under en gemensam resursgrupp.
Om du vill läsa mer om Azure-resursgrupper kan du gå till resursgruppens artikel.
Välj en plats.
Du måste också bekräfta att du har förstått de villkor som gäller för 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 aviseringarna 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 .
Kommentar
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. Observera dessa värden på en tom Anteckningar-sida , 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 Access, som du behöver 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 som 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 platsen 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 filgenerering > 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älld 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 den nyligen skapade mappen Scener och skriv Sedan ApplicationInsightsScene i fältet Filnamn: text 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 Bygginställningar väljer du Spelarinställningar. Då öppnas den relaterade panelen i utrymmet 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-motsvarighet), 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 (som finns under 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 scen och 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 kod kan du ladda ned den här Azure-MR-309.unitypackage och 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:n 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 åtgärdats.
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 har valts .
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
Med det här plugin-programmet markerat 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.
Kommentar
Om du markerar plugin-programmen så här konfigurerar du dem 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 mappen WSA i mappen Insights . Du ser en kopia av samma fil som du har konfigurerat. Välj den här filen och se sedan till att Alla plattformar är avmarkerade i inspektören och se sedan till att endast WSAPlayer är markerat. Klicka på Använd.
Nu måste du följa steg 4–6, men för Newtonsoft-plugin-program 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 i hierarkipanelen och sedan på Skapa>tom.
Byt namn på den nya tomma GameObject till Kameraförälder.
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 höger hand 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.
I hierarkipanelen drar du huvudkameran och de högra objekten 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 din scen, som användaren kan interagera med.
Högerklicka i ett tomt område i hierarkipanelen och välj sedan Planet 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 objektet Plane , 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 och högerklicka sedan på Skapa och sedan 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 Hex Color: #3592FFFF). Klicka på knappen Stäng när du har valt.
Dra ditt nya material från mappen Material till det nyligen skapade planet i din scen (eller släpp det på planet-objektet i hierarkin).
Högerklicka i ett tomt område i hierarkipanelen och sedan på 3D-objekt, kapsel.
- Med kapseln markerad ändrar du dess transformeringsposition till: -10, 1, 0.
Högerklicka i ett tomt område i 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 i hierarkipanelen och sedan på 3D-objekt, Sphere.
- När Sphere har valts ändrar du dess transformeringsposition till: 10, 0, 0.
Kommentar
Dessa positionsvärden är förslag. Du kan ange objektens positioner till vad du vill, även om det är lättare för användaren av programmet om objektavstånden 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... som växlar inspektören med panelen Taggar och lager.
Klicka på symbolen + (plus) och skriv taggnamnet som ObjectInScene.
Varning
Om du använder ett annat namn för taggen måste du se till att den här ändringen också görs till DataFromAnalytics, ObjectTrigger och Gaze, skript senare, så att dina objekt hittas och identifieras i din scen.
När taggen har skapats måste du nu tillämpa den på alla tre objekten. Håll ned Skift-tangenten från hierarkin och klicka sedan på objekten Capsule, Cube och Sphere. Klicka sedan på listrutan bredvid Tagg i Inspector och klicka 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 för att skicka 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 i 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 visas nedan:
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;
Kommentar
Ange värdena instrumentationKey, applicationId och API_Key på rätt sätt med hjälp av tjänstnycklarna från Azure-portalen enligt beskrivningen i 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 återvänder 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 kommer att 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 ObjectInScene-taggen 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 återvänder 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.
- Identifiera 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 återvänder 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 kontaktats mest av kameran.
- Med hjälp av tjänstnycklarna tillåter du kommunikation med din Azure Application Insights Service-instans.
- Sortera objekten i scenen, enligt vilka har det högsta antalet händelser.
- Ändra materialfärgen, av det mest närmade 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 lägger du direkt efter metoden Start() till följande metod med namnet FetchAnalytics(). Den här metoden ansvarar för att fylla i listan över nyckelvärdepar, med ett GameObject och ett nummer för platshållarhändelseantal. Sedan initieras coroutinen 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, som motsvarar 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 den materiella färgen på 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 Movement
Movement-skriptet är nästa skript som du behöver skapa. Det är ansvarigt 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() { } }
Under den tomma metoden Update() i klassen Movement 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 movement-skriptet på kameraparentesen och ange dess referensmål. Skriptet hanterar sedan att placera de andra skripten där de behöver vara.
Dra förflyttningsskriptet från mappen Skript i projektpanelen till objektet Kamera överordnad i hierarkipanelen.
Klicka på Kamera överordnad. I hierarkipanelen drar du objektet Höger från hierarkipanelen till referensmålet Controller i 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 Bygginställningar klickar du på Skapa.
Ett Utforskaren fönster visas, där 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 programmet för mixad verklighet.
Flytta runt i scenen, närmar sig objekt och tittar 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!
Medan den genomsnittliga väntetiden för händelser och mått som ska samlas in av tjänsten tar cirka 15 minuter, kan det i vissa fall ta upp till 1 timme.
Kapitel 14 – Application Insights-tjänstportalen
När du har vandrat runt på 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 per 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 dem 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 implementera tidskänsliga data i ditt program.