Urządzenia HoloLens (1. generacji) i Azure 309: Application Insights
Uwaga
Samouczki akademii rzeczywistości mieszanej zostały zaprojektowane z myślą o urządzeniach HoloLens (1. generacji) i zestawach słuchawkowych immersyjnych rzeczywistości mieszanej. W związku z tym uważamy, że ważne jest pozostawienie tych samouczków na miejscu dla deweloperów, którzy nadal szukają wskazówek dotyczących opracowywania tych urządzeń. Te samouczki nie zostaną zaktualizowane przy użyciu najnowszych zestawów narzędzi ani interakcji używanych dla urządzenia HoloLens 2. Będą one utrzymywane w celu kontynuowania pracy na obsługiwanych urządzeniach. W przyszłości zostanie opublikowana nowa seria samouczków, które pokażą, jak opracowywać urządzenia HoloLens 2. To powiadomienie zostanie zaktualizowane za pomocą linku do tych samouczków po ich opublikowaniu.
W tym kursie dowiesz się, jak dodać funkcje usługi Application Insights do aplikacji rzeczywistości mieszanej przy użyciu interfejsu API aplikacja systemu Azure Insights w celu zbierania analiz dotyczących zachowania użytkowników.
Application Insights to usługa firmy Microsoft, która umożliwia deweloperom zbieranie analiz ze swoich aplikacji i zarządzanie nią z łatwego w użyciu portalu. Analiza może być niczym, od wydajności do informacji niestandardowych, które chcesz zbierać. Aby uzyskać więcej informacji, odwiedź stronę usługi Application Insights.
Po ukończeniu tego kursu będziesz mieć aplikację immersywną rzeczywistości mieszanej, która będzie mogła wykonać następujące czynności:
- Zezwalaj użytkownikowi na patrzenie i poruszanie się po scenie.
- Wyzwól wysyłanie analiz do usługi Application Insights przy użyciu funkcji Gaze i Zbliżenia do obiektów w scenie.
- Aplikacja będzie również wywoływać usługę, pobierając informacje o tym, który obiekt został najbardziej zbliżył się do użytkownika w ciągu ostatnich 24 godzin. Ten obiekt zmieni kolor na zielony.
W tym kursie nauczysz się, jak uzyskać wyniki z usługi Application Insights w przykładowej aplikacji opartej na środowisku Unity. Zastosowanie tych pojęć do niestandardowej aplikacji, którą można utworzyć, będzie możliwe.
Obsługa urządzeń
Kurs | HoloLens | Immersyjne zestawy nagłowne |
---|---|---|
MR i Azure 309: Application Insights | ✔️ | ✔️ |
Uwaga
Chociaż ten kurs koncentruje się głównie na zestawach nagłownych immersywnych windows Mixed Reality (VR), możesz również zastosować to, czego nauczysz się w tym kursie do urządzenia Microsoft HoloLens. Zgodnie z kursem zobaczysz notatki dotyczące wszelkich zmian, które mogą być konieczne do zastosowania w celu obsługi urządzenia HoloLens. Podczas korzystania z urządzenia HoloLens można zauważyć echo podczas przechwytywania głosu.
Wymagania wstępne
Uwaga
Ten samouczek jest przeznaczony dla deweloperów, którzy mają podstawowe doświadczenie w językach Unity i C#. Należy również pamiętać, że wymagania wstępne i pisemne instrukcje zawarte w tym dokumencie reprezentują to, co zostało przetestowane i zweryfikowane w momencie pisania dokumentu (lipiec 2018 r.). Możesz bezpłatnie korzystać z najnowszego oprogramowania, jak wymieniono w artykule dotyczącym instalacji narzędzi , choć nie należy zakładać, że informacje zawarte w tym kursie doskonale pasują do tego, co znajdziesz w nowszym oprogramowaniu niż to, co zostało wymienione poniżej.
Na potrzeby tego kursu zalecamy następujące oprogramowanie i sprzęt:
- Komputer deweloperzy zgodny z windows Mixed Reality na potrzeby programowania immersyjnego zestawu słuchawkowego (VR)
- Windows 10 Fall Creators Update (lub nowszy) z włączonym trybem dewelopera
- Najnowszy zestaw SDK systemu Windows 10
- Unity 2017.4
- Visual Studio 2017
- Zestaw słuchawkowy windows Mixed Reality immersywny (VR) lub Microsoft HoloLens z włączonym trybem dewelopera
- Zestaw słuchawek z wbudowanym mikrofonem (jeśli zestaw słuchawkowy nie ma wbudowanego mikrofonu i głośników)
- Dostęp do Internetu dla konfiguracji platformy Azure i pobierania danych usługi Application Insights
Przed rozpoczęciem
Aby uniknąć problemów podczas kompilowania tego projektu, zdecydowanie zaleca się utworzenie projektu w tym samouczku w folderze głównym lub niemal głównym (długie ścieżki folderów mogą powodować problemy w czasie kompilacji).
Ostrzeżenie
Należy pamiętać, że dane przechodzące do usługi Application Insights zajmują trochę czasu, więc bądź cierpliwy. Jeśli chcesz sprawdzić, czy usługa otrzymała dane, zapoznaj się z rozdziałem 14, który pokaże, jak nawigować po portalu.
Rozdział 1 — Witryna Azure Portal
Aby korzystać z usługi Application Insights, należy utworzyć i skonfigurować usługę Application Insights w witrynie Azure Portal.
Zaloguj się do Portalu Azure.
Uwaga
Jeśli nie masz jeszcze konta platformy Azure, musisz je utworzyć. Jeśli obserwujesz ten samouczek w sytuacji w klasie lub laboratorium, poproś instruktora lub jednego z opiekunów o pomoc przy konfigurowaniu nowego konta.
Po zalogowaniu kliknij pozycję Nowy w lewym górnym rogu i wyszukaj ciąg Application Insights, a następnie kliknij przycisk Enter.
Uwaga
Wyraz Nowy mógł zostać zastąpiony ciągiem Create a resource (Utwórz zasób) w nowszych portalach.
Nowa strona po prawej stronie zawiera opis usługi aplikacja systemu Azure Insights. W lewym dolnym rogu tej strony wybierz przycisk Utwórz , aby utworzyć skojarzenie z tą usługą.
Po kliknięciu pozycji Utwórz:
Wstaw żądaną nazwę dla tego wystąpienia usługi.
W polu Typ aplikacji wybierz pozycję Ogólne.
Wybierz odpowiednią subskrypcję.
Wybierz grupę zasobów lub utwórz nową. Grupa zasobów umożliwia monitorowanie, kontrolowanie dostępu, aprowizowania i zarządzania rozliczeniami dla kolekcji zasobów platformy Azure. Zaleca się zachowanie wszystkich usług platformy Azure skojarzonych z pojedynczym projektem (na przykład takimi jak te kursy) w ramach wspólnej grupy zasobów.
Jeśli chcesz dowiedzieć się więcej na temat grup zasobów platformy Azure, odwiedź artykuł grupy zasobów.
Wybierz lokalizację.
Musisz również potwierdzić, że rozumiesz warunki i postanowienia zastosowane do tej usługi.
Wybierz pozycję Utwórz.
Po kliknięciu pozycji Utwórz trzeba będzie poczekać na utworzenie usługi. Może to potrwać minutę.
Po utworzeniu wystąpienia usługi w portalu zostanie wyświetlone powiadomienie.
Wybierz powiadomienia, aby eksplorować nowe wystąpienie usługi.
Kliknij przycisk Przejdź do zasobu w powiadomieniu, aby zapoznać się z nowym wystąpieniem usługi. Nastąpi przekierowanie do nowego wystąpienia usługi Application Insights.
Uwaga
Pozostaw tę stronę internetową otwartą i łatwą do uzyskania dostępu. W tym miejscu często będziesz widzieć zebrane dane.
Ważne
Aby zaimplementować usługę Application Insights, należy użyć trzech (3) określonych wartości: Klucz instrumentacji, Identyfikator aplikacji i Klucz interfejsu API. Poniżej zobaczysz, jak pobrać te wartości z usługi. Pamiętaj, aby zanotować te wartości na pustej stronie Notatnika , ponieważ będą one używane wkrótce w kodzie.
Aby znaleźć klucz instrumentacji, musisz przewinąć listę funkcji usługi i wybrać pozycję Właściwości, na wyświetlonej karcie zostanie wyświetlony klucz usługi.
Nieco poniżej pozycji Właściwości znajdziesz dostęp do interfejsu API, który należy kliknąć. Panel po prawej stronie udostępni identyfikator aplikacji aplikacji.
Po otwarciu panelu Identyfikator aplikacji kliknij pozycję Utwórz klucz interfejsu API, co spowoduje otwarcie panelu Tworzenie klucza interfejsu API.
W panelu Utwórz klucz interfejsu API wpisz opis i zaznacz trzy pola.
Kliknij pozycję Generuj klucz. Klucz interfejsu API zostanie utworzony i wyświetlony.
Ostrzeżenie
Jest to jedyny czas wyświetlania klucza usługi, więc upewnij się, że teraz utworzysz jego kopię.
Rozdział 2 . Konfigurowanie projektu aparatu Unity
Poniżej przedstawiono typową konfigurację programowania z rzeczywistością mieszaną, a w związku z tym jest to dobry szablon dla innych projektów.
Otwórz aparat Unity i kliknij pozycję Nowy.
Teraz musisz podać nazwę projektu aparatu Unity, wstawić MR_Azure_Application_Insights. Upewnij się, że szablon jest ustawiony na wartość 3D. Ustaw lokalizację na odpowiednią dla Ciebie (pamiętaj, że bliżej katalogów głównych jest lepiej). Następnie kliknij pozycję Utwórz projekt.
Po otwarciu aparatu Unity warto sprawdzić, czy domyślny edytor skryptów jest ustawiony na program Visual Studio. Przejdź do pozycji Edytuj > preferencje , a następnie w nowym oknie przejdź do pozycji Narzędzia zewnętrzne. Zmień edytor skryptów zewnętrznych na Visual Studio 2017. Zamknij okno Preferencje.
Następnie przejdź do pozycji Ustawienia kompilacji plików > i przełącz platformę na platforma uniwersalna systemu Windows, klikając przycisk Przełącz platformę.
Przejdź do pozycji Ustawienia kompilacji pliku > i upewnij się, że:
Urządzenie docelowe jest ustawione na Dowolne urządzenie
W przypadku urządzenia Microsoft HoloLens ustaw wartość Urządzenie docelowe na HoloLens.
Typ kompilacji jest ustawiony na D3D
Zestaw SDK jest ustawiony na najnowszą zainstalowaną
Kompilowanie i uruchamianie jest ustawione na komputer lokalny
Zapisz scenę i dodaj ją do kompilacji.
W tym celu wybierz pozycję Dodaj otwarte sceny. Zostanie wyświetlone okno zapisywania.
Utwórz nowy folder dla tego i dowolnego przyszłego sceny, a następnie kliknij przycisk Nowy folder, aby utworzyć nowy folder , nazwij go Sceny.
Otwórz nowo utworzony folder Sceny , a następnie w polu Nazwa pliku: tekst wpisz ApplicationInsightsScene, a następnie kliknij przycisk Zapisz.
Pozostałe ustawienia w obszarze Ustawienia kompilacji powinny być pozostawione jako domyślne na razie.
W oknie Ustawienia kompilacji wybierz pozycję Ustawienia odtwarzacza, spowoduje to otwarcie powiązanego panelu w przestrzeni, w której znajduje się inspektor.
W tym panelu należy zweryfikować kilka ustawień:
Na karcie Inne ustawienia:
Wersja środowiska uruchomieniowego skryptów powinna być eksperymentalna (odpowiednik platformy.NET 4.6), co spowoduje konieczność ponownego uruchomienia edytora.
Zaplecze skryptów powinno mieć wartość .NET
Poziom zgodności interfejsu API powinien mieć wartość .NET 4.6
Na karcie Ustawienia publikowania w obszarze Możliwości sprawdź:
InternetClient
W dalszej części panelu w obszarze Ustawienia XR (znajdujące się poniżej ustawień publikowania) zaznacz pole Virtual Reality Supported (Obsługiwana rzeczywistość wirtualna), upewnij się, że dodano zestaw WINDOWS Mixed Reality SDK.
Po powrocie do ustawień kompilacji projekty języka C# aparatu Unity nie są już wyszarzone; zaznacz pole wyboru obok tego.
Zamknij okno Build Settings (Ustawienia kompilacji).
Zapisz scenę i projekt (PLIK>ZAPISZ SCENĘ / PLIK>ZAPISZ PROJEKT).
Rozdział 3 . Importowanie pakietu aparatu Unity
Ważne
Jeśli chcesz pominąć konfigurowanie składników aparatu Unity w tym kursie i kontynuować bezpośrednio w kodzie, możesz pobrać ten pakiet Azure-MR-309.unitypackage, zaimportować go do projektu jako pakiet niestandardowy. Będzie również zawierać biblioteki DLL z następnego rozdziału. Po zaimportowaniu przejdź do rozdziału 6.
Ważne
Aby użyć usługi Application Insights w środowisku Unity, należy zaimportować dla niej bibliotekę DLL wraz z biblioteką DLL Newtonsoft. Obecnie w środowisku Unity występuje znany problem, który wymaga ponownego skonfigurowania wtyczek po zaimportowaniu. Te kroki (4–7 w tej sekcji) nie będą już wymagane po rozwiązaniu usterki.
Aby zaimportować usługę Application Insights do własnego projektu, upewnij się, że pobrano pakiet ".unitypackage" zawierający wtyczki. Następnie wykonaj następujące czynności:
Dodaj plik unitypackage** do aparatu Unity przy użyciu opcji menu Importuj pakiet niestandardowy pakietu > Assets>.
W wyświetlonym oknie Importuj pakiet aparatu Unity upewnij się, że wybrano wszystkie elementy w obszarze (i w tym) Wtyczki .
Kliknij przycisk Importuj, aby dodać elementy do projektu.
Przejdź do folderu Szczegółowe informacje w obszarze Wtyczki w widoku Projektu i wybierz tylko następujące wtyczki:
- Microsoft.ApplicationInsights
Po wybraniu tej wtyczki upewnij się, że wszystkie platformy są niezaznaczone, a następnie upewnij się, że WSAPlayer jest również niezaznaczone, a następnie kliknij przycisk Zastosuj. W tym celu wystarczy potwierdzić, że pliki są poprawnie skonfigurowane.
Uwaga
Oznaczanie wtyczek w następujący sposób powoduje skonfigurowanie ich do użycia tylko w edytorze aparatu Unity. Istnieje inny zestaw bibliotek DLL w folderze WSA, który będzie używany po wyeksportowaniu projektu z aparatu Unity.
Następnie należy otworzyć folder WSA w folderze Insights . Zobaczysz kopię tego samego skonfigurowanego pliku. Wybierz ten plik, a następnie w inspektorze upewnij się, że pole wyboru Dowolna platforma jest niezaznaczone, a następnie upewnij się, że zaznaczono tylko element WSAPlayer. Kliknij Zastosuj.
Teraz należy wykonać kroki 4–6, ale w przypadku wtyczek Newtonsoft . Zapoznaj się z poniższym zrzutem ekranu, aby dowiedzieć się, jak powinien wyglądać wynik.
Rozdział 4 . Konfigurowanie aparatu i kontrolek użytkownika
W tym rozdziale skonfigurujesz aparat i kontrolki, aby umożliwić użytkownikowi wyświetlanie i poruszanie się w scenie.
Kliknij prawym przyciskiem myszy pusty obszar w panelu hierarchii, a następnie w obszarze Utwórz>puste.
Zmień nazwę nowego pustego obiektu GameObject na Element nadrzędny aparatu.
Kliknij prawym przyciskiem myszy pusty obszar w panelu hierarchii, a następnie na obiekcie 3D, a następnie w obszarze Sphere.
Zmień nazwę sfery na prawą.
Ustaw skalę przekształcania prawej strony na 0.1, 0.1, 0.1, 0.1
Usuń składnik zderzacz sfery z prawej strony, klikając koło zębate w składniku zderzającego sferę, a następnie usuń składnik.
W panelu hierarchii przeciągnij obiekty Main Camera i Right Hand na obiekt nadrzędny aparatu fotograficznego.
Ustaw pozycję przekształcania zarówno głównego aparatu, jak i obiektu Prawa ręka na 0, 0, 0.
Rozdział 5 . Konfigurowanie obiektów w scenie aparatu Unity
Teraz utworzysz kilka podstawowych kształtów sceny, za pomocą których użytkownik może wchodzić w interakcje.
Kliknij prawym przyciskiem myszy pusty obszar w panelu hierarchii, a następnie w obszarze Obiekt 3D, a następnie wybierz pozycję Płaszczyzna.
Ustaw pozycję przekształcania płaszczyzny na 0, -1, 0.
Ustaw skalowanie płaszczyzny na 5, 1, 5.
Utwórz podstawowy materiał do użycia z obiektem Płaszczyzna , aby ułatwić wyświetlanie innych kształtów. Przejdź do panelu projektu, kliknij prawym przyciskiem myszy, a następnie pozycję Utwórz, a następnie pozycję Folder, aby utworzyć nowy folder. Nadaj jej nazwę Materiały.
Otwórz folder Materials, a następnie kliknij prawym przyciskiem myszy pozycję Utwórz, a następnie pozycję Materiał, aby utworzyć nowy materiał. Nadaj jej nazwę Niebieski.
Po wybraniu nowego materiału Niebieskiego przyjrzyj się inspektorowi i kliknij prostokątne okno obok Albedo. Wybierz niebieski kolor (jeden z poniższych obrazów to Szesnastkowy kolor: #3592FFFF). Kliknij przycisk Zamknij po wybraniu.
Przeciągnij nowy materiał z folderu Materials na nowo utworzoną płaszczyznę w scenie (lub upuść go na obiekcie Płaszczyzna w hierarchii).
Kliknij prawym przyciskiem myszy pusty obszar w panelu hierarchii, a następnie na obiekcie 3D Kapsuła.
- Po wybraniu opcji Kapsuła zmień położenie przekształcenia na: -10, 1, 0.
Kliknij prawym przyciskiem myszy pusty obszar w panelu hierarchii, a następnie w obszarze Obiekt 3D, Cube.
- Po wybraniu modułu zmień położenie przekształcenia na: 0, 0, 10.
Kliknij prawym przyciskiem myszy pusty obszar w panelu hierarchii, a następnie w obszarze Obiekt 3D, Sphere.
- Po wybraniu pozycji Sphere zmień położenie przekształcenia na: 10, 0, 0.
Uwaga
Te wartości pozycji to sugestie. Możesz ustawić położenie obiektów na dowolną wartość, chociaż jest to łatwiejsze dla użytkownika aplikacji, jeśli odległości obiektów nie są zbyt dalekie od aparatu.
Gdy aplikacja jest uruchomiona, musi być w stanie zidentyfikować obiekty w scenie, aby to osiągnąć, muszą być oznakowane. Wybierz jeden z obiektów, a następnie w panelu Inspector (Inspektor) kliknij przycisk Add Tag...( Dodaj tag...), co spowoduje zamianę inspektora na panel Tagi i warstwy.
Kliknij symbol + (plus), a następnie wpisz nazwę tagu jako ObjectInScene.
Ostrzeżenie
Jeśli używasz innej nazwy tagu, musisz upewnić się, że ta zmiana zostanie również zmieniona na dataFromAnalytics, ObjectTrigger i Gaze, skrypty później, aby obiekty zostały znalezione i wykryte w scenie.
Po utworzeniu tagu należy teraz zastosować go do wszystkich trzech obiektów. W hierarchii przytrzymaj Shift, a następnie kliknij kapsułę, moduł i sferę, obiekty, a następnie w inspektorze kliknij menu rozwijane obok pozycji Tag, a następnie kliknij utworzony tag ObjectInScene.
Rozdział 6 . Tworzenie klasy ApplicationInsightsTracker
Pierwszy skrypt, który należy utworzyć, to ApplicationInsightsTracker, który jest odpowiedzialny za:
Tworzenie zdarzeń na podstawie interakcji użytkownika w celu przesłania ich do usługi aplikacja systemu Azure Insights.
Tworzenie odpowiednich nazw zdarzeń w zależności od interakcji użytkownika.
Przesyłanie zdarzeń do wystąpienia usługi Application Insights.
Aby utworzyć tę klasę:
Kliknij prawym przyciskiem myszy w panelu projektu, a następnie utwórz>folder. Nadaj folderowi nazwę Scripts( Skrypty).
Po utworzeniu folderu Skrypty kliknij go dwukrotnie, aby go otworzyć. Następnie w tym folderze kliknij prawym przyciskiem myszy pozycję Utwórz>skrypt języka C#. Nadaj skryptowi nazwę ApplicationInsightsTracker.
Kliknij dwukrotnie nowy skrypt ApplicationInsightsTracker , aby otworzyć go za pomocą programu Visual Studio.
Zaktualizuj przestrzenie nazw w górnej części skryptu tak, aby był następujący:
using Microsoft.ApplicationInsights; using Microsoft.ApplicationInsights.DataContracts; using Microsoft.ApplicationInsights.Extensibility; using UnityEngine;
Wewnątrz klasy wstaw następujące zmienne:
/// <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;
Uwaga
Odpowiednio ustaw wartości instrumentationKey, applicationId i API_Key przy użyciu kluczy usługi z witryny Azure Portal, jak wspomniano w rozdziale 1, krok 9.
Następnie dodaj metody Start() i Awake(), które będą wywoływane podczas inicjowania klasy:
/// <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; }
Dodaj metody odpowiedzialne za wysyłanie zdarzeń i metryk zarejestrowanych przez aplikację:
/// <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); }
Przed powrotem do aparatu Unity pamiętaj o zapisaniu zmian w programie Visual Studio.
Rozdział 7 . Tworzenie skryptu okiem
Następnym skryptem do utworzenia jest skrypt gaze. Ten skrypt jest odpowiedzialny za utworzenie raycast, który będzie przewidywany do przodu z kamery głównej, aby wykryć, który obiekt patrzy użytkownik. W takim przypadku raycast będzie musiał określić, czy użytkownik patrzy na obiekt z tagiem ObjectInScene, a następnie zlicza, jak długo użytkownik patrzy na ten obiekt.
Kliknij dwukrotnie folder Skrypty, aby go otworzyć.
Kliknij prawym przyciskiem myszy wewnątrz folderu Scripts (Skrypty), kliknij polecenie Create C# Script (Utwórz>skrypt języka C#). Nadaj skryptowi nazwę Gaze.
Kliknij dwukrotnie skrypt, aby otworzyć go za pomocą programu Visual Studio.
Zastąp istniejący kod następującym kodem:
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; }
Teraz należy dodać kod dla metod Awake() i Start().
private void Awake() { // Set this class to behave similar to singleton Instance = this; _cursor = CreateCursor(); } void Start() { FocusedGameObject = null; } /// <summary> /// Create a cursor object, to provide what the user /// is looking at. /// </summary> /// <returns></returns> private GameObject CreateCursor() { GameObject newCursor = GameObject.CreatePrimitive(PrimitiveType.Sphere); // Remove the collider, so it does not block raycast. Destroy(newCursor.GetComponent<SphereCollider>()); newCursor.transform.localScale = new Vector3(0.1f, 0.1f, 0.1f); newCursor.GetComponent<MeshRenderer>().material.color = Color.HSVToRGB(0.0223f, 0.7922f, 1.000f); newCursor.SetActive(false); return newCursor; }
Wewnątrz klasy Gaze dodaj następujący kod w metodzie Update(), aby projektować raycast i wykrywać trafienie docelowe:
/// <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; } }
Dodaj metodę ResetFocusedObject(), aby wysyłać dane do usługi Application Insights , gdy użytkownik spojrzał na obiekt.
/// <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; } } }
Ukończono skrypt gazecie . Zapisz zmiany w programie Visual Studio przed powrotem do aparatu Unity.
Rozdział 8 . Tworzenie klasy ObjectTrigger
Następnym skryptem, który należy utworzyć, jest ObjectTrigger, który jest odpowiedzialny za:
- Dodawanie składników potrzebnych do kolizji z aparatem głównym.
- Wykrywanie, czy aparat znajduje się w pobliżu obiektu oznaczonego jako ObjectInScene.
Aby utworzyć skrypt:
Kliknij dwukrotnie folder Skrypty, aby go otworzyć.
Kliknij prawym przyciskiem myszy wewnątrz folderu Scripts (Skrypty), kliknij polecenie Create C# Script (Utwórz>skrypt języka C#). Nadaj skryptowi nazwę ObjectTrigger.
Kliknij dwukrotnie skrypt, aby otworzyć go za pomocą programu Visual Studio. Zastąp istniejący kod następującym kodem:
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); } } }
Przed powrotem do aparatu Unity pamiętaj o zapisaniu zmian w programie Visual Studio.
Rozdział 9 . Tworzenie klasy DataFromAnalytics
Teraz musisz utworzyć skrypt DataFromAnalytics , który jest odpowiedzialny za:
- Pobieranie danych analitycznych dotyczących tego, do którego obiektu najbardziej zwróciła się kamera.
- Używając kluczy usługi, które umożliwiają komunikację z wystąpieniem usługi aplikacja systemu Azure Insights.
- Sortowanie obiektów w scenie, zgodnie z którym ma najwyższą liczbę zdarzeń.
- Zmiana koloru materiału, najbardziej zbliżył się do obiektu, na zielony.
Aby utworzyć skrypt:
Kliknij dwukrotnie folder Skrypty, aby go otworzyć.
Kliknij prawym przyciskiem myszy wewnątrz folderu Scripts (Skrypty), kliknij polecenie Create C# Script (Utwórz>skrypt języka C#). Nadaj skryptowi nazwę DataFromAnalytics.
Kliknij dwukrotnie skrypt, aby otworzyć go za pomocą programu Visual Studio.
Wstaw następujące przestrzenie nazw:
using Newtonsoft.Json; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.Networking;
Wewnątrz skryptu wstaw następujące elementy:
/// <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(); }
W klasie DataFromAnalytics bezpośrednio po metodzie Start() dodaj następującą metodę o nazwie FetchAnalytics(). Ta metoda jest odpowiedzialna za wypełnianie listy par klucz-wartość z obiektem GameObject i numerem liczby zdarzeń symbolu zastępczego. Następnie inicjuje coroutine GetWebRequest(). Struktura zapytań wywołania do usługi Application Insights można również znaleźć w tej metodzie jako punkt końcowy adresu URL zapytania.
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)); } }
Tuż poniżej metody FetchAnalytics() dodaj metodę o nazwie GetWebRequest(), która zwraca moduł IEnumerator. Ta metoda jest odpowiedzialna za żądanie liczby wywołań zdarzenia odpowiadającego określonemu obiektowi GameObject w usłudze Application Insights. Po zwróceniu wszystkich wysłanych zapytań wywoływana jest metoda 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(); } } } }
Następną metodą jest DeterminWinner(), która sortuje listę par GameObject i Int , zgodnie z największą liczbą zdarzeń. Następnie zmienia kolor materiału tego obiektu GameObject na zielony (jako opinie o najwyższej liczbie). Spowoduje to wyświetlenie komunikatu z wynikami analizy.
/// <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); }
Dodaj strukturę klas, która będzie używana do deserializacji obiektu JSON otrzymanego z usługi Application Insights. Dodaj te klasy w dolnej części pliku klasy DataFromAnalytics poza definicją klasy.
/// <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; } }
Przed powrotem do aparatu Unity pamiętaj o zapisaniu zmian w programie Visual Studio.
Rozdział 10 — Tworzenie klasy Ruchu
Skrypt przenoszenia to następny skrypt, który należy utworzyć. Odpowiada za:
- Przesuwanie kamery głównej zgodnie z kierunkiem, w kierunku, w który patrzy kamera.
- Dodawanie wszystkich innych skryptów do obiektów sceny.
Aby utworzyć skrypt:
Kliknij dwukrotnie folder Skrypty, aby go otworzyć.
Kliknij prawym przyciskiem myszy wewnątrz folderu Scripts (Skrypty), kliknij polecenie Create C# Script (Utwórz>skrypt języka C#). Nazwij ruch skryptu.
Kliknij dwukrotnie skrypt, aby otworzyć go za pomocą programu Visual Studio.
Zastąp istniejący kod następującym kodem:
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() { } }
W klasie Movement poniżej pustej metody Update() wstaw następujące metody, które umożliwiają użytkownikowi przenoszenie się w przestrzeni wirtualnej za pomocą kontrolera ręki:
/// <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); }
Na koniec dodaj wywołanie metody w metodzie Update().
// Update is called once per frame void Update() { UpdateControllerState(); }
Przed powrotem do aparatu Unity pamiętaj o zapisaniu zmian w programie Visual Studio.
Rozdział 11 — Konfigurowanie odwołań do skryptów
W tym rozdziale należy umieścić skrypt ruchu na obiekcie nadrzędnym aparatu i ustawić jego elementy docelowe odniesienia. Ten skrypt będzie następnie obsługiwać umieszczanie innych skryptów, w których muszą być.
Z folderu Scripts (Skrypty) w panelu projektu przeciągnij skrypt Ruchu do obiektu nadrzędnego aparatu znajdującego się w panelu hierarchii.
Kliknij element nadrzędny aparatu. W panelu hierarchii przeciągnij obiekt Prawa ręka z Panelu hierarchii do obiektu docelowego odwołania, Kontroler, w Panelu inspektora. Ustaw wartość Szybkość użytkownika na 5, jak pokazano na poniższej ilustracji.
Rozdział 12 — Kompilowanie projektu aparatu Unity
Wszystko, co jest potrzebne w sekcji aparatu Unity tego projektu, zostało ukończone, więc nadszedł czas, aby skompilować go z poziomu aparatu Unity.
Przejdź do obszaru Ustawienia kompilacji (Ustawienia kompilacji pliku>).
W oknie Ustawienia kompilacji kliknij pozycję Kompiluj.
Zostanie wyświetlone okno Eksplorator plików z monitem o lokalizację kompilacji. Utwórz nowy folder (klikając pozycję Nowy folder w lewym górnym rogu) i nadaj mu nazwę BUILDS.
Otwórz nowy folder BUILDS i utwórz inny folder (ponownie użyj nowego folderu ) i nadaj mu nazwę MR_Azure_Application_Insights.
Po wybraniu folderu MR_Azure_Application_Insights kliknij pozycję Wybierz folder. Skompilowanie projektu potrwa minutę.
Po utworzeniu Eksplorator plików zostanie wyświetlona lokalizacja nowego projektu.
Rozdział 13 — Wdrażanie aplikacji MR_Azure_Application_Insights na maszynie
Aby wdrożyć aplikację MR_Azure_Application_Insights na komputerze lokalnym:
Otwórz plik rozwiązania aplikacji MR_Azure_Application_Insights w programie Visual Studio.
W polu Platforma rozwiązania wybierz pozycję x86, Komputer lokalny.
W obszarze Konfiguracja rozwiązania wybierz pozycję Debuguj.
Przejdź do menu Kompilacja i kliknij pozycję Wdróż rozwiązanie, aby załadować aplikację bezpośrednio na maszynę.
Aplikacja powinna być teraz wyświetlana na liście zainstalowanych aplikacji gotowych do uruchomienia.
Uruchom aplikację rzeczywistości mieszanej.
Poruszanie się po scenie, zbliżanie się do obiektów i przyjrzenie się im, gdy usługa Azure Insight Service zebrała wystarczającą ilość danych zdarzenia, ustawi obiekt, który był najbardziej zielony.
Ważne
Podczas gdy średni czas oczekiwania na zbieranie zdarzeń i metryk przez usługę trwa około 15 minut, w niektórych przypadkach może upłynąć do 1 godziny.
Rozdział 14 — portal usługi Application Insights
Po przejeżdżeniu po scenie i spojrzeniu na kilka obiektów można zobaczyć dane zebrane w portalu usługi Application Insights.
Wróć do portalu usługi Application Insights.
Wybierz pozycję Eksplorator metryk.
Zostanie on otwarty na karcie zawierającej graf, który reprezentuje zdarzenia i metryki związane z aplikacją. Jak wspomniano powyżej, wyświetlenie danych na wykresie może potrwać do 1 godziny
Wybierz pasek Zdarzenia w obszarze Total of Events by Application Version (Łączna liczba zdarzeń według wersji aplikacji), aby wyświetlić szczegółowy podział zdarzeń o ich nazwach.
Ukończono aplikację usługi Application Insights Service
Gratulacje, utworzono aplikację rzeczywistości mieszanej, która korzysta z usługi Application Insights do monitorowania aktywności użytkownika w aplikacji.
Ćwiczenia bonusowe
Ćwiczenie 1
Spróbuj zduplikować, a nie ręcznie utworzyć obiekty ObjectInScene i ustawić ich współrzędne na płaszczyźnie w skryptach. W ten sposób można zapytać platformę Azure, jaki był najbardziej popularny obiekt (z spojrzenia lub wyników sąsiedztwa) i zduplikować dodatkowy obiekt.
Ćwiczenie 2
Posortuj wyniki usługi Application Insights według czasu, aby uzyskać najbardziej odpowiednie dane i zaimplementować te poufne dane w aplikacji.