HoloLens (1e generatie) en Azure 309: Application Insights
Notitie
De Mixed Reality Academy-zelfstudies zijn ontworpen met HoloLens (1e generatie) en Mixed Reality Immersive Headsets in gedachten. Daarom vinden we het belangrijk om deze zelfstudies te behouden voor ontwikkelaars die nog steeds op zoek zijn naar richtlijnen bij het ontwikkelen voor die apparaten. Deze zelfstudies worden niet bijgewerkt met de nieuwste toolsets of interacties die worden gebruikt voor HoloLens 2. Ze blijven behouden om door te gaan met het werken op de ondersteunde apparaten. Er is een nieuwe reeks zelfstudies die in de toekomst zullen worden gepost en die laten zien hoe u zich ontwikkelt voor HoloLens 2. Deze kennisgeving wordt bijgewerkt met een koppeling naar deze zelfstudies wanneer ze worden gepost.
In deze cursus leert u hoe u Application Insights-mogelijkheden toevoegt aan een mixed reality-toepassing met behulp van de api Azure-toepassing Insights om analyses te verzamelen met betrekking tot gebruikersgedrag.
Application Insights is een Microsoft-service waarmee ontwikkelaars analyses van hun toepassingen kunnen verzamelen en beheren vanuit een gebruiksvriendelijke portal. De analyse kan van alles zijn, van prestaties tot aangepaste gegevens die u wilt verzamelen. Ga naar de Application Insights-pagina voor meer informatie.
Nadat u deze cursus hebt voltooid, hebt u een mixed reality immersive headset-toepassing, die het volgende kan doen:
- Hiermee staat u toe dat de gebruiker naar een scène kijkt en navigeert.
- Activeer het verzenden van analyses naar de Application Insights-service met behulp van gaze- en nabijheidsobjecten in de scène.
- De app roept ook de Service aan, waarbij informatie wordt opgehaald over welk object het meest door de gebruiker is benaderd, binnen de afgelopen 24 uur. Dat object wijzigt de kleur in groen.
In deze cursus leert u hoe u de resultaten van de Application Insights-service kunt ophalen in een voorbeeldtoepassing op basis van Unity. Het is aan u om deze concepten toe te passen op een aangepaste toepassing die u mogelijk bouwt.
Ondersteuning voor apparaten
Cursus | HoloLens | Insluitende headsets |
---|---|---|
MR en Azure 309: Application Insights | ✔️ | ✔️ |
Notitie
Hoewel deze cursus voornamelijk gericht is op Windows Mixed Reality immersive headsets (VR), kunt u ook toepassen wat u in deze cursus leert op Microsoft HoloLens. Terwijl u de cursus volgt, ziet u notities over de wijzigingen die u mogelijk moet toepassen om HoloLens te ondersteunen. Wanneer u HoloLens gebruikt, ziet u mogelijk echo tijdens spraakopname.
Vereisten
Notitie
Deze zelfstudie is ontworpen voor ontwikkelaars die basiservaring hebben met Unity en C#. Houd er ook rekening mee dat de vereisten en schriftelijke instructies in dit document staan voor wat er op het moment van schrijven is getest en geverifieerd (juli 2018). U bent vrij om de nieuwste software te gebruiken, zoals vermeld in het artikel over de installatie van de hulpprogramma's , hoewel wordt ervan uitgegaan dat de informatie in deze cursus perfect overeenkomt met wat u vindt in nieuwere software dan hieronder wordt vermeld.
Voor deze cursus raden we de volgende hardware en software aan:
- Een ontwikkel-pc, compatibel met Windows Mixed Reality voor insluitende headsetontwikkeling (VR)
- Windows 10 Fall Creators Update (of hoger) waarvoor de ontwikkelaarsmodus is ingeschakeld
- De nieuwste Windows 10 SDK
- Unity 2017.4
- Visual Studio 2017
- Een Windows Mixed Reality immersive (VR)-headset of Microsoft HoloLens waarvoor de ontwikkelaarsmodus is ingeschakeld
- Een set hoofdtelefoons met een ingebouwde microfoon (als de headset geen ingebouwde microfoon en luidsprekers heeft)
- Internettoegang voor het instellen van Azure en het ophalen van Application Insights-gegevens
Voordat u begint
Als u problemen wilt voorkomen bij het bouwen van dit project, wordt u sterk aangeraden het project in deze zelfstudie te maken in een hoofdmap of een near-rootmap (lange mappaden kunnen problemen veroorzaken tijdens de build).
Waarschuwing
Houd er rekening mee dat gegevens die naar Application Insights gaan tijd in beslag neemt, dus wees geduldig. Als u wilt controleren of de Service uw gegevens heeft ontvangen, bekijkt u hoofdstuk 14, waarmee u kunt zien hoe u door de portal navigeert.
Hoofdstuk 1: Azure Portal
Als u Application Insights wilt gebruiken, moet u een Application Insights-service maken en configureren in Azure Portal.
Meld u aan bij Azure-portal.
Notitie
Als u nog geen Azure-account hebt, moet u er een maken. Als u deze zelfstudie volgt in een leslokaal- of labsituatie, vraagt u uw docent of een van de proctors voor hulp bij het instellen van uw nieuwe account.
Zodra u bent aangemeld, klikt u op Nieuw in de linkerbovenhoek en zoekt u Naar Application Insights en klikt u op Enter.
Notitie
Het woord Nieuw is mogelijk vervangen door Een resource maken in nieuwere portals.
De nieuwe pagina aan de rechterkant geeft een beschrijving van de Azure-toepassing Insights Service. Selecteer linksonder op deze pagina de knop Maken om een koppeling met deze service te maken.
Zodra u op Maken hebt geklikt:
Voeg de gewenste naam in voor dit service-exemplaar.
Als toepassingstype selecteert u Algemeen.
Selecteer een geschikt abonnement.
Kies een resourcegroep of maak een nieuwe. Een resourcegroep biedt een manier om de toegang te bewaken, te beheren, facturering in te richten en te beheren voor een verzameling Azure-assets. Het is raadzaam om alle Azure-services die zijn gekoppeld aan één project (zoals deze cursussen) onder een gemeenschappelijke resourcegroep te houden.
Als u meer wilt weten over Azure-resourcegroepen, gaat u naar het artikel over de resourcegroep.
Selecteer een Locatie.
U moet ook bevestigen dat u de voorwaarden hebt begrepen die zijn toegepast op deze service.
Selecteer Maken.
Nadat u op Maken hebt geklikt, moet u wachten totdat de service is gemaakt. Dit kan een minuut duren.
Er wordt een melding weergegeven in de portal zodra het service-exemplaar is gemaakt.
Selecteer de meldingen om uw nieuwe service-exemplaar te verkennen.
Klik op de knop Ga naar de resource in de melding om uw nieuwe service-exemplaar te verkennen. U wordt naar uw nieuwe Application Insights Service-exemplaar gebracht.
Notitie
Houd deze webpagina open en gemakkelijk toegankelijk, u komt hier vaak terug om de verzamelde gegevens te zien.
Belangrijk
Als u Application Insights wilt implementeren, moet u drie (3) specifieke waarden gebruiken: Instrumentatiesleutel, toepassings-id en API-sleutel. Hieronder ziet u hoe u deze waarden ophaalt uit uw service. Noteer deze waarden op een lege Kladblok-pagina , omdat u deze binnenkort in uw code zult gebruiken.
Als u de instrumentatiesleutel wilt vinden, moet u omlaag schuiven in de lijst met servicefuncties en Eigenschappen selecteren. Op het weergegeven tabblad wordt de servicesleutel weergegeven.
Een beetje onder Eigenschappen vindt u API Access, waarop u moet klikken. In het deelvenster aan de rechterkant wordt de toepassings-id van uw app opgegeven.
Als het deelvenster Toepassings-id nog steeds is geopend, klikt u op API-sleutel maken. Hiermee opent u het deelvenster API-sleutel maken.
Typ in het nu geopende deelvenster API-sleutel maken een beschrijving en schakel de drie vakken in.
Klik op Sleutel genereren. Uw API-sleutel wordt gemaakt en weergegeven.
Waarschuwing
Dit is de enige keer dat uw servicesleutel wordt weergegeven, dus zorg ervoor dat u er nu een kopie van maakt.
Hoofdstuk 2 - Het Unity-project instellen
Hier volgt een typische installatie voor het ontwikkelen met mixed reality en is daarom een goede sjabloon voor andere projecten.
Open Unity en klik op Nieuw.
U moet nu een Unity-projectnaam opgeven, MR_Azure_Application_Insights invoegen. Zorg ervoor dat de sjabloon is ingesteld op 3D. Stel de locatie in op een locatie die geschikt is voor u (vergeet niet, dichter bij hoofdmappen is beter). Klik vervolgens op Project maken.
Als Unity is geopend, is het de moeite waard om te controleren of de standaardscripteditor is ingesteld op Visual Studio. Ga naar Voorkeuren bewerken > en navigeer vervolgens vanuit het nieuwe venster naar Externe hulpprogramma's. Wijzig de externe scripteditor in Visual Studio 2017. Sluit het venster Voorkeuren .
Ga vervolgens naar > Instellingen voor bestandsbuild en schakel het platform over naar Universeel Windows-platform door op de knop Platform wisselen te klikken.
Ga naar Instellingen voor bestandsbuild > en zorg ervoor dat:
Doelapparaat is ingesteld op Elk apparaat
Voor de Microsoft HoloLens stelt u Doelapparaat in op HoloLens.
Buildtype is ingesteld op D3D
SDK is ingesteld op Laatst geïnstalleerd
Bouwen en uitvoeren is ingesteld op lokale computer
Sla de scène op en voeg deze toe aan de build.
Doe dit door Open Scènes toevoegen te selecteren. Er wordt een venster voor opslaan weergegeven.
Maak hiervoor een nieuwe map en een toekomstige scène en klik vervolgens op de knop Nieuwe map om een nieuwe map te maken, geef deze de naam Scènes.
Open de zojuist gemaakte map Scènes en klik vervolgens in de bestandsnaam: tekstveld, typ ApplicationInsightsScene en klik vervolgens op Opslaan.
De overige instellingen, in Build-instellingen, moeten voorlopig standaard blijven staan.
Selecteer In het venster Build Settings de optie Player Settings. Hiermee opent u het bijbehorende deelvenster in de ruimte waar de Inspector zich bevindt.
In dit deelvenster moeten enkele instellingen worden geverifieerd:
Op het tabblad Overige instellingen :
De runtimeversie van scripting moet experimenteel (.NET 4.6 Equivalent) zijn, waardoor de editor opnieuw moet worden opgestart.
Back-end voor scripts moet .NET zijn
API-compatibiliteitsniveau moet .NET 4.6 zijn
Schakel op het tabblad Publicatie-instellingen onder Mogelijkheden het volgende in:
InternetClient
Verderop in het deelvenster, in XR-instellingen (onder Publicatie-instellingen), tikt u op Virtual Reality Ondersteund, controleert u of de Windows Mixed Reality SDK is toegevoegd.
In Build Settings wordt Unity C# Projects niet meer grijs weergegeven. Schakel het selectievakje naast dit selectievakje in.
Sluit het venster Build Settings.
Sla uw scène en project op (FILE>SAVE SCENE/ FILE>SAVE PROJECT).
Hoofdstuk 3: Het Unity-pakket importeren
Belangrijk
Als u de Unity Set up-onderdelen van deze cursus wilt overslaan en direct in code wilt doorgaan, kunt u deze Azure-MR-309.unitypackage downloaden, het importeren in uw project als een aangepast pakket. Dit bevat ook de DLL's uit het volgende hoofdstuk. Na de invoer gaat u verder vanaf hoofdstuk 6.
Belangrijk
Als u Application Insights in Unity wilt gebruiken, moet u het DLL-bestand importeren, samen met de Newtonsoft-DLL. Er is momenteel een bekend probleem in Unity waarvoor invoegtoepassingen na het importeren opnieuw moeten worden geconfigureerd. Deze stappen (4 - 7 in deze sectie) zijn niet meer vereist nadat de fout is opgelost.
Als u Application Insights in uw eigen project wilt importeren, moet u ervoor zorgen dat u de .unitypackage hebt gedownload met de invoegtoepassingen. Ga als volgt verder:
Voeg the.unitypackage** toe aan Unity met behulp van de menuoptie Aangepast pakket voor > assets > importeren.
Controleer in het vak Import Unity Package dat verschijnt of alles onder (en inclusief) invoegtoepassingen is geselecteerd.
Klik op de knop Importeren om de items aan uw project toe te voegen.
Ga naar de map Insights onder Invoegtoepassingen in de projectweergave en selecteer alleen de volgende invoegtoepassingen:
- Microsoft.ApplicationInsights
Als deze invoegtoepassing is geselecteerd, controleert u of Any Platform is uitgeschakeld en controleert u of WSAPlayer ook is uitgeschakeld en klikt u vervolgens op Toepassen. Dit is alleen om te bevestigen dat de bestanden correct zijn geconfigureerd.
Notitie
Als u de invoegtoepassingen als deze markeert, configureert u deze zodanig dat ze alleen worden gebruikt in de Unity Editor. Er is een andere set DLL's in de WSA-map die wordt gebruikt nadat het project is geëxporteerd uit Unity.
Vervolgens moet u de WSA-map openen in de map Insights . U ziet een kopie van hetzelfde bestand dat u hebt geconfigureerd. Selecteer dit bestand en controleer vervolgens in de inspector of Any Platform is uitgeschakeld en zorg ervoor dat alleen WSAPlayer is ingeschakeld. Klik op Toepassen.
U moet nu stap 4-6 volgen, maar in plaats daarvan voor de Newtonsoft-invoegtoepassingen. Zie de onderstaande schermopname voor hoe het resultaat eruit moet zien.
Hoofdstuk 4 - De camera- en gebruikersbesturingselementen instellen
In dit hoofdstuk stelt u de camera en de besturingselementen in zodat de gebruiker de scène kan zien en verplaatsen.
Klik met de rechtermuisknop in een leeg gebied in het deelvenster Hiërarchie en klik vervolgens op Leeg maken>.
Wijzig de naam van het nieuwe lege GameObject in Camera Parent.
Klik met de rechtermuisknop in een leeg gebied in het deelvenster Hiërarchie en vervolgens op 3D-object en vervolgens op Sphere.
Wijzig de naam van de bol in de rechterhand.
De transformatieschaal van de rechterhand instellen op 0.1, 0.1, 0.1
Verwijder het Sphere Collider-onderdeel uit de rechterhand door op het tandwiel in het Sphere Collider-onderdeel te klikken en vervolgens component verwijderen.
Sleep in het deelvenster Hiërarchie de hoofdcamera en de rechterobjecten naar het bovenliggende object Camera.
Stel de transformatiepositie van zowel de hoofdcamera als het object rechts in op 0, 0, 0.
Hoofdstuk 5: de objecten instellen in de Unity-scène
U gaat nu enkele basisshapes maken voor uw scène, waarmee de gebruiker kan communiceren.
Klik met de rechtermuisknop in een leeg gebied in het deelvenster Hiërarchie en selecteer vervolgens Op 3D-object de optie Vlak.
Stel de positie van de vlaktransformatie in op 0, -1, 0.
Stel de schaal van de vlaktransformatie in op 5, 1, 5.
Maak een basismateriaal voor gebruik met uw Plane-object , zodat de andere shapes gemakkelijker te zien zijn. Navigeer naar het deelvenster Project, klik met de rechtermuisknop en klik vervolgens op Maken, gevolgd door Map, om een nieuwe map te maken. Noem het materiaal.
Open de map Materialen en klik met de rechtermuisknop op Maken en vervolgens Op Materiaal om een nieuw materiaal te maken. Noem het blauw.
Als het nieuwe blauwe materiaal is geselecteerd, bekijkt u de Inspector en klikt u op het rechthoekige venster naast Albedo. Selecteer een blauwe kleur (de onderstaande afbeelding is Hex Color: #3592FFFF). Klik op de knop Sluiten zodra u deze hebt gekozen.
Sleep uw nieuwe materiaal vanuit de map Materialen naar uw zojuist gemaakte vlak in uw scène (of zet het neer op het object Plane in de hiërarchie).
Klik met de rechtermuisknop in een leeg gebied in het deelvenster Hiërarchie en klik vervolgens op 3D-object, capsule.
- Terwijl de capsule is geselecteerd, wijzigt u de positie van de transformatie in: -10, 1, 0.
Klik met de rechtermuisknop in een leeg gebied in het deelvenster Hiërarchie en klik vervolgens op 3D-object, kubus.
- Terwijl de kubus is geselecteerd, wijzigt u de positie van de transformatie in: 0, 0, 10.
Klik met de rechtermuisknop in een leeg gebied in het deelvenster Hiërarchie en klik vervolgens op 3D-object, Sphere.
- Terwijl de bol is geselecteerd, wijzigt u de positie van de transformatie in: 10, 0, 0.
Notitie
Deze positiewaarden zijn suggesties. U kunt de posities van de objecten instellen op wat u wilt, hoewel het voor de gebruiker van de toepassing gemakkelijker is als de afstand tussen objecten zich niet te ver van de camera bevindt.
Wanneer uw toepassing wordt uitgevoerd, moet deze de objecten in de scène kunnen identificeren om dit te bereiken, moeten ze worden gelabeld. Selecteer een van de objecten en klik in het deelvenster Inspector op Tag toevoegen..., waarmee de Inspector wordt verwisseld met het deelvenster Tags & Layers .
Klik op het +-symbool (plusteken) en typ de tagnaam als ObjectInScene.
Waarschuwing
Als u een andere naam voor uw tag gebruikt, moet u ervoor zorgen dat deze wijziging ook wordt aangebracht in DataFromAnalytics, ObjectTrigger en Gaze, scripts later, zodat uw objecten in uw scène worden gevonden en gedetecteerd.
Nu de tag is gemaakt, moet u deze toepassen op alle drie uw objecten. Houd de Shift-toets in de hiërarchie ingedrukt en klik vervolgens op de capsule, kubus en bol, en klik vervolgens in de Inspector op de vervolgkeuzelijst naast Tag en klik vervolgens op de ObjectInScene-tag die u hebt gemaakt.
Hoofdstuk 6: de klasse ApplicationInsightsTracker maken
Het eerste script dat u moet maken, is ApplicationInsightsTracker. Dit is verantwoordelijk voor:
Gebeurtenissen maken op basis van gebruikersinteracties die moeten worden verzonden naar Azure-toepassing Insights.
De juiste gebeurtenisnamen maken, afhankelijk van de interactie van de gebruiker.
Gebeurtenissen verzenden naar het Application Insights Service-exemplaar.
Ga als volgt te werk om deze klasse te maken:
Klik met de rechtermuisknop in het projectvenster en klik vervolgens op Map maken>. Geef de mapscripts een naam.
Dubbelklik op de map Scripts om deze te openen. Klik vervolgens in die map met de rechtermuisknop op C#-script maken>. Geef het script de naam ApplicationInsightsTracker.
Dubbelklik op het nieuwe ApplicationInsightsTracker-script om het te openen met Visual Studio.
Werk naamruimten boven aan het script bij zodat deze er als volgt uitziet:
using Microsoft.ApplicationInsights; using Microsoft.ApplicationInsights.DataContracts; using Microsoft.ApplicationInsights.Extensibility; using UnityEngine;
Voeg in de klasse de volgende variabelen in:
/// <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;
Notitie
Stel de waarden voor instrumentationKey, applicationId en API_Key op de juiste wijze in met behulp van de servicesleutels uit de Azure-portal, zoals vermeld in hoofdstuk 1, stap 9 en hoger.
Voeg vervolgens de Methoden Start() en Awake() toe, die worden aangeroepen wanneer de klasse wordt geïnitialiseerd:
/// <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; }
Voeg de methoden toe die verantwoordelijk zijn voor het verzenden van de gebeurtenissen en metrische gegevens die zijn geregistreerd door uw toepassing:
/// <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); }
Sla uw wijzigingen op in Visual Studio voordat u terugkeert naar Unity.
Hoofdstuk 7 - Het gazescript maken
Het volgende script dat moet worden gemaakt, is het Gaze-script . Dit script is verantwoordelijk voor het maken van een Raycast die wordt geprojecteerd vanaf de Main Camera, om te detecteren naar welk object de gebruiker kijkt. In dit geval moet de Raycast bepalen of de gebruiker naar een object kijkt met de ObjectInScene-tag en vervolgens tellen hoe lang de gebruiker naar dat object kijkt .
Dubbelklik op de map Scripts om deze te openen.
Klik met de rechtermuisknop in de map Scripts en klik op C#-script maken>. Geef het script de naam Gaze.
Dubbelklik op het script om het te openen met Visual Studio.
Vervang de bestaande code door het volgende:
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; }
Code voor de methoden Awake() en Start() moet nu worden toegevoegd.
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; }
Voeg in de klasse Gaze de volgende code toe in de methode Update() om een Raycast te projecteren en de doeltreffer te detecteren:
/// <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; } }
Voeg de methode ResetFocusedObject() toe om gegevens naar Application Insights te verzenden wanneer de gebruiker een object heeft bekeken.
/// <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; } } }
U hebt nu het gazescript voltooid. Sla uw wijzigingen op in Visual Studio voordat u terugkeert naar Unity.
Hoofdstuk 8: de klasse ObjectTrigger maken
Het volgende script dat u moet maken, is ObjectTrigger, die verantwoordelijk is voor:
- Onderdelen toevoegen die nodig zijn voor botsing met de hoofdcamera.
- Detecteren of de camera zich in de buurt van een object bevindt dat is gelabeld als ObjectInScene.
Het script maken:
Dubbelklik op de map Scripts om deze te openen.
Klik met de rechtermuisknop in de map Scripts en klik op C#-script maken>. Geef het script de naam ObjectTrigger.
Dubbelklik op het script om het te openen met Visual Studio. Vervang de bestaande code door het volgende:
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); } } }
Sla uw wijzigingen op in Visual Studio voordat u terugkeert naar Unity.
Hoofdstuk 9: de klasse DataFromAnalytics maken
U moet nu het DataFromAnalytics-script maken. Dit is verantwoordelijk voor:
- Het ophalen van analysegegevens over welk object het meest door de camera is benaderd.
- Gebruik de servicesleutels om communicatie met uw Azure-toepassing Insights Service-exemplaar mogelijk te maken.
- Sorteer de objecten in de scène, afhankelijk van het hoogste aantal gebeurtenissen.
- Het wijzigen van de materiaalkleur, van het meest benaderde object, in groen.
Het script maken:
Dubbelklik op de map Scripts om deze te openen.
Klik met de rechtermuisknop in de map Scripts en klik op C#-script maken>. Noem het script DataFromAnalytics.
Dubbelklik op het script om het te openen met Visual Studio.
Voeg de volgende naamruimten in:
using Newtonsoft.Json; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.Networking;
Voeg in het script het volgende in:
/// <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(); }
Voeg in de klasse DataFromAnalytics, direct na de Methode Start(), de volgende methode toe met de naam FetchAnalytics(). Deze methode is verantwoordelijk voor het vullen van de lijst met sleutelwaardeparen, met een GameObject en een aantal tijdelijke aanduidingen voor het aantal gebeurtenissen. Vervolgens wordt de GetWebRequest() coroutine geïnitialiseerd. De querystructuur van de aanroep naar Application Insights vindt u ook in deze methode, als het eindpunt van de query-URL .
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)); } }
Voeg onder de methode FetchAnalytics() een methode toe met de naam GetWebRequest(), die een IEnumerator retourneert. Deze methode is verantwoordelijk voor het aanvragen van het aantal keren dat een gebeurtenis, die overeenkomt met een specifiek GameObject, is aangeroepen in Application Insights. Wanneer alle verzonden query's zijn geretourneerd, wordt de methode DetermineWinner() aangeroepen.
/// <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(); } } } }
De volgende methode is DetermineWinner(), waarmee de lijst met GameObject- en Int-paren wordt gesorteerd op basis van het hoogste aantal gebeurtenissen. Vervolgens verandert de materiaalkleur van dat GameObject in groen (als feedback voor het hebben van het hoogste aantal). Hiermee wordt een bericht met de analyseresultaten weergegeven.
/// <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); }
Voeg de klassestructuur toe die wordt gebruikt om het JSON-object te deserialiseren, ontvangen van Application Insights. Voeg deze klassen onder aan uw DataFromAnalytics-klassebestand toe, buiten de klassedefinitie.
/// <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; } }
Sla uw wijzigingen op in Visual Studio voordat u terugkeert naar Unity.
Hoofdstuk 10 - De klasse Beweging maken
Het script Movement is het volgende script dat u moet maken. Het is verantwoordelijk voor:
- Het verplaatsen van de hoofdcamera volgens de richting waarnaar de camera kijkt.
- Alle andere scripts toevoegen aan scèneobjecten.
Het script maken:
Dubbelklik op de map Scripts om deze te openen.
Klik met de rechtermuisknop in de map Scripts en klik op C#-script maken>. Noem de scriptverplaatsing.
Dubbelklik op het script om het te openen met Visual Studio.
Vervang de bestaande code door het volgende:
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() { } }
Voeg in de klasse Movement onder de lege Methode Update() de volgende methoden in waarmee de gebruiker de handcontroller kan gebruiken om in de virtuele ruimte te navigeren:
/// <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); }
Voeg tot slot de methode-aanroep toe binnen de methode Update( ).
// Update is called once per frame void Update() { UpdateControllerState(); }
Sla uw wijzigingen op in Visual Studio voordat u terugkeert naar Unity.
Hoofdstuk 11 - De scriptsverwijzingen instellen
In dit hoofdstuk moet u het bewegingsscript op de bovenliggende camera plaatsen en de referentiedoelen instellen. Dat script verwerkt vervolgens het plaatsen van de andere scripts waar ze moeten zijn.
Sleep vanuit de map Scripts in het projectvenster het verplaatsingsscript naar het bovenliggende object Camera, dat zich in het deelvenster Hiërarchie bevindt.
Klik op de bovenliggende camera. Sleep in het deelvenster Hiërarchie het object Rechts van het deelvenster Hierarchy naar het referentiedoel, Controller, in het Inspector Panel. Stel de gebruikerssnelheid in op 5, zoals wordt weergegeven in de onderstaande afbeelding.
Hoofdstuk 12 - Het Unity-project bouwen
Alles wat nodig is voor de Unity-sectie van dit project is nu voltooid, dus het is tijd om het te bouwen vanuit Unity.
Navigeer naar Build-instellingen (Instellingen voor bestandsbuild>).
Klik in het venster Build-instellingen op Build.
Er wordt een Bestandenverkenner venster weergegeven waarin u wordt gevraagd om een locatie voor de build. Maak een nieuwe map (door te klikken op Nieuwe map in de linkerbovenhoek) en geef deze de naam BUILDS.
Open de nieuwe map BUILDS en maak nog een map (met nieuwe map ) en geef deze MR_Azure_Application_Insights een naam.
Klik op Map selecteren als de MR_Azure_Application_Insights map is geselecteerd. Het duurt ongeveer een minuut voordat het project is gebouwd.
Na Build wordt Bestandenverkenner weergegeven met de locatie van uw nieuwe project.
Hoofdstuk 13- MR_Azure_Application_Insights-app implementeren op uw computer
De MR_Azure_Application_Insights-app implementeren op uw lokale computer:
Open het oplossingsbestand van uw MR_Azure_Application_Insights-app in Visual Studio.
Selecteer x86 in het Solution Platform, Local Machine.
Selecteer foutopsporing in de oplossingsconfiguratie.
Ga naar het menu Build en klik op Oplossing implementeren om de toepassing naar uw computer te sideloaden.
Uw app moet nu worden weergegeven in de lijst met geïnstalleerde apps die klaar zijn om te worden gestart.
Start de mixed reality-toepassing.
Navigeer door de scène, naderende objecten en bekijk ze, wanneer de Azure Insight Service voldoende gebeurtenisgegevens heeft verzameld, wordt het object ingesteld dat het meest groen is benaderd.
Belangrijk
Hoewel de gemiddelde wachttijd voor de gebeurtenissen en metrische gegevens die door de service moeten worden verzameld ongeveer 15 minuten duurt, kan het in sommige gevallen tot 1 uur duren.
Hoofdstuk 14 - De Application Insights Service-portal
Zodra u rond de scène hebt geroerd en naar verschillende objecten hebt gekeken, kunt u de gegevens zien die zijn verzameld in de Application Insights Service-portal .
Ga terug naar uw Application Insights Service-portal.
Selecteer Metrics Explorer.
Het wordt geopend op een tabblad met de grafiek, dat de gebeurtenissen en metrische gegevens vertegenwoordigt die betrekking hebben op uw toepassing. Zoals hierboven vermeld, kan het enige tijd duren (maximaal 1 uur) voordat de gegevens in de grafiek worden weergegeven
Selecteer de gebeurtenissenbalk in het totaal van gebeurtenissen per toepassingsversie om een gedetailleerde uitsplitsing van de gebeurtenissen met hun namen te bekijken.
Uw Toepassing application Application Insights Service is voltooid
Gefeliciteerd, u hebt een mixed reality-app gemaakt die gebruikmaakt van de Application Insights-service om de activiteiten van gebruikers in uw app te bewaken.
Bonusoefeningen
Oefening 1
Probeer te spawning, in plaats van handmatig te maken, de ObjectInScene-objecten en stel de coördinaten in het vlak in uw scripts in. Op deze manier kunt u Azure vragen wat het populairste object was (van staren of nabijheidsresultaten) en een extra van deze objecten spawnen.
Oefening 2
Sorteer uw Application Insights-resultaten op tijd, zodat u de meest relevante gegevens krijgt en die tijdgevoelige gegevens in uw toepassing implementeert.