HoloLens (1e generatie) en Azure 309: Application Insights
Opmerking
De zelfstudies van Mixed Reality Academy zijn ontworpen met HoloLens (1e generatie) en Mixed Reality Immersive Headsets in gedachten. Daarom vinden we het belangrijk om deze zelfstudies te laten staan voor ontwikkelaars die nog steeds op zoek zijn naar hulp bij het ontwikkelen van deze apparaten. Deze zelfstudies worden niet bijgewerkt met de nieuwste hulpprogramma's of interacties die worden gebruikt voor HoloLens 2. Ze worden onderhouden om te blijven werken op de ondersteunde apparaten. Er zal in de toekomst een nieuwe reeks zelfstudies worden gepost die laten zien hoe u kunt ontwikkelen voor HoloLens 2. Deze kennisgeving wordt bijgewerkt met een koppeling naar die zelfstudies wanneer ze worden geplaatst.
In deze cursus leert u hoe u Application Insights-mogelijkheden toevoegt aan een mixed reality-toepassing met behulp van de Azure-toepassing Insights-API om analyses met betrekking tot gebruikersgedrag te verzamelen.
Application Insights is een Microsoft-service waarmee ontwikkelaars analyses van hun toepassingen kunnen verzamelen en deze kunnen beheren vanuit een gebruiksvriendelijke portal. De analyse kan van alles zijn, van prestaties tot aangepaste informatie die u wilt verzamelen. Ga naar de pagina Application Insights voor meer informatie.
Nadat u deze cursus hebt voltooid, hebt u een mixed reality immersive headset-toepassing, die het volgende kan doen:
- Sta de gebruiker toe om een scène te staren en te verplaatsen.
- Activeer het verzenden van analyses naar de Application Insights-service met behulp van Starze en Proximity voor objecten in scènes.
- De app roept ook de Service aan en haalt informatie op over welk object het meest is benaderd door de gebruiker, in de afgelopen 24 uur. Dat object verandert 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.
Apparaatondersteuning
Cursus | HoloLens | Meeslepende headsets |
---|---|---|
MR en Azure 309: Application Insights | ✔️ | ✔️ |
Opmerking
Hoewel deze cursus zich voornamelijk richt op Windows Mixed Reality immersive (VR)-headsets, kunt u wat u in deze cursus leert ook toepassen op Microsoft HoloLens. Terwijl u de cursus volgt, ziet u notities over alle wijzigingen die u mogelijk moet toepassen om HoloLens te ondersteunen. Wanneer u HoloLens gebruikt, ziet u mogelijk wat echo tijdens spraakopname.
Voorwaarden
Opmerking
Deze zelfstudie is bedoeld voor ontwikkelaars die basiservaring hebben met Unity en C#. Houd er ook rekening mee dat de vereisten en schriftelijke instructies in dit document vertegenwoordigen wat is getest en geverifieerd op het moment van schrijven (juli 2018). U bent vrij om de nieuwste software te gebruiken, zoals vermeld in het artikel over het installeren van de hulpprogramma's , hoewel niet mag worden aangenomen dat de informatie in deze cursus perfect overeenkomt met wat u in nieuwere software zult vinden dan wat hieronder wordt vermeld.
Voor deze cursus raden we de volgende hardware en software aan:
- Een ontwikkel-pc, compatibel met Windows Mixed Reality voor ontwikkeling van immersive headsets (VR)
- Windows 10 Fall Creators Update (of hoger) met de ontwikkelaarsmodus ingeschakeld
- De nieuwste Windows 10 SDK
- Unity 2017.4
- Visual Studio 2017
- Een Windows Mixed Reality immersive (VR) headset of Microsoft HoloLens met ontwikkelaarsmodus 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 van start gaat
Om problemen bij het bouwen van dit project te voorkomen, wordt u sterk aangeraden het project in deze zelfstudie te maken in een hoofdmap of bijna-hoofdmap (lange mappaden kunnen tijdens het bouwen problemen veroorzaken).
Waarschuwing
Houd er rekening mee dat gegevens die naar Application Insights gaan, tijd kosten, dus wees geduldig. Als u wilt controleren of de Service uw gegevens heeft ontvangen, raadpleegt u Hoofdstuk 14, waarin wordt uitgelegd 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 de Azure Portal.
Meld u aan bij Azure Portal.
Opmerking
Als u nog geen Azure-account hebt, moet u er een maken. Als u deze zelfstudie volgt in een les- of labsituatie, vraagt u uw instructeur of een van de proctors om hulp bij het instellen van uw nieuwe account.
Zodra u bent aangemeld, klikt u op Nieuw in de linkerbovenhoek, zoekt u naar Application Insights en klikt u op Enter.
Opmerking
Het woord Nieuw is mogelijk vervangen door Een resource maken in nieuwere portals.
De nieuwe pagina aan de rechterkant bevat een beschrijving van de Azure-toepassing Insights Service. Selecteer linksonder op deze pagina de knop Maken om een koppeling met deze service te maken.
Nadat u op Maken hebt geklikt:
Voeg de gewenste naam voor dit service-exemplaar in.
SelecteerAlgemeen als Toepassingstype.
Selecteer een geschikt abonnement.
Kies een resourcegroep of maak een nieuwe. Een resourcegroep biedt een manier om een verzameling Azure-assets te bewaken, de toegang te beheren, de facturering in te richten en te beheren. Het is raadzaam om alle Azure-services die zijn gekoppeld aan één project (bijvoorbeeld deze cursussen) onder een gemeenschappelijke resourcegroep te houden.
Als u meer wilt lezen 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 op deze Service van toepassing zijn.
Selecteer Maken.
Nadat u op Maken hebt geklikt, moet u wachten tot 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 resource in de melding om uw nieuwe service-exemplaar te verkennen. U wordt naar uw nieuwe Application Insights Service-exemplaar gebracht.
Opmerking
Houd deze webpagina geopend en gemakkelijk toegankelijk. U komt hier vaak terug om de verzamelde gegevens te bekijken.
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 kunt ophalen uit uw Service. Noteer deze waarden op een lege Kladblok-pagina , omdat u ze 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.
Onder Eigenschappen vindt u API-toegang, waarop u moet klikken. In het deelvenster aan de rechterkant wordt de toepassings-id van uw app weergegeven.
Terwijl het deelvenster Toepassings-id nog steeds is geopend, klikt u op API-sleutel maken. Het deelvenster API-sleutel maken wordt geopend.
Typ in het nu geopende deelvenster API-sleutel maken een beschrijving en vink de drie vakjes aan.
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
Het volgende is een typische installatie voor het ontwikkelen met de mixed reality en is als zodanig een goede sjabloon voor andere projecten.
Open Unity en klik op Nieuw.
U moet nu een Unity-projectnaam opgeven en MR_Azure_Application_Insights invoegen. Zorg ervoor dat de sjabloon is ingesteld op 3D. Stel de locatie in op een geschikte locatie (dichter bij hoofdmappen is beter). Klik vervolgens op Project maken.
Als Unity is geopend, is het de moeite waard om te controleren of de standaardscript-Editor is ingesteld op Visual Studio. Ga naar Voorkeuren bewerken > en navigeer vervolgens vanuit het nieuwe venster naar Externe hulpprogramma's. Wijzig extern script Editor 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 bestandsopbouw > 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.
Dit doet u door Open scènes toevoegen te selecteren. Er wordt een venster voor opslaan weergegeven.
Maak hiervoor een nieuwe map en een eventuele toekomstige scène en klik vervolgens op de knop Nieuwe map om een nieuwe map te maken en geef deze de naam Scènes.
Open de zojuist gemaakte map Scènes en typ in het tekstveld Bestandsnaam:ApplicationInsightsScene en klik vervolgens op Opslaan.
De overige instellingen, in Build-instellingen, moeten voorlopig als standaard worden gelaten.
Selecteer in het venster Build-instellingende optie Instellingen voor speler. Hiermee wordt het gerelateerde deelvenster geopend in de ruimte waar de Inspector zich bevindt.
In dit deelvenster moeten enkele instellingen worden geverifieerd:
Op het tabblad Overige instellingen :
ScriptingRuntime-versie moet Experimenteel (.NET 4.6 Equivalent) zijn, waardoor de Editor opnieuw moet worden gestart.
De back-end van scripts moet .NET zijn
API-compatibiliteitsniveau moet .NET 4.6 zijn
Controleer op het tabblad Publicatie-instellingen onder Mogelijkheden het volgende:
InternetClient
Verderop in het deelvenster, in XR-instellingen (te vinden onder Publicatie-instellingen), selecteert u Virtual Reality Ondersteund en controleert u of de Windows Mixed Reality SDK is toegevoegd.
Terug in build-instellingen wordt Unity C# Projects niet langer grijs weergegeven; schakel het selectievakje naast dit selectievakje in.
Sluit het venster Build-instellingen.
Sla uw scène en project op (SCÈNE BESTAND>OPSLAAN/PROJECT OPSLAAN> BESTAND).
Hoofdstuk 3- Het Unity-pakket importeren
Belangrijk
Als u de Unity Set up-onderdelen van deze cursus wilt overslaan en direct verder wilt gaan met code, kunt u dit Azure-MR-309.unitypackage downloaden en het in uw project importeren als een aangepast pakket. Deze bevat ook de DLL's uit het volgende hoofdstuk. Ga na het importeren verder vanaf hoofdstuk 6.
Belangrijk
Als u Application Insights in Unity wilt gebruiken, moet u de DLL ervoor importeren, samen met de Newtonsoft-DLL. Er is momenteel een bekend probleem in Unity waardoor 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 wilt importeren in uw eigen project, moet u ervoor zorgen dat u het '.unitypackage' met de invoegtoepassingen hebt gedownload. Ga vervolgens als volgt te werk:
Voeg het.unitypackage** toe aan Unity met behulp van de menuoptie Aangepast pakket voor het importeren > van assets>.
Controleer in het vak Unity-pakket importeren dat wordt weergegeven of alles onder (en inclusief) Invoegtoepassingen is geselecteerd.
Klik op de knop Importeren om de items aan uw project toe te voegen.
Ga in de projectweergave naar de map Inzichten onder Invoegtoepassingen en selecteer alleen de volgende invoegtoepassingen:
- Microsoft.ApplicationInsights
Als deze invoegtoepassing is geselecteerd, controleert u of Elk platform is uitgeschakeld, 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.
Opmerking
Als u de invoegtoepassingen op deze wijze markeert, configureert u ze om ze alleen te gebruiken in de Unity-Editor. De WSA-map bevat een andere set DLL's die wordt gebruikt nadat het project uit Unity is geëxporteerd.
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 in de inspector of Any Platform is uitgeschakeld en zorg er vervolgens voor dat alleenWSAPlayer 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 erin kan bewegen.
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 Bovenliggende camera.
Klik met de rechtermuisknop in een leeg gebied in het deelvenster Hiërarchie, klik vervolgens op 3D-object en vervolgens op Sphere.
Wijzig de naam van de bol in rechterhand.
De transformatieschaal van de rechterhand instellen op 0.1, 0.1, 0.1
Verwijder het onderdeel Sphere Collider uit de rechterhand door te klikken op het tandwiel in het onderdeel Sphere Collider en vervolgens Onderdeel verwijderen.
Sleep in het deelvenster Hiërarchie de objecten Hoofdcamera en rechterhand 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 maakt nu enkele basisvormen voor uw scène, waarmee de gebruiker kan communiceren.
Klik met de rechtermuisknop in een leeg gebied in het deelvenster Hiërarchie, klik vervolgens op 3D-object en selecteer vervolgens 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 vormen beter zichtbaar zijn. Navigeer naar het projectvenster, 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 , 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, kijkt u naar de Inspector en klikt u op het rechthoekige venster naast Albedo. Selecteer een blauwe kleur (de enige afbeelding hieronder is HexKleur: #3592FFFF). Klik op de knop Sluiten zodra u hebt gekozen.
Sleep het nieuwe materiaal vanuit de map Materialen naar het 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 transformatiepositie 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 transformatiepositie 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 transformatiepositie in: 10, 0, 0.
Opmerking
Deze positiewaarden zijn suggesties. U bent vrij om de positie van de objecten in te stellen op wat u wilt, maar het is gemakkelijker voor de gebruiker van de toepassing als de objecten niet te ver van de camera verwijderd zijn.
Wanneer uw toepassing wordt uitgevoerd, moet deze de objecten in de scène kunnen identificeren. Hiervoor moeten ze worden getagd. Selecteer een van de objecten en klik in het deelvenster Inspector op Tag toevoegen... om de Inspector te vervangen door het deelvenster Tags & Lagen .
Klik op het plusteken + en typ de tagnaam als ObjectInScene.
Waarschuwing
Als u een andere naam voor uw tag gebruikt, moet u ervoor zorgen dat deze wijziging later ook wordt doorgevoerd in de scripts DataFromAnalytics, ObjectTrigger en Gaze, zodat uw objecten in uw scène worden gevonden en gedetecteerd.
Nu de tag is gemaakt, moet u deze nu toepassen op alle drie uw objecten. Houd in de hiërarchie de Shift-toets ingedrukt, klik vervolgens op de objecten Capsule, Kubus en Bol, en klik in de Inspector op het vervolgkeuzemenu naast Tag en klik vervolgens op de tag ObjectInScene die u hebt gemaakt.
Hoofdstuk 6: De klasse ApplicationInsightsTracker maken
Het eerste script dat u moet maken, is ApplicationInsightsTracker, dat verantwoordelijk is voor:
Gebeurtenissen maken op basis van gebruikersinteracties die moeten worden verzonden naar Azure-toepassing Insights.
De juiste gebeurtenisnamen maken, afhankelijk van gebruikersinteractie.
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 map de naam Scripts.
Dubbelklik op de map Scripts om de map te openen. Klik vervolgens in die map met de rechtermuisknop opC#-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;
Opmerking
Stel de waarden instrumentationKey, applicationId en API_Key op de juiste manier in met behulp van de servicesleutels van de Azure-portal, zoals vermeld in hoofdstuk 1, stap 9 en hoger.
Voeg vervolgens de methoden Start() en Awake() toe. Deze 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 door uw toepassing zijn geregistreerd:
/// <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 Gaze-script 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 hoofdcamera, om te detecteren welk object de gebruiker bekijkt. In dit geval moet de Raycast bepalen of de gebruiker naar een object kijkt met de tag ObjectInScene en vervolgens tellen hoe lang de gebruiker naar dat object staart .
Dubbelklik op de map Scripts om deze te openen.
Klik met de rechtermuisknop in de map Scripts en klik opC#-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 Gaze-script 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, dat verantwoordelijk is voor:
- Onderdelen die nodig zijn voor botsingen toevoegen aan de hoofdcamera.
- Detecteren of de camera zich in de buurt van een object bevindt dat is getagd als ObjectInScene.
Het script maken:
Dubbelklik op de map Scripts om deze te openen.
Klik met de rechtermuisknop in de map Scripts en klik opC#-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 script DataFromAnalytics maken, dat verantwoordelijk is voor:
- Analytische gegevens ophalen over welk object het meest is benaderd door de camera.
- Met behulp van de servicesleutels, waarmee communicatie met uw Azure-toepassing Insights Service-exemplaar mogelijk is.
- De objecten sorteren in scène, op basis waarvan het hoogste aantal gebeurtenissen is.
- De materiaalkleur van het meest benaderde object wijzigen in groen.
Het script maken:
Dubbelklik op de map Scripts om deze te openen.
Klik met de rechtermuisknop in de map Scripts en klik opC#-script maken>. Geef het script de naam 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 het volgende in het script 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 met de naam FetchAnalytics() toe. Deze methode is verantwoordelijk voor het vullen van de lijst met sleutelwaardeparen, met een GameObject en een tijdelijke aanduiding voor het aantal gebeurtenissen. Vervolgens wordt de coroutine GetWebRequest() geïnitialiseerd. De querystructuur van de aanroep van Application Insights is ook te vinden 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 direct 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 van GameObject - en Int-paren wordt gesorteerd op basis van het hoogste aantal gebeurtenissen. Vervolgens wordt de materiaalkleur van dat GameObject gewijzigd in groen (als feedback voor het hoogste aantal). Hiermee wordt een bericht weergegeven met de analyseresultaten.
/// <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 dat is ontvangen van Application Insights, te deserialiseren. Voeg deze klassen helemaal onder aan het klassebestand DataFromAnalytics 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 Movement maken
Het verplaatsingsscript is het volgende script dat u moet maken. Het is verantwoordelijk voor:
- De hoofdcamera verplaatsen 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 opC#-script maken>. Geef het script de naam Movement.
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 zich in de virtuele ruimte te verplaatsen:
/// <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 ten slotte 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 scripts-verwijzingen instellen
In dit hoofdstuk moet u het bewegingsscript op de bovenliggende camera plaatsen en de referentiedoelen instellen. Dat script zorgt er vervolgens voor dat de andere scripts worden geplaatst waar ze moeten zijn.
Sleep vanuit de map Scripts in het deelvenster Project 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 Hiërarchie naar het referentiedoel, Controller, in het deelvenster Inspector. 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.
Ga naar Build-instellingen(Instellingenvoor bestandsbuilding>).
Klik in het venster Build-instellingen op Bouwen.
Er verschijnt een Bestandenverkenner venster waarin u wordt gevraagd om een locatie voor de build. Maak een nieuwe map (door in de linkerbovenhoek op Nieuwe map te klikken) en geef deze de naam BUILDS.
Open de nieuwe map BUILDS en maak een andere map (opnieuw met behulp van Nieuwe map ) en geef deze de naam MR_Azure_Application_Insights.
Klik op Map selecterenterwijl de map MR_Azure_Application_Insights is geselecteerd. Het bouwen van het project duurt ongeveer een minuut.
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 in solution platformx86, Lokale computer.
Selecteer foutopsporing in de oplossingsconfiguratie.
Ga naar het menu Bouwen en klik op Oplossing implementeren om de toepassing naar uw computer te sideloaden.
Uw app wordt nu weergegeven in de lijst met geïnstalleerde apps, klaar om te worden gestart.
Start de mixed reality-toepassing.
Ga door de scène, benader objecten en bekijk ze. Wanneer de Azure Insight Service voldoende gebeurtenisgegevens heeft verzameld, wordt het object dat het meest is benaderd, ingesteld op groen.
Belangrijk
Hoewel de gemiddelde wachttijd voor het verzamelen van gebeurtenissen en metrische gegevens door de service ongeveer 15 minuten duurt, kan dit in sommige gevallen maximaal 1 uur duren.
Hoofdstuk 14- De Application Insights Service-portal
Zodra u door de scène hebt gezworven en naar verschillende objecten hebt gekeken, kunt u de gegevens bekijken die zijn verzameld in de Application Insights Service-portal .
Terug naar uw Application Insights Service-portal.
Selecteer Metrics Explorer.
Het wordt geopend op een tabblad met de grafiek, die de gebeurtenissen en metrische gegevens vertegenwoordigt die betrekking hebben op uw toepassing. Zoals hierboven vermeld, kan het enige tijd duren (tot 1 uur) voordat de gegevens in de grafiek worden weergegeven
Selecteer de balk Gebeurtenissen in het totaal aantal gebeurtenissen per toepassingsversie om een gedetailleerde uitsplitsing van de gebeurtenissen met hun namen te bekijken.
Uw Application Insights Service-toepassing 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 de ObjectInScene-objecten te spawnen in plaats van handmatig te maken en stel hun coördinaten in het vlak in uw scripts in. Op deze manier kunt u Azure vragen wat het populairste object was (hetzij van staren of nabijheidsresultaten) en een extra van deze objecten maken.
Oefening 2
Sorteer uw Application Insights-resultaten op tijd, zodat u de meest relevante gegevens krijgt en implementeer die tijdgevoelige gegevens in uw toepassing.