Urządzenia HoloLens (1. generacji) i Azure 309: Application Insights
Uwaga
Samouczki Mixed Reality Academy zostały zaprojektowane z myślą o urządzeniach HoloLens (1. generacji) i Mixed Reality immersywnych zestawów słuchawkowych. W związku z tym uważamy, że ważne jest pozostawienie tych samouczków w miejscu dla deweloperów, którzy nadal szukają wskazówek dotyczących opracowywania dla tych urządzeń. Te samouczki nie zostaną zaktualizowane o najnowsze zestawy narzędzi ani interakcje używane na potrzeby 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ć na potrzeby 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 usługi 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 poziomu łatwego w użyciu portalu. Analiza może być niczym — od wydajności do niestandardowych informacji, 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.
- Wyzwalanie wysyłania 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 aplikacji aparatu Unity. Zastosowanie tych pojęć do aplikacji niestandardowej, 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 Windows Mixed Reality immersywnych zestawach słuchawkowych (VR), możesz również zastosować to, czego uczysz się w tym kursie, aby Microsoft HoloLens. Wraz 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ć pewne 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ący sprzęt i oprogramowanie:
- Komputer deweloperów, zgodny z Windows Mixed Reality na potrzeby opracowywania immersywnych zestawów słuchawkowych (VR)
- Windows 10 Fall Creators Update (lub nowszy) z włączonym trybem dewelopera
- Najnowszy zestaw SDK 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 na potrzeby instalacji 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 Ci, 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 Azure Portal.
Zaloguj się do Portalu Azure.
Uwaga
Jeśli nie masz jeszcze konta platformy Azure, musisz je utworzyć. Jeśli korzystasz z tego samouczka w sytuacji na potrzeby zajęć 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 przycisk 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ąć w dół 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 .
Po otwarciu panelu Identyfikator aplikacji kliknij pozycję Utwórz klucz interfejsu API, co spowoduje otwarcie panelu Tworzenie klucza interfejsu API .
W panelu Tworzenie klucza 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 , dlatego upewnij się, że teraz utworzysz kopię.
Rozdział 2 . Konfigurowanie projektu aparatu Unity
Poniżej przedstawiono typową konfigurację do opracowywania za pomocą rzeczywistości mieszanej, 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 dla szablonu ustawiono 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 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.
Zrób to, wybierając pozycję Dodaj otwarte sceny. Zostanie wyświetlone okno zapisywania.
Utwórz nowy folder dla tego i dowolnej przyszłej sceny, a następnie kliknij przycisk Nowy folder , aby utworzyć nowy folder, nadaj mu nazwę 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 obszarze, w którym znajduje się inspektor .
W tym panelu należy zweryfikować kilka ustawień:
Na karcie Inne ustawienia :
Wersja środowiska uruchomieniowegoskryptó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 opcję Obsługa rzeczywistości wirtualnej, upewnij się, że dodano zestaw SDK Windows Mixed Reality.
Po powrocie do ustawień kompilacjiprojekty 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 to 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 istnieje znany problem w środowisku Unity, 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 oknie Importuj pakiet aparatu Unity wyskakującym upewnij się, że wybrano wszystkie elementy w obszarze (i włącznie) Wtyczki .
Kliknij przycisk Importuj , aby dodać elementy do projektu.
Przejdź do folderu Insights 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 program WSAPlayer jest również niezaznaczone, a następnie kliknij przycisk Zastosuj. W tym celu wystarczy potwierdzić, że pliki są poprawnie skonfigurowane.
Uwaga
Oznaczanie wtyczek, takich jak to, konfiguruje je 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 pliku, który został skonfigurowany. Wybierz ten plik, a następnie w inspektorze upewnij się, że pole wyboru Dowolna platforma jest niezaznaczone, a następnie upewnij się, że zaznaczono tylkoelement WSAPlayer. Kliknij pozycję Zastosuj.
Teraz musisz wykonać kroki od 4 do 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 przenoszenie 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 zderzacz sfery , a następnie usuń składnik.
W panelu hierarchii przeciągnij aparat główny i obiekty po prawej stronie do obiektu Nadrzędnego aparatu.
Ustaw pozycję przekształcania zarówno głównego aparatu, jak i obiektu prawego ręki na 0, 0, 0.
Rozdział 5 . Konfigurowanie obiektów w scenie aparatu Unity
Teraz utworzysz kilka podstawowych kształtów dla sceny, z którymi użytkownik może korzystać.
Kliknij prawym przyciskiem myszy pusty obszar w panelu hierarchii, a następnie w obiekcie 3D, a następnie wybierz pozycję Płaszczyzna.
Ustaw pozycję Przekształcanie płaszczyzny na 0, -1, 0.
Ustaw skalę przekształcenia płaszczyzny na 5, 1, 5.
Utwórz podstawowy materiał do użycia z obiektem Płaszczyzna , dzięki czemu inne kształty są łatwiejsze do zobaczenia. 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 Materiały , a następnie kliknij prawym przyciskiem myszy pozycję Utwórz, a następnie pozycję Materiał, aby utworzyć nowy materiał. Nadaj mu 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 Kolor szesnastkowy: #3592FFFF). Po wybraniu przycisku Zamknij kliknij przycisk Zamknij.
Przeciągnij nowy materiał z folderu Materiały 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 pozycji Kapsuła zmień pozycję przekształcaniana:-10, 1, 0.
Kliknij prawym przyciskiem myszy pusty obszar w panelu hierarchii, a następnie na obiekcie 3D, module.
- Po wybraniu pozycji Moduł zmień pozycję przekształcaniana: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ń pozycję przekształcaniana: 10, 0, 0.
Uwaga
Te wartości pozycji są sugestiami. Możesz ustawić pozycje obiektów na dowolną wartość, choć jest to łatwiejsze dla użytkownika aplikacji, jeśli odległości obiektów nie są zbyt daleko od aparatu.
Gdy aplikacja jest uruchomiona, musi być w stanie zidentyfikować obiekty w scenie, aby to osiągnąć, muszą zostać otagowane. Wybierz jeden z obiektów, a następnie w panelu Inspektor kliknij przycisk Dodaj tag..., co spowoduje zamianę inspektora na panel Tagi & 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ż wprowadzona w obszarze 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 klawisz Shift , a następnie kliknij utworzony tag Capsule, Cube i Sphere, 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 przesyłania 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 panel projektu, a następnie pozycję Utwórz>folder. Nadaj nazwę skryptom folderu.
Po utworzeniu folderu Scripts 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 znajdowały się w następujący sposób:
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
Ustaw odpowiednio 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); }
Pamiętaj, aby zapisać zmiany w programie Visual Studio przed powrotem do aparatu Unity.
Rozdział 7 . Tworzenie skryptu uczenia
Następnym skryptem do utworzenia jest skrypt gazecie . Ten skrypt jest odpowiedzialny za utworzenie raycastu , 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 Scripts (Skrypty ), aby go otworzyć.
Kliknij prawym przyciskiem myszy wewnątrz folderu Scripts (Skrypty ), kliknij pozycję Create C# Script ( Utwórz>skrypt języka C#). Nadaj nazwę gazecie skryptu.
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; }
Należy teraz 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 wykryć 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ępny skrypt, który należy utworzyć, to ObjectTrigger, który jest odpowiedzialny za:
- Dodanie składników wymaganych do kolizji z aparatem głównym.
- Wykrywanie, czy aparat znajduje się w pobliżu obiektu oznaczonego jako ObjectInScene.
Aby utworzyć skrypt:
Kliknij dwukrotnie folder Scripts (Skrypty ), aby go otworzyć.
Kliknij prawym przyciskiem myszy wewnątrz folderu Scripts (Skrypty ), kliknij pozycję 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); } } }
Pamiętaj, aby zapisać zmiany w programie Visual Studio przed powrotem do aparatu Unity.
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 zbliżył się aparat.
- Przy użyciu 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 Scripts (Skrypty ), aby go otworzyć.
Kliknij prawym przyciskiem myszy wewnątrz folderu Scripts (Skrypty ), kliknij pozycję 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 wartości klucza z obiektem GameObject i numerem liczby zdarzeń symbolu zastępczego. Następnie inicjuje koprocedurynę GetWebRequest(). Struktura zapytań wywołania 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 DetermineWinner(), która sortuje listę par GameObject i Int , zgodnie z największą liczbą zdarzeń. Następnie zmienia kolor materiału 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 DataFromAnalyticspoza 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; } }
Pamiętaj, aby zapisać zmiany w programie Visual Studio przed powrotem do aparatu Unity.
Rozdział 10 — Tworzenie klasy Movement
Skrypt przenoszenia to kolejny 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 Scripts ( Skrypty ), aby go otworzyć.
Kliknij prawym przyciskiem myszy wewnątrz folderu Scripts (Skrypty), a następnie kliknij polecenie CreateC# Script (Utwórz > skrypt języka C#). Nadaj skryptowi nazwę Movement.
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 Movementponiżej pustej metody Update() wstaw następujące metody, które umożliwiają użytkownikowi przenoszenie się w przestrzeni wirtualnej za pomocą kontrolera ręcznego:
/// <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(); }
Pamiętaj, aby zapisać zmiany w programie Visual Studio przed powrotem do aparatu Unity.
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 tam, gdzie muszą być.
Z folderu Scripts (Skrypty ) w panelu projektu przeciągnij skrypt Movement do obiektu nadrzędnego aparatu znajdującego się w panelu hierarchii.
Kliknij pozycję Aparat nadrzędny. 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 — Tworzenie 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 kompilacjipliku>).
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 ponownie utwórz inny folder (przy użyciu nowego folderu ) i nadaj mu nazwę MR_Azure_Application_Insights.
Po wybraniu folderu MR_Azure_Application_Insights kliknij pozycję Wybierz folder. Kompilacja projektu może potrwać kilka minut.
Po utworzeniuEksplorator 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.
Poruszaj się po scenie, zbliżając się do obiektów i patrząc na nie, gdy usługa Azure Insight Service zebrała wystarczającą ilość danych zdarzeń, 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 to potrwać do 1 godziny.
Rozdział 14 — portal usługi Application Insights
Po wędrowania po scenie i przyjrzeniu się kilku obiektom można zobaczyć dane zebrane w portalu usługi Application Insights .
Wstecz do portalu usługi Application Insights.
Wybierz pozycję Eksplorator metryk.
Zostanie on otwarty na karcie zawierającej graf, który reprezentuje zdarzenia i metryki powią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
Gratulacje, utworzono aplikację rzeczywistości mieszanej, która korzysta z usługi Application Insights do monitorowania aktywności użytkownika w aplikacji.
Ćwiczenia dodatkowe
Ć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 (na podstawie spojrzenia lub wyników zbliżeniowych) i zduplikować dodatkowy obiekt.
Ćwiczenie 2
Posortuj wyniki usługi Application Insights według czasu, aby uzyskać najbardziej odpowiednie dane i zaimplementować te dane poufne czasowo w aplikacji.