HoloLens (1. gen) i Azure 311 — Microsoft Graph

Uwaga

Samouczki dotyczące akademii Mixed Reality 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 tych urządzeń. Te samouczki nie zostaną zaktualizowane przy użyciu najnowszych zestawów narzędzi ani interakcji używanych do 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ć HoloLens 2. To powiadomienie zostanie zaktualizowane za pomocą linku do tych samouczków po ich opublikowaniu.

Z tego kursu dowiesz się, jak logować się do konta Microsoft za pomocą programu Microsoft Graph przy użyciu bezpiecznego uwierzytelniania w aplikacji rzeczywistości mieszanej. Następnie pobierzesz i wyświetlisz zaplanowane spotkania w interfejsie aplikacji.

Zrzut ekranu przedstawiający zaplanowane spotkania w interfejsie aplikacji.

Microsoft Graph to zestaw interfejsów API przeznaczonych do umożliwienia dostępu do wielu usług firmy Microsoft. Firma Microsoft opisuje program Microsoft Graph jako macierz zasobów połączonych relacjami, co oznacza, że umożliwia aplikacji dostęp do wszystkich rodzajów połączonych danych użytkownika. Aby uzyskać więcej informacji, odwiedź stronę programu Microsoft Graph.

Programowanie będzie obejmować tworzenie aplikacji, w której użytkownik zostanie poinstruowany, aby spojrzeć na, a następnie nacisnąć sferę, co spowoduje monit użytkownika o bezpieczne zalogowanie się do konta Microsoft. Po zalogowaniu się na swoje konto użytkownik będzie mógł wyświetlić listę spotkań zaplanowanych na ten dzień.

Po ukończeniu tego kursu będziesz mieć aplikację HoloLens rzeczywistości mieszanej, która będzie mogła wykonać następujące czynności:

  1. Za pomocą gestu Naciśnij naciśnij obiekt, który wyświetli monit użytkownika o zalogowanie się do konta Microsoft (przejście z aplikacji w celu zalogowania się, a następnie powrót do aplikacji).
  2. Wyświetl listę spotkań zaplanowanych na dzień.

W aplikacji należy do Ciebie, jak zintegrować wyniki z projektem. Ten kurs został zaprojektowany, aby nauczyć cię, jak zintegrować usługę platformy Azure z projektem aparatu Unity. Twoim zadaniem jest wykorzystanie wiedzy uzyskanych z tego kursu w celu ulepszenia aplikacji rzeczywistości mieszanej.

Obsługa urządzeń

Kurs HoloLens Immersyjne zestawy słuchawkowe
MR i Azure 311: Microsoft Graph ✔️

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). Możesz bezpłatnie korzystać z najnowszego oprogramowania, jak pokazano w artykule dotyczącym instalacji narzędzi , choć nie należy zakładać, że informacje w tym kursie będą doskonale zgodne z tym, co znajdziesz w nowszym oprogramowaniu niż wymienione poniżej.

Na potrzeby tego kursu zalecamy następujące oprogramowanie i sprzęt:

Przed rozpoczęciem

  1. Aby uniknąć problemów z kompilowaniem tego projektu, zdecydowanie zaleca się utworzenie projektu wymienionego w tym samouczku w folderze głównym lub niemal głównym (długie ścieżki folderów mogą powodować problemy w czasie kompilacji).
  2. Skonfiguruj i przetestuj urządzenie HoloLens. Jeśli potrzebujesz pomocy technicznej dotyczącej konfigurowania urządzenia HoloLens, zapoznaj się z artykułem dotyczącym konfiguracji urządzenia HoloLens.
  3. Dobrym pomysłem jest wykonanie kalibracji i dostrajania czujników podczas tworzenia nowej aplikacji HoloLens (czasami może to pomóc w wykonywaniu tych zadań dla każdego użytkownika).

Aby uzyskać pomoc dotyczącą kalibracji, skorzystaj z tego linku do artykułu Kalibracja urządzenia HoloLens.

Aby uzyskać pomoc dotyczącą dostrajania czujników, skorzystaj z tego linku do artykułu Dostrajanie czujników holoLens.

Rozdział 1 . Tworzenie aplikacji w portalu rejestracji aplikacji

Aby rozpocząć pracę, musisz utworzyć i zarejestrować aplikację w portalu rejestracji aplikacji.

W tym rozdziale znajdziesz również klucz usługi, który umożliwi wykonywanie połączeń z programem Microsoft Graph w celu uzyskania dostępu do zawartości konta.

  1. Przejdź do portalu rejestracji aplikacji firmy Microsoft i zaloguj się przy użyciu konta Microsoft. Po zalogowaniu nastąpi przekierowanie do portalu rejestracji aplikacji.

  2. W sekcji Moje aplikacje kliknij przycisk Dodaj aplikację.

    Zrzut ekranu pokazujący, gdzie wybrać pozycję Dodaj aplikację.

    Ważne

    Portal rejestracji aplikacji może wyglądać inaczej, w zależności od tego, czy wcześniej pracowałeś z programem Microsoft Graph. Poniższe zrzuty ekranu przedstawiają te różne wersje.

  3. Dodaj nazwę aplikacji i kliknij przycisk Utwórz.

    Zrzut ekranu przedstawiający miejsce dodania nazwy aplikacji.

  4. Po utworzeniu aplikacji nastąpi przekierowanie do strony głównej aplikacji. Skopiuj identyfikator aplikacji i pamiętaj, aby zanotować tę wartość w bezpiecznym miejscu. Użyjesz jej wkrótce w kodzie.

    Zrzut ekranu przedstawiający miejsce wyświetlania identyfikatora aplikacji.

  5. W sekcji Platformy upewnij się, że jest wyświetlana aplikacja natywna . Jeśli nie kliknij pozycji Dodaj platformę i wybierz pozycję Aplikacja natywna.

    Zrzut ekranu przedstawiający sekcję Aplikacja natywna.

  6. Przewiń w dół na tej samej stronie i w sekcji o nazwie Uprawnienia programu Microsoft Graph musisz dodać dodatkowe uprawnienia dla aplikacji. Kliknij pozycję Dodaj obok pozycji Uprawnienia delegowane.

    Zrzut ekranu pokazujący, gdzie wybrać pozycję Dodaj obok pozycji Uprawnienia delegowane.

  7. Ponieważ aplikacja ma uzyskać dostęp do kalendarza użytkownika, zaznacz pole wyboru o nazwie Calendars.Read i kliknij przycisk OK.

    Zrzut ekranu przedstawiający pole wyboru Calendars.Read.

  8. Przewiń w dół i kliknij przycisk Zapisz .

    Zrzut ekranu pokazujący, gdzie wybrać pozycję Zapisz.

  9. Zapisanie zostanie potwierdzone i możesz wylogować się z portalu rejestracji aplikacji.

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.

  1. Otwórz aparat Unity i kliknij pozycję Nowy.

    Zrzut ekranu przedstawiający interfejs aparatu Unity.

  2. Musisz podać nazwę projektu aparatu Unity. Wstaw msGraphMR. Upewnij się, że szablon projektu ma ustawioną 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.

    Zrzut ekranu pokazujący, gdzie wybrać pozycję Utwórz projekt.

  3. Po otwarciu aparatu Unity warto sprawdzić domyślny edytor skryptów 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 .

    Zrzut ekranu pokazujący, gdzie ustawić edytor skryptów zewnętrznych na Visual Studio 2017.

  4. Przejdź dopozycji Ustawienia kompilacjiplików> i wybierz pozycję platforma uniwersalna systemu Windows, a następnie kliknij przycisk Przełącz platformę, aby zastosować wybór.

    Zrzut ekranu pokazujący, gdzie wybrać pozycję Przełącz platformę.

  5. Nadal wobszarze Ustawienia kompilacjipliku> upewnij się, że:

    1. Urządzenie docelowe ma ustawioną wartość HoloLens

    2. Typ kompilacji jest ustawiony na D3D

    3. Zestaw SDK jest ustawiony na najnowszą zainstalowaną

    4. Dla wersji programu Visual Studio jest ustawiona wartość Najnowsza zainstalowana

    5. Kompilowanie i uruchamianie jest ustawione na maszynę lokalną

    6. Zapisz scenę i dodaj ją do kompilacji.

      1. W tym celu wybierz pozycję Dodaj otwarte sceny. Zostanie wyświetlone okno zapisywania.

        Zrzut ekranu pokazujący, gdzie wybrać pozycję Dodaj otwarte sceny.

      2. Utwórz nowy folder dla tego i każdej przyszłej sceny. Wybierz przycisk Nowy folder , aby utworzyć nowy folder, nadaj mu nazwę Sceny.

        Zrzut ekranu pokazujący, gdzie nazwać nowy folder.

      3. Otwórz nowo utworzony folder Sceny , a następnie w polu Nazwa pliku: tekst wpisz MR_ComputerVisionScene, a następnie kliknij przycisk Zapisz.

        Zrzut ekranu przedstawiający miejsce wpisywania nazwy pliku.

        Ważne

        Pamiętaj, że musisz zapisać sceny aparatu Unity w folderze Assets , ponieważ muszą być skojarzone z projektem aparatu Unity. Tworzenie folderu scen (i innych podobnych folderów) to typowy sposób tworzenia struktury projektu aparatu Unity.

    7. Pozostałe ustawienia w obszarze Ustawienia kompilacji powinny być teraz pozostawione jako domyślne.

  6. W oknie Ustawienia kompilacji kliknij przycisk Ustawienia odtwarzacza . Spowoduje to otwarcie powiązanego panelu w przestrzeni, w której znajduje się inspektor .

    Zrzut ekranu przedstawiający okno dialogowe Ustawienia odtwarzacza.

  7. W tym panelu należy zweryfikować kilka ustawień:

    1. Na karcie Inne ustawienia :

      1. Wersja środowiska uruchomieniowegoskryptów powinna być eksperymentalna (odpowiednik platformy .NET 4.6), co spowoduje konieczność ponownego uruchomienia edytora.

      2. Tworzenie skryptów zaplecza powinno mieć wartość .NET

      3. Poziom zgodności interfejsu API powinien mieć wartość .NET 4.6

        Zrzut ekranu pokazujący, gdzie sprawdzić poziom zgodności interfejsu API.

    2. Na karcie Ustawienia publikowania w obszarze Możliwości sprawdź:

      • InternetClient

        Zrzut ekranu pokazujący, gdzie wybrać opcję InternetClient.

    3. W dalszej części panelu, w obszarze Ustawienia XR (znajdujące się poniżej pozycji Ustawienia publikowania), zaznacz pozycję Obsługa rzeczywistości wirtualnej, upewnij się, że zestaw SDK Windows Mixed Reality został dodany.

      Zrzut ekranu przedstawiający miejsce dodawania zestawu SDK Windows Mixed Reality.

  8. Po powrocie do ustawień kompilacjiprojekty Unity C# nie są już wyszarzone; zaznacz pole wyboru obok tego pola wyboru.

  9. Zamknij okno Build Settings (Ustawienia kompilacji).

  10. Zapisz scenę i projekt (PLIK>ZAPISZ SCENY /PLIK ZAPISZ>PROJEKT).

Rozdział 3 — Importowanie bibliotek w a unity

Ważne

Jeśli chcesz pominąć składnik konfiguracji aparatu Unity w tym kursie i kontynuować bezpośrednio w kodzie, możesz pobrać pakiet Azure-MR-311.unitypackage, zaimportować go do projektu jako pakiet niestandardowy, a następnie kontynuować z rozdziału 5.

Aby korzystać z programu Microsoft Graph w środowisku Unity, należy użyć biblioteki DLL Microsoft.Identity.Client . Można jednak użyć zestawu SDK programu Microsoft Graph, jednak po utworzeniu projektu aparatu Unity będzie wymagane dodanie pakietu NuGet (co oznacza edytowanie projektu po kompilacji). Jest uważany za prostszy do importowania wymaganych bibliotek DLL bezpośrednio do aparatu Unity.

Uwaga

Obecnie istnieje znany problem w a aparatu 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ć program Microsoft Graph do własnego projektu, pobierz plik MSGraph_LabPlugins.zip. Ten pakiet został utworzony z wersjami bibliotek, które zostały przetestowane.

Jeśli chcesz dowiedzieć się więcej na temat dodawania niestandardowych bibliotek DLL do projektu aparatu Unity, użyj tego linku.

Aby zaimportować pakiet:

  1. Dodaj pakiet aparatu Unity do aparatu Unity przy użyciu opcji menuImportuj pakiet>niestandardowy pakietu Assets>. Wybierz właśnie pobrany pakiet.

  2. W wyświetlonym oknie Import Unity Package (Importuj pakiet aparatu Unity ) upewnij się, że wybrano wszystkie elementy w obszarze (i w tym) Wtyczki .

    Zrzut ekranu przedstawiający wybrane parametry konfiguracji w obszarze Wtyczki.

  3. Kliknij przycisk Importuj , aby dodać elementy do projektu.

  4. Przejdź do folderu MSGraph w obszarze Wtyczki w panelu projektu i wybierz wtyczkę o nazwie Microsoft.Identity.Client.

    Zrzut ekranu przedstawiający wtyczkę Microsoft.Identity.Client.

  5. Po wybraniu wtyczki upewnij się, że pole wyboru Dowolna platforma jest niezaznaczone, a następnie upewnij się, że w programie WSAPlayer nie zaznaczono również pola wyboru, a następnie kliknij przycisk Zastosuj. Jest to tylko potwierdzenie, że pliki są poprawnie skonfigurowane.

    Zrzut ekranu pokazujący, gdzie potwierdzić, że nie zaznaczono opcji Dowolna platforma i WSAPlayer.

    Uwaga

    Oznaczanie tych wtyczek konfiguruje je tak, aby były używane 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 jako aplikacji uniwersalnej systemu Windows.

  6. Następnie należy otworzyć folder WSA w folderze MSGraph . Zobaczysz kopię tego samego pliku, który został właśnie skonfigurowany. Wybierz plik, a następnie w inspektorze:

    • upewnij się, że opcja Dowolna platforma nie jest zaznaczona i zaznaczono tylkoWSAPlayer.

    • Upewnij się, że zestaw SDK jest ustawiony na platformę UWP, a zaplecze skryptów ma ustawioną wartość Dot Net

    • Upewnij się, że nie zaznaczono wyboruNie przetwarzaj.

      Zrzut ekranu przedstawiający wybranie opcji Nie przetwarzaj.

  7. Kliknij pozycję Zastosuj.

Rozdział 4 — Konfiguracja aparatu

W tym rozdziale skonfigurujesz kamerę główną sceny:

  1. W panelu hierarchii wybierz kamerę główną.

  2. Po wybraniu tej opcji będzie można wyświetlić wszystkie składniki głównego aparatu w panelu Inspektor .

    1. Obiekt Camera musi mieć nazwę Main Camera (zanotuj pisownię!)

    2. Główny tag aparatu musi być ustawiony na MainCamera (zanotuj pisownię!)

    3. Upewnij się, że pozycja przekształcenia jest ustawiona na 0, 0, 0

    4. Ustaw opcję Wyczyść flagi na kolor stały

    5. Ustaw kolor tła składnika aparatu na Czarny, Alfa 0(Kod szesnastkowy: #00000000)

      Zrzut ekranu przedstawiający miejsce ustawiania koloru tła.

  3. Ostateczna struktura obiektu w panelu hierarchii powinna przypominać strukturę pokazaną na poniższej ilustracji:

    Zrzut ekranu przedstawiający ostateczną strukturę obiektu w panelu hierarchii.

Rozdział 5 — Tworzenie klasy MeetingsUI

Pierwszy skrypt, który należy utworzyć, to MeetingUI, który jest odpowiedzialny za hostowanie i wypełnianie interfejsu użytkownika aplikacji (wiadomość powitalna, instrukcje i szczegóły spotkań).

Aby utworzyć tę klasę:

  1. Kliknij prawym przyciskiem myszy folder Assets w panelu projektu, a następnie wybierz pozycję Utwórz>folder. Nadaj folderowi nazwę Scripts( Skrypty).

    Zrzut ekranu przedstawiający miejsce znalezienia folderu Assets.Zrzut ekranu pokazujący, gdzie utworzyć folder Scripts.

  2. Otwórz folder Scripts (Skrypty), a następnie w tym folderze kliknij prawym przyciskiem myszy pozycję CreateC# Script (Utwórz > skrypt języka C#). Nadaj skryptowi nazwę MeetingsUI.

    Zrzut ekranu pokazujący, gdzie utworzyć folder MeetingsUI.

  3. Kliknij dwukrotnie nowy skrypt MeetingsUI , aby otworzyć go za pomocą programu Visual Studio.

  4. Wstaw następujące przestrzenie nazw:

    using System;
    using UnityEngine;
    
  5. Wewnątrz klasy wstaw następujące zmienne:

        /// <summary>
        /// Allows this class to behave like a singleton
        /// </summary>
        public static MeetingsUI Instance;
    
        /// <summary>
        /// The 3D text of the scene
        /// </summary>
        private TextMesh _meetingDisplayTextMesh;
    
  6. Następnie zastąp metodę Start() i dodaj metodę Awake(). Będą one wywoływane, gdy klasa inicjuje:

        /// <summary>
        /// Called on initialization
        /// </summary>
        void Awake()
        {
            Instance = this;
        }
    
        /// <summary>
        /// Called on initialization, after Awake
        /// </summary>
        void Start ()
        {
            // Creating the text mesh within the scene
            _meetingDisplayTextMesh = CreateMeetingsDisplay();
        }
    
  7. Dodaj metody odpowiedzialne za utworzenie interfejsu użytkownika spotkań i wypełnij je bieżącymi spotkaniami po zażądaniu:

        /// <summary>
        /// Set the welcome message for the user
        /// </summary>
        internal void WelcomeUser(string userName)
        {
            if(!string.IsNullOrEmpty(userName))
            {
                _meetingDisplayTextMesh.text = $"Welcome {userName}";
            }
            else 
            {
                _meetingDisplayTextMesh.text = "Welcome";
            }
        }
    
        /// <summary>
        /// Set up the parameters for the UI text
        /// </summary>
        /// <returns>Returns the 3D text in the scene</returns>
        private TextMesh CreateMeetingsDisplay()
        {
            GameObject display = new GameObject();
            display.transform.localScale = new Vector3(0.03f, 0.03f, 0.03f);
            display.transform.position = new Vector3(-3.5f, 2f, 9f);
            TextMesh textMesh = display.AddComponent<TextMesh>();
            textMesh.anchor = TextAnchor.MiddleLeft;
            textMesh.alignment = TextAlignment.Left;
            textMesh.fontSize = 80;
            textMesh.text = "Welcome! \nPlease gaze at the button" +
                "\nand use the Tap Gesture to display your meetings";
    
            return textMesh;
        }
    
        /// <summary>
        /// Adds a new Meeting in the UI by chaining the existing UI text
        /// </summary>
        internal void AddMeeting(string subject, DateTime dateTime, string location)
        {
            string newText = $"\n{_meetingDisplayTextMesh.text}\n\n Meeting,\nSubject: {subject},\nToday at {dateTime},\nLocation: {location}";
    
            _meetingDisplayTextMesh.text = newText;
        }
    
  8. Usuń metodę Update() i zapisz zmiany w programie Visual Studio przed powrotem do środowiska Unity.

Rozdział 6 — Tworzenie klasy Graph

Następnym skryptem do utworzenia jest skrypt programu Graph . Ten skrypt jest odpowiedzialny za wykonywanie wywołań w celu uwierzytelnienia użytkownika i pobieranie zaplanowanych spotkań dla bieżącego dnia z kalendarza użytkownika.

Aby utworzyć tę klasę:

  1. Kliknij dwukrotnie folder Scripts( Skrypty ), aby go otworzyć.

  2. 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ę Graph.

  3. Kliknij dwukrotnie skrypt, aby otworzyć go za pomocą programu Visual Studio.

  4. Wstaw następujące przestrzenie nazw:

    using System.Collections.Generic;
    using UnityEngine;
    using Microsoft.Identity.Client;
    using System;
    using System.Threading.Tasks;
    
    #if !UNITY_EDITOR && UNITY_WSA
    using System.Net.Http;
    using System.Net.Http.Headers;
    using Windows.Storage;
    #endif
    

    Ważne

    Zauważysz, że części kodu w tym skrycie są opakowane wokół dyrektyw prekompilacyjnych. Pozwala to uniknąć problemów z bibliotekami podczas kompilowania rozwiązania programu Visual Studio.

  5. Usuń metody Start() i Update(), ponieważ nie będą używane.

  6. Poza klasą Graph wstaw następujące obiekty, które są niezbędne do deserializacji obiektu JSON reprezentującego codzienne zaplanowane spotkania:

    /// <summary>
    /// The object hosting the scheduled meetings
    /// </summary>
    [Serializable]
    public class Rootobject
    {
        public List<Value> value;
    }
    
    [Serializable]
    public class Value
    {
        public string subject { get; set; }
        public StartTime start { get; set; }
        public Location location { get; set; }
    }
    
    [Serializable]
    public class StartTime
    {
        public string dateTime;
    
        private DateTime? _startDateTime;
        public DateTime StartDateTime
        {
            get
            {
                if (_startDateTime != null)
                    return _startDateTime.Value;
                DateTime dt;
                DateTime.TryParse(dateTime, out dt);
                _startDateTime = dt;
                return _startDateTime.Value;
            }
        }
    }
    
    [Serializable]
    public class Location
    {
        public string displayName { get; set; }
    }
    
  7. Wewnątrz klasy Graph dodaj następujące zmienne:

        /// <summary>
        /// Insert your Application Id here
        /// </summary>
        private string _appId = "-- Insert your Application Id here --";
    
        /// <summary>
        /// Application scopes, determine Microsoft Graph accessibility level to user account
        /// </summary>
        private IEnumerable<string> _scopes = new List<string>() { "User.Read", "Calendars.Read" };
    
        /// <summary>
        /// Microsoft Graph API, user reference
        /// </summary>
        private PublicClientApplication _client;
    
        /// <summary>
        /// Microsoft Graph API, authentication
        /// </summary>
        private AuthenticationResult _authResult;
    
    

    Uwaga

    Zmień wartość appId na identyfikator aplikacji zanotowany w rozdziale 1, krok 4. Ta wartość powinna być taka sama jak wyświetlana w portalu rejestracji aplikacji na stronie rejestracji aplikacji.

  8. W klasie Graph dodaj metody SignInAsync() i AquireTokenAsync(), które będą monitować użytkownika o wstawienie poświadczeń logowania.

        /// <summary>
        /// Begin the Sign In process using Microsoft Graph Library
        /// </summary>
        internal async void SignInAsync()
        {
    #if !UNITY_EDITOR && UNITY_WSA
            // Set up Grap user settings, determine if needs auth
            ApplicationDataContainer localSettings = ApplicationData.Current.LocalSettings;
            string userId = localSettings.Values["UserId"] as string;
            _client = new PublicClientApplication(_appId);
    
            // Attempt authentication
            _authResult = await AcquireTokenAsync(_client, _scopes, userId);
    
            // If authentication is successful, retrieve the meetings
            if (!string.IsNullOrEmpty(_authResult.AccessToken))
            {
                // Once Auth as been completed, find the meetings for the day
                await ListMeetingsAsync(_authResult.AccessToken);
            }
    #endif
        }
    
        /// <summary>
        /// Attempt to retrieve the Access Token by either retrieving
        /// previously stored credentials or by prompting user to Login
        /// </summary>
        private async Task<AuthenticationResult> AcquireTokenAsync(
            IPublicClientApplication app, IEnumerable<string> scopes, string userId)
        {
            IUser user = !string.IsNullOrEmpty(userId) ? app.GetUser(userId) : null;
            string userName = user != null ? user.Name : "null";
    
            // Once the User name is found, display it as a welcome message
            MeetingsUI.Instance.WelcomeUser(userName);
    
            // Attempt to Log In the user with a pre-stored token. Only happens
            // in case the user Logged In with this app on this device previously
            try
            {
                _authResult = await app.AcquireTokenSilentAsync(scopes, user);
            }
            catch (MsalUiRequiredException)
            {
                // Pre-stored token not found, prompt the user to log-in 
                try
                {
                    _authResult = await app.AcquireTokenAsync(scopes);
                }
                catch (MsalException msalex)
                {
                    Debug.Log($"Error Acquiring Token: {msalex.Message}");
                    return _authResult;
                }
            }
    
            MeetingsUI.Instance.WelcomeUser(_authResult.User.Name);
    
    #if !UNITY_EDITOR && UNITY_WSA
            ApplicationData.Current.LocalSettings.Values["UserId"] = 
            _authResult.User.Identifier;
    #endif
            return _authResult;
        }
    
  9. Dodaj następujące dwie metody:

    1. BuildTodayCalendarEndpoint(), który tworzy identyfikator URI określający dzień i przedział czasu, w którym są pobierane zaplanowane spotkania.

    2. ListMeetingsAsync(), który żąda zaplanowanych spotkań z programu Microsoft Graph.

        /// <summary>
        /// Build the endpoint to retrieve the meetings for the current day.
        /// </summary>
        /// <returns>Returns the Calendar Endpoint</returns>
        public string BuildTodayCalendarEndpoint()
        {
            DateTime startOfTheDay = DateTime.Today.AddDays(0);
            DateTime endOfTheDay = DateTime.Today.AddDays(1);
            DateTime startOfTheDayUTC = startOfTheDay.ToUniversalTime();
            DateTime endOfTheDayUTC = endOfTheDay.ToUniversalTime();
    
            string todayDate = startOfTheDayUTC.ToString("o");
            string tomorrowDate = endOfTheDayUTC.ToString("o");
            string todayCalendarEndpoint = string.Format(
                "https://graph.microsoft.com/v1.0/me/calendarview?startdatetime={0}&enddatetime={1}",
                todayDate,
                tomorrowDate);
    
            return todayCalendarEndpoint;
        }
    
        /// <summary>
        /// Request all the scheduled meetings for the current day.
        /// </summary>
        private async Task ListMeetingsAsync(string accessToken)
        {
    #if !UNITY_EDITOR && UNITY_WSA
            var http = new HttpClient();
    
            http.DefaultRequestHeaders.Authorization = 
            new AuthenticationHeaderValue("Bearer", accessToken);
            var response = await http.GetAsync(BuildTodayCalendarEndpoint());
    
            var jsonResponse = await response.Content.ReadAsStringAsync();
    
            Rootobject rootObject = new Rootobject();
            try
            {
                // Parse the JSON response.
                rootObject = JsonUtility.FromJson<Rootobject>(jsonResponse);
    
                // Sort the meeting list by starting time.
                rootObject.value.Sort((x, y) => DateTime.Compare(x.start.StartDateTime, y.start.StartDateTime));
    
                // Populate the UI with the meetings.
                for (int i = 0; i < rootObject.value.Count; i++)
                {
                    MeetingsUI.Instance.AddMeeting(rootObject.value[i].subject,
                                                rootObject.value[i].start.StartDateTime.ToLocalTime(),
                                                rootObject.value[i].location.displayName);
                }
            }
            catch (Exception ex)
            {
                Debug.Log($"Error = {ex.Message}");
                return;
            }
    #endif
        }
    
  10. Skrypt programu Graph został ukończony. Zapisz zmiany w programie Visual Studio przed powrotem do aparatu Unity.

Rozdział 7 . Tworzenie skryptu GazeInput

Teraz utworzysz witrynę GazeInput. Ta klasa obsługuje i śledzi spojrzenie użytkownika, używając Raycast pochodzącego z kamery głównej, projektując do przodu.

Aby utworzyć skrypt:

  1. Kliknij dwukrotnie folder Scripts (Skrypty ), aby go otworzyć.

  2. Kliknij prawym przyciskiem myszy wewnątrz folderu Scripts (Skrypty ), kliknij pozycję Create C# Script ( Utwórz>skrypt języka C#). Nadaj skryptowi nazwę GazeInput.

  3. Kliknij dwukrotnie skrypt, aby otworzyć go za pomocą programu Visual Studio.

  4. Zmień kod przestrzeni nazw, aby był zgodny z poniższym, wraz z dodaniem tagu "[System.Serializable]" nad klasą GazeInput , aby można było go serializować:

    using UnityEngine;
    
    /// <summary>
    /// Class responsible for the User's Gaze interactions
    /// </summary>
    [System.Serializable]
    public class GazeInput : MonoBehaviour
    {
    
  5. W klasie GazeInput dodaj następujące zmienne:

        [Tooltip("Used to compare whether an object is to be interacted with.")]
        internal string InteractibleTag = "SignInButton";
    
        /// <summary>
        /// Length of the gaze
        /// </summary>
        internal float GazeMaxDistance = 300;
    
        /// <summary>
        /// Object currently gazed
        /// </summary>
        internal GameObject FocusedObject { get; private set; }
    
        internal GameObject oldFocusedObject { get; private set; }
    
        internal RaycastHit HitInfo { get; private set; }
    
        /// <summary>
        /// Cursor object visible in the scene
        /// </summary>
        internal GameObject Cursor { get; private set; }
    
        internal bool Hit { get; private set; }
    
        internal Vector3 Position { get; private set; }
    
        internal Vector3 Normal { get; private set; }
    
        private Vector3 _gazeOrigin;
    
        private Vector3 _gazeDirection;
    
  6. Dodaj metodę CreateCursor(), aby utworzyć kursor HoloLens w scenie i wywołać metodę z metody Start():

        /// <summary>
        /// Start method used upon initialisation.
        /// </summary>
        internal virtual void Start()
        {
            FocusedObject = null;
            Cursor = CreateCursor();
        }
    
        /// <summary>
        /// Method to create a cursor object.
        /// </summary>
        internal GameObject CreateCursor()
        {
            GameObject newCursor = GameObject.CreatePrimitive(PrimitiveType.Sphere);
            newCursor.SetActive(false);
            // Remove the collider, so it doesn't block raycast.
            Destroy(newCursor.GetComponent<SphereCollider>());
            newCursor.transform.localScale = new Vector3(0.05f, 0.05f, 0.05f);
            Material mat = new Material(Shader.Find("Diffuse"));
            newCursor.GetComponent<MeshRenderer>().material = mat;
            mat.color = Color.HSVToRGB(0.0223f, 0.7922f, 1.000f);
            newCursor.SetActive(true);
    
            return newCursor;
        }
    
  7. Poniższe metody umożliwiają spojrzenie Raycast i śledzenie skoncentrowanych obiektów.

    /// <summary>
    /// Called every frame
    /// </summary>
    internal virtual void Update()
    {
        _gazeOrigin = Camera.main.transform.position;
    
        _gazeDirection = Camera.main.transform.forward;
    
        UpdateRaycast();
    }
    /// <summary>
    /// Reset the old focused object, stop the gaze timer, and send data if it
    /// is greater than one.
    /// </summary>
    private void ResetFocusedObject()
    {
        // Ensure the old focused object is not null.
        if (oldFocusedObject != null)
        {
            if (oldFocusedObject.CompareTag(InteractibleTag))
            {
                // Provide the 'Gaze Exited' event.
                oldFocusedObject.SendMessage("OnGazeExited", SendMessageOptions.DontRequireReceiver);
            }
        }
    }
    
        private void UpdateRaycast()
        {
            // Set the old focused gameobject.
            oldFocusedObject = FocusedObject;
            RaycastHit hitInfo;
    
            // Initialise Raycasting.
            Hit = Physics.Raycast(_gazeOrigin,
                _gazeDirection,
                out hitInfo,
                GazeMaxDistance);
                HitInfo = hitInfo;
    
            // Check whether raycast has hit.
            if (Hit == true)
            {
                Position = hitInfo.point;
                Normal = hitInfo.normal;
    
                // Check whether the hit has a collider.
                if (hitInfo.collider != null)
                {
                    // Set the focused object with what the user just looked at.
                    FocusedObject = hitInfo.collider.gameObject;
                }
                else
                {
                    // Object looked on is not valid, set focused gameobject to null.
                    FocusedObject = null;
                }
            }
            else
            {
                // No object looked upon, set focused gameobject to null.
                FocusedObject = null;
    
                // Provide default position for cursor.
                Position = _gazeOrigin + (_gazeDirection * GazeMaxDistance);
    
                // Provide a default normal.
                Normal = _gazeDirection;
            }
    
            // Lerp the cursor to the given position, which helps to stabilize the gaze.
            Cursor.transform.position = Vector3.Lerp(Cursor.transform.position, Position, 0.6f);
    
            // Check whether the previous focused object is this same. If so, reset the focused object.
            if (FocusedObject != oldFocusedObject)
            {
                ResetFocusedObject();
                if (FocusedObject != null)
                {
                    if (FocusedObject.CompareTag(InteractibleTag))
                    {
                        // Provide the 'Gaze Entered' event.
                        FocusedObject.SendMessage("OnGazeEntered", 
                            SendMessageOptions.DontRequireReceiver);
                    }
                }
            }
        }
    
  8. Zapisz zmiany w programie Visual Studio przed powrotem do aparatu Unity.

Rozdział 8 . Tworzenie klasy Interakcje

Teraz musisz utworzyć skrypt Interakcje , który jest odpowiedzialny za:

  • Obsługa interakcji Naciśnięcie i Widok kamery, który umożliwia użytkownikowi interakcję z logowaniem "button" w scenie.

  • Utworzenie obiektu "button" w scenie, z którymi użytkownik będzie wchodzić w interakcję.

Aby utworzyć skrypt:

  1. Kliknij dwukrotnie folder Scripts (Skrypty ), aby go otworzyć.

  2. Kliknij prawym przyciskiem myszy wewnątrz folderu Scripts (Skrypty ), kliknij pozycję Create C# Script ( Utwórz>skrypt języka C#). Nadaj skryptowi nazwę Interakcje.

  3. Kliknij dwukrotnie skrypt, aby otworzyć go za pomocą programu Visual Studio.

  4. Wstaw następujące przestrzenie nazw:

    using UnityEngine;
    using UnityEngine.XR.WSA.Input;
    
  5. Zmień dziedziczenie klasy Interaction z MonoBehaviour na GazeInput.

    interakcje z klasą publiczną: MonoBehaviour

    public class Interactions : GazeInput
    
  6. Wewnątrz klasy Interaction wstaw następującą zmienną:

        /// <summary>
        /// Allows input recognition with the HoloLens
        /// </summary>
        private GestureRecognizer _gestureRecognizer;
    
  7. Zastąp metodę Start ; zwróć uwagę, że jest to metoda zastąpienia, która wywołuje metodę klasy "base" Gaze. Funkcja Start() zostanie wywołana podczas inicjowania klasy, rejestrowania w celu rozpoznawania danych wejściowych i tworzenia przycisku logowania w scenie:

        /// <summary>
        /// Called on initialization, after Awake
        /// </summary>
        internal override void Start()
        {
            base.Start();
    
            // Register the application to recognize HoloLens user inputs
            _gestureRecognizer = new GestureRecognizer();
            _gestureRecognizer.SetRecognizableGestures(GestureSettings.Tap);
            _gestureRecognizer.Tapped += GestureRecognizer_Tapped;
            _gestureRecognizer.StartCapturingGestures();
    
            // Add the Graph script to this object
            gameObject.AddComponent<MeetingsUI>();
            CreateSignInButton();
        }
    
  8. Dodaj metodę CreateSignInButton(), która utworzy wystąpienie przycisku logowania w scenie i ustawi jej właściwości:

        /// <summary>
        /// Create the sign in button object in the scene
        /// and sets its properties
        /// </summary>
        void CreateSignInButton()
        {
            GameObject signInButton = GameObject.CreatePrimitive(PrimitiveType.Sphere);
    
            Material mat = new Material(Shader.Find("Diffuse"));
            signInButton.GetComponent<Renderer>().material = mat;
            mat.color = Color.blue;
    
            signInButton.transform.position = new Vector3(3.5f, 2f, 9f);
            signInButton.tag = "SignInButton";
            signInButton.AddComponent<Graph>();
        }
    
  9. Dodaj metodę GestureRecognizer_Tapped(), która odpowiada na zdarzenie Naciśnij użytkownika.

        /// <summary>
        /// Detects the User Tap Input
        /// </summary>
        private void GestureRecognizer_Tapped(TappedEventArgs obj)
        {
            if(base.FocusedObject != null)
            {
                Debug.Log($"TAP on {base.FocusedObject.name}");
                base.FocusedObject.SendMessage("SignInAsync", SendMessageOptions.RequireReceiver);
            }
        }
    
  10. Usuń metodę Update(), a następnie zapisz zmiany w programie Visual Studio przed powrotem do aparatu Unity.

Rozdział 9 . Konfigurowanie odwołań do skryptu

W tym rozdziale należy umieścić skrypt Interakcje na głównym aparacie. Ten skrypt będzie następnie obsługiwać umieszczanie innych skryptów, w których muszą być.

  • W folderze Scripts (Skrypty ) w panelu projektu przeciągnij skrypt Interactions (Interakcje skryptu ) do obiektu Main Camera (Główny aparat), jak pokazano poniżej.

    Zrzut ekranu przedstawiający miejsce przeciągania skryptu Interakcje.

Rozdział 10 — konfigurowanie tagu

Kod obsługujący spojrzenie będzie korzystał z tagu SignInButton w celu zidentyfikowania obiektu, z którym użytkownik będzie wchodzić w interakcję w celu logowania się do programu Microsoft Graph.

Aby utworzyć tag:

  1. W Edytorze aparatu Unity kliknij kartę Main Camera (Główny aparat) w panelu hierarchii.

  2. W Panelu inspektora kliknij tagMainCamera, aby otworzyć listę rozwijaną. Kliknij pozycję Dodaj tag...

    Zrzut ekranu przedstawiający wyróżnianie tagu Dodaj... Opcja.

  3. + Kliknij przycisk.

    Zrzut ekranu przedstawiający przycisk + .

  4. Napisz nazwę tagu jako SignInButton i kliknij przycisk Zapisz.

    Zrzut ekranu przedstawiający miejsce dodawania nazwy tagu SignInButton.

Rozdział 11 — Tworzenie projektu aparatu Unity na platformie UWP

Wszystko potrzebne dla sekcji Aparatu Unity tego projektu zostało ukończone, więc nadszedł czas, aby skompilować go z aparatu Unity.

  1. Przejdź do obszaru Ustawieniakompilacji (Ustawienia kompilacjipliku>).

    Zrzut ekranu przedstawiający okno dialogowe Ustawienia kompilacji.

  2. Jeśli jeszcze tego nie zrobiono, zaznacz pozycję Projekty języka C# aparatu Unity.

  3. Kliknij pozycję Skompiluj. Środowisko Unity uruchomi okno Eksplorator plików, w którym należy utworzyć, a następnie wybierz folder, w którym ma zostać skompilowanie aplikacji. Utwórz teraz ten folder i nadaj mu nazwę Aplikacja. Następnie z wybraną pozycją Folder aplikacji kliknij pozycję Wybierz folder.

  4. Środowisko Unity rozpocznie kompilowanie projektu w folderze App .

  5. Po zakończeniu kompilowania aparatu Unity (może to zająć trochę czasu), zostanie otwarte okno Eksplorator plików w lokalizacji kompilacji (sprawdź pasek zadań, ponieważ może nie zawsze pojawić się nad oknami, ale powiadomi Cię o dodaniu nowego okna).

Rozdział 12 — Wdrażanie na urządzeniu HoloLens

Aby wdrożyć na urządzeniu HoloLens:

  1. Będziesz potrzebować adresu IP urządzenia HoloLens (na potrzeby zdalnego wdrażania) i upewnić się, że urządzenie HoloLens jest w trybie dewelopera. Aby to zrobić:

    1. Podczas noszenia urządzenia HoloLens otwórz pozycję Ustawienia.

    2. Przejdź do pozycji Sieć &Opcje zaawansowane sieci >Wi-Fi>

    3. Zanotuj adres IPv4 .

    4. Następnie przejdź z powrotem do pozycji Ustawienia, a następnie przejdź do pozycji Aktualizuj & Zabezpieczenia>dla deweloperów

    5. Ustaw tryb dewelopera na włączony.

  2. Przejdź do nowej kompilacji aparatu Unity (folderu App ) i otwórz plik rozwiązania za pomocą programu Visual Studio.

  3. W obszarze Konfiguracja rozwiązania wybierz pozycję Debuguj.

  4. W obszarze Platforma rozwiązania wybierz pozycję x86, Maszyna zdalna. Zostanie wyświetlony monit o wstawienie adresu IP urządzenia zdalnego (w tym przypadku urządzenia HoloLens, który został zanotowany).

    Zrzut ekranu przedstawiający lokalizację wyboru x86 i maszyny zdalnej.

  5. Przejdź do menu Kompilacja i kliknij pozycję Wdróż rozwiązanie , aby załadować aplikację bezpośrednio do urządzenia HoloLens.

  6. Aplikacja powinna być teraz wyświetlana na liście zainstalowanych aplikacji na urządzeniu HoloLens, które będą gotowe do uruchomienia.

Aplikacja Microsoft Graph HoloLens

Gratulacje, utworzono aplikację rzeczywistości mieszanej, która wykorzystuje program Microsoft Graph do odczytywania i wyświetlania danych kalendarza użytkownika.

Zrzut ekranu przedstawiający ukończoną aplikację rzeczywistości mieszanej.

Ćwiczenia dodatkowe

Ćwiczenie 1

Używanie programu Microsoft Graph do wyświetlania innych informacji o użytkowniku

  • Adres e-mail użytkownika/ numer telefonu/ obraz profilu

Ćwiczenie 1

Zaimplementuj sterowanie głosem, aby nawigować po interfejsie użytkownika programu Microsoft Graph.