HoloLens (第 1 世代) と Azure 311 - Microsoft Graph

Note

Mixed Reality Academy のチュートリアルは、HoloLens (第 1 世代) と Mixed Reality イマーシブ ヘッドセットを念頭に置いて編成されています。 そのため、それらのデバイスの開発に関するガイダンスを引き続き探している開発者のために、これらのチュートリアルをそのまま残しておくことが重要だと考えています。 これらのチュートリアルが、HoloLens 2 に使用されている最新のツールセットや操作に更新されることは "ありません"。 これらは、サポートされているデバイス上で継続して動作するように、保守されます。 今後、HoloLens 2 向けに開発する方法を示す新しいチュートリアル シリーズが投稿される予定です。 この通知は、それらのチュートリアルが投稿されたときにリンクと共に更新されます。

このコースでは、Microsoft Graph を使い、Mixed Reality アプリケーション内でセキュリティで保護された認証を使用して Microsoft アカウントにログインする方法について説明します。 次に、スケジュールされている会議をアプリケーション インターフェイスで取得して表示します。

アプリケーション インターフェイスのスケジュールされた会議を示すスクリーンショット。

Microsoft Graph は、Microsoft の多くのサービスにアクセスできるように設計された API のセットです。 Microsoft では、Microsoft Graph を、リレーションシップによって接続されたリソースのマトリックスと説明しています。つまり、これを使用すると、アプリケーションは、接続されたあらゆる種類のユーザー データにアクセスできます。 詳細については、Microsoft Graph に関するページをご覧ください。

開発にはアプリの作成が含まれます。そこで、ユーザーは球体を見つめ、次にタップするように指示されます。それにより、Microsoft アカウントに安全にログインするように求めるメッセージがユーザーに表示されます。 自分のアカウントにログインすると、ユーザーはその日にスケジュールされている会議の一覧を見ることができます。

このコースを修了すると、Mixed Reality HoloLens アプリケーションで次のことができるようになります。

  1. タップ ジェスチャを使用してオブジェクトをタップします。これにより、ユーザーは Microsoft アカウントにログインするように求められます (ログインするためにアプリの外に移動し、その後、再びアプリに戻ります)。
  2. その日にスケジュールされている会議の一覧を表示します。

お客様のアプリケーションで、結果をどのようにデザインと統合するかは、お客様次第です。 このコースは、Azure のサービスをお客様の Unity プロジェクトに統合する方法を説明することを目的としています。 このコースで得られた知識を使用して、ご自分の Mixed Reality アプリケーションを強化しましょう。

デバイス サポート

コース HoloLens イマーシブ ヘッドセット
MR と Azure 311:Microsoft Graph ✔️

前提条件

Note

このチュートリアルは、Unity と C# の基本的な使用経験がある開発者を対象としています。 また、このドキュメント内の前提条件や文章による説明は、執筆時 (2018 年 7 月) にテストおよび検証された内容であることをご了承ください。 ツールのインストールの記事に記載されているように、お客様は最新のソフトウェアを自由に使用できます。ただし、このコースの情報は、以下に記載されているものよりも新しいソフトウェアでの設定や結果と完全に一致するとは限りません。

このコースでは、次のハードウェアとソフトウェアをお勧めします。

開始する前に

  1. このプロジェクトをビルドする際の問題を避けるために、このチュートリアルで紹介するプロジェクトをルートまたはルートに近いフォルダーに作成することを強くお勧めします (フォルダー パスが長いと、ビルド時に問題が発生する可能性があります)。
  2. HoloLens を設定してテストします。 HoloLens の設定でサポートが必要な場合は、HoloLens セットアップに関する記事にアクセスしてください
  3. 新しい HoloLens アプリの開発を開始するときは、調整とセンサーのチューニングを実行することをお勧めします (ユーザーごとにこれらのタスクを実行すると役立つ場合があります)。

調整の詳細については、この HoloLens の調整に関する記事へのリンクを参照してください。

センサー チューニングの詳細については、HoloLens センサー チューニングに関する記事へのリンクを参照してください。

第 1 章 - アプリケーション登録ポータルでアプリを作成する

まず、アプリケーション登録ポータルでアプリケーションを作成し、登録する必要があります。

この章では、Microsoft Graph を呼び出してアカウント コンテンツにアクセスできるようにするサービス キーについても説明します。

  1. Microsoft アプリケーション登録ポータルに移動し、自分の Microsoft アカウントを使用してログインします。 ログインすると、アプリケーション登録ポータルにリダイレクトされます。

  2. [マイ アプリケーション] セクションで、[アプリの追加] ボタンをクリックします。

    [アプリの追加] を選択する場所を示すスクリーンショット。

    重要

    アプリケーション登録ポータルは、以前に Microsoft Graph を操作したことがあるかどうかによって外観が異なる場合があります。 以下のスクリーンショットでは、これらのさまざまなバージョンを示しています。

  3. アプリケーションの名前を追加し、[作成] をクリックします。

    アプリケーションの名前を追加する場所を示すスクリーンショット。

  4. アプリケーションが作成されると、アプリケーションのメイン ページにリダイレクトされます。 アプリケーション ID をコピーし、必ずこの値をどこか安全な場所に書き留めておいてください。すぐにコードで使用します。

    アプリケーション ID を表示する場所を示すスクリーンショット。

  5. [プラットフォーム] セクションで、[ネイティブ アプリケーション] が表示されていることを確認します。 "されていない" 場合は、[プラットフォームの追加] をクリックし、[ネイティブ アプリケーション] を選択します。

    [ネイティブ アプリケーション] セクションが強調表示されているスクリーンショット。

  6. 同じページを下にスクロールし、[Microsoft Graph のアクセス許可] というセクションで、アプリケーションに対する追加のアクセス許可を指定する必要があります。 [委任されたアクセス許可] の横にある [追加] をクリックします。

    [委任されたアクセス許可] の横にある [追加] を選択する場所を示すスクリーンショット。

  7. アプリケーションでユーザーの予定表にアクセスできるようにするには、[Calendars.Read] というボックスをオンにし、[OK] をクリックします。

    Calendars.Read チェックボックスを示すスクリーンショット。

  8. 一番下までスクロールし、[保存] ボタンをクリックします。

    [保存] を選択する場所を示すスクリーンショット。

  9. 保存が確認され、アプリケーション登録ポータルからログ アウトできます。

第 2 章 - Unity プロジェクトを設定する

次に示すのは、複合現実での開発のための一般的な設定であり、他のプロジェクトのテンプレートとして利用できます。

  1. Unity を開き、[New] (新規) をクリックします。

    Unity インターフェイスを示すスクリーンショット。

  2. Unity プロジェクト名を指定する必要があります。 MSGraphMR を挿入します。 プロジェクト テンプレートが [3D] に設定されていることを確認します。 [Location] (場所) を適切な場所に設定します (前述のとおり、ルート ディレクトリに近い場所をお勧めします)。 次に、[プロジェクトの作成] をクリックします。

    [プロジェクトの作成] を選択する場所を示すスクリーンショット。

  3. Unity を開いた状態で、既定のスクリプト エディターVisual Studio に設定されているかどうか確認することをお勧めします。 [Edit] (編集)>[Preferences] (環境設定) に移動し、新しいウィンドウで [External Tools] (外部ツール) に移動します。 [外部スクリプト エディター][Visual Studio 2017] に変更します。 [環境設定] ウィンドウを閉じます。

    外部スクリプト エディターを Visual Studio 2017 に設定する場所を示すスクリーンショット。

  4. [File](ファイル)>[Build Settings](ビルド設定) で、[Universal Windows Platform](ユニバーサル Windows プラットフォーム) を選択します。次に、[Switch Platform](プラットフォームの切り替え) ボタンをクリックして選択を適用します。

    [プラットフォームの切り替え] を選択する場所を示すスクリーンショット。

  5. [File](ファイル)>[Build Settings](ビルド設定) で、次のことを確認します。

    1. [ターゲット デバイス][HoloLens] に設定されている

    2. [ビルドの種類][D3D] に設定されている

    3. [SDK][Latest installed] (最新のインストール) に設定されている

    4. [Visual Studio Version] (Visual Studio のバージョン)[Latest installed] (最新のインストール) に設定されている

    5. [Build and Run] (ビルドと実行)[Local Machine] (ローカル マシン) に設定されている

    6. シーンを保存し、ビルドに追加します。

      1. これを行うには、[Add Open Scenes] (開いているシーンを追加) を選択します。 保存ウィンドウが表示されます。

        [開いているシーンの追加] を選択する場所を示すスクリーンショット。

      2. この、および将来のシーン用に新しいフォルダーを作成します。 [新しいフォルダー] ボタンを選択して新しいフォルダーを作成し、「Scenes」と名前を付けます。

        新しいフォルダーに名前を付ける場所を示すスクリーンショット。

      3. 新しく作成した Scenes フォルダーを開き、"[File name:](ファイル名)" テキスト フィールドに「MR_ComputerVisionScene」と入力して [Save](保存) をクリックします。

        ファイル名を入力する場所を示すスクリーンショット。

        重要

        Unity のシーンは Unity プロジェクトに関連付けられている必要があるため、Assets フォルダーに保存する必要があることに注意してください。 Unity プロジェクトの構築では、通常、Scenes フォルダー (とその他の類似フォルダー) を作成します。

    7. [ビルド設定] の残りの設定は、ここでは既定値のままにしておきます。

  6. [ビルド設定] ウィンドウで、[プレーヤー設定] ボタンをクリックすると、[インスペクター] が配置されているスペースに関連パネルが表示されます。

    [プレーヤーの設定] ダイアログ ボックスを示すスクリーンショット。

  7. このパネルでは、いくつかの設定を確認する必要があります。

    1. [Other Settings](その他の設定) タブで、次の内容を確認します。

      1. [ScriptingRuntime Version](スクリプト ランタイム バージョン)[Experimental](試験段階) (.NET 4.6 と同等) である。この場合、エディターの再起動が必要になります。

      2. [Scripting Backend](スクリプト バックエンド)[.NET] である。

      3. [API Compatibility Level] (API 互換性レベル)[.NET 4.6] である

        API 互換性レベルをチェックする場所を示すスクリーンショット。

    2. [公開設定] タブ内の [機能] で、次の内容を確認します。

      • InternetClient

        InternetClient オプションを選択する場所を示すスクリーンショット。

    3. パネルのずっと下にある [XR Settings](XR 設定) ([Publish Settings](公開設定) の下にある) で、[Virtual Reality Supported](仮想現実のサポート) をオンにし、[Windows Mixed Reality SDK] が追加されていることを確認します。

      WINDOWS MIXED REALITY SDK を追加する場所を示すスクリーンショット。

  8. "[Build Settings](ビルド設定)" に戻ると、"Unity C# プロジェクト" に適用されていた灰色表示が解除されています。その横にあるチェック ボックスをオンにします。

  9. [ビルド設定] ウィンドウを閉じます。

  10. シーンとプロジェクトを保存します ([FILE](ファイル)>[SAVE SCENE / FILE](シーン / ファイルの保存)>[SAVE PROJECT](プロジェクトの保存))。

第 3 章 - Unity でライブラリをインポートする

重要

このコースの "Unity のセットアップ" コンポーネントをスキップして、そのままコードに進みたい場合は、この Azure-MR-311.unitypackage をダウンロードして、それをカスタム パッケージとしてご自分のプロジェクトにインポートしてから、第 5 章から続けてください。

Unity 内で Microsoft Graph を使用するには、Microsoft.Identity.Client DLL を使用する必要があります。 Microsoft Graph SDK を使用することもできますが、その場合は、Unity プロジェクトをビルドした後に NuGet パッケージの追加 (つまり、プロジェクトのビルド後の編集) が必要になります。 必要な DLL を Unity に直接インポートした方が簡単であると考えられます。

Note

現在、Unity には、インポート後にプラグインを再構成する必要があるという既知の問題があります。 バグが解決された後は、当該手順 (このセクションの 4 - 7) は不要になります。

Microsoft Graph を独自のプロジェクトにインポートするには、MSGraph_LabPlugins.zip ファイルをダウンロードします。 このパッケージは、テスト済みライブラリのバージョンで作成されています。

カスタム DLL を Unity プロジェクトに追加する方法の詳細については、こちらのリンクを参照してください。

パッケージをインポートするには、次のようにします。

  1. [Assets](アセット)>[Import Package](パッケージのインポート)>[Custom Package](カスタム パッケージ) メニュー オプションを使用して、Unity パッケージを Unity に追加します。 ダウンロードしたパッケージを選択します。

  2. ポップアップ表示される [Import Unity Package](Unity パッケージのインポート) ボックスで、[Plugins](プラグイン) およびその下にあるすべての項目が選択されていることを確認します。

    [プラグイン] で選択した構成パラメーターを示すスクリーンショット。

  3. [Import](インポート) ボタンをクリックして、各項目をプロジェクトに追加します。

  4. "[Project](プロジェクト) パネル" 内の [Plugins](プラグイン) の下にある MSGraph フォルダーに移動し、Microsoft.Identity.Client というプラグインを選択します。

    Microsoft.Identity.Client プラグインを示すスクリーンショット。

  5. この "プラグイン" を選択した状態で、[Any Platform](任意のプラットフォーム) がオフになっていることを確認し、[WSAPlayer] もオフになっていることを確認してから、[Apply](適用) をクリックします。 これは単に、ファイルが正しく構成されていることを確認するためのものです。

    Any Platform と WSAPlayer がチェックされていないことを確認する場所を示すスクリーンショット。

    Note

    これらのプラグインにマークを付けると、それらは Unity エディターでのみ使用されるように構成されます。 WSA フォルダー内には、プロジェクトが Unity からユニバーサル Windows アプリケーションとしてエクスポートされた後に使用される異なる DLL のセットがあります。

  6. 次に、MSGraph フォルダー内にある WSA フォルダーを開く必要があります。 先ほど構成したものと同じファイルのコピーがあるのが分かります。 ファイルを選択し、インスペクターで次を行います。

    • [Any Platform](任意のプラットフォーム)オフになっており、[WSAPlayer]のみオンになっている。

    • [SDK][UWP] に設定されており、[Scripting Backend](スクリプティング バックエンド)[.NET] に設定されている。

    • [Don't process](処理しない)オンになっている。

      [処理しない] が選択されていることを示すスクリーンショット。

  7. [Apply] をクリックします。

第 4 章 - カメラの設定

この章では、シーンのメイン カメラを設定します。

  1. "[Hierarchy](階層)" パネルで [Main Camera](メイン カメラ) を選択します。

  2. 選択すると、メイン カメラのすべてのコンポーネントを "[Inspector](インスペクター)" パネルに表示できるようになります。

    1. カメラ オブジェクトの名前Main Camera である必要があります (スペルに注意します)。

    2. Main Camera タグMainCamera に設定する必要があります (スペルに注意します)。

    3. [Transform](変換) の [Position](位置)0、0、0 に設定されていることを確認します。

    4. [Clear Flags](クリア フラグ)[Solid Color](単色) に設定します。

    5. カメラ コンポーネントの [Background Color](背景色)黒、アルファ 0 (16 進コード: #00000000) に設定します。

      背景色を設定する場所が強調表示されているスクリーンショット。

  3. "[Hierarchy](階層)" パネルの最終的なオブジェクト構造は、次の図に示すようになります。

    [階層] パネルの最終的なオブジェクト構造を示すスクリーンショット。

第 5 章 - MeetingsUI クラスを作成する

最初に作成する必要があるスクリプトは MeetingsUI です。これは、アプリケーションの UI (ウェルカム メッセージ、指示、会議の詳細) のホストと設定を担当します。

このクラスを作成するには、次の手順を実行します。

  1. "[Project](プロジェクト) パネル" の Assets フォルダーを右クリックし、次に [Create](作成)>[Folder](フォルダー) の順に選択します。 フォルダーに「Scripts」という名前を付けます。

    Assets フォルダーの場所を示すスクリーンショット。Scripts フォルダーを作成する場所を示すスクリーンショット。

  2. Scripts フォルダーを開き、そのフォルダー内で右クリックし、[Create](作成)>[C# Script](C# スクリプト) と選択します。 スクリプトに「MeetingsUI」という名前を付けます。

    MeetingsUI フォルダーを作成する場所を示すスクリーンショット。

  3. 新しい MeetingsUI スクリプトをダブルクリックして、それを Visual Studio で開きます。

  4. 次の名前空間を挿入します。

    using System;
    using UnityEngine;
    
  5. クラス内に次の変数を挿入します。

        /// <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. 次に、Start() メソッドを置き換え、Awake() メソッドを追加します。 これらは、クラスの初期化時に呼び出されます。

        /// <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. Meetings UI の作成を行うメソッドを追加し、要求に応じてそれに現在の会議を追加します。

        /// <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. Unity に戻る前に、Update() メソッドを削除し、Visual Studio で変更を保存してください。

第 6 章 - Graph クラスを作成する

次に作成するスクリプトは、Graph スクリプトです。 このスクリプトでは、ユーザーを認証し、本日スケジュールされている会議をユーザーの予定表から取得するための呼び出しを行います。

このクラスを作成するには、次の手順を実行します。

  1. Scripts フォルダーをダブルクリックして開きます。

  2. Scripts フォルダー内で右クリックし、[Create](作成)>[C# Script](C# スクリプト) をクリックします。 スクリプトに「Graph」という名前を付けます。

  3. スクリプトをダブルクリックして、それを Visual Studio で開きます。

  4. 次の名前空間を挿入します。

    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
    

    重要

    このスクリプトのコードの一部がプリコンパイル ディレクティブでラップされているのがわかります。これは、Visual Studio ソリューションを構築する際のライブラリに関する問題を回避するためです。

  5. Start()Update() メソッドは使用されないので削除します。

  6. Graph クラスの外に、次のオブジェクトを挿入します。これらは、毎日のスケジュールされている会議を表す JSON オブジェクトを逆シリアル化するために必要です。

    /// <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. Graph クラス内に次の変数を追加します。

        /// <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;
    
    

    Note

    appId 値を、第 1 章、手順 4 でメモしておいた App Id になるように変更します。 この値は、アプリケーション登録ポータルのアプリケーション登録ページに表示されるものと同じです。

  8. Graph クラス内に、ユーザーにログイン資格情報の挿入を求めるメッセージを表示する SignInAsync()AquireTokenAsync() メソッドを追加します。

        /// <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. 次の 2 つのメソッドを追加します。

    1. BuildTodayCalendarEndpoint()。スケジュールされた会議が取得される日付と期間を指定する URI を作成します。

    2. ListMeetingsAsync()。スケジュールされた会議を 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. これで、Graph スクリプトが完成しました。 Unity に戻る前に、Visual Studio で変更を保存します。

第 7 章 – GazeInput スクリプトを作成する

次に、GazeInput を作成します。 このクラスは、前方に投影される、メイン カメラからのレイキャストを使用して、ユーザーの視線入力を処理して追跡します。

スクリプトを作成するには:

  1. Scripts フォルダーをダブルクリックして開きます。

  2. Scripts フォルダー内で右クリックし、[Create](作成)>[C# Script](C# スクリプト) をクリックします。 スクリプトに「GazeInput」という名前を付けます。

  3. スクリプトをダブルクリックして、それを Visual Studio で開きます。

  4. 名前空間コードを次のものと一致するように変更し、併せて "[System.Serializable]" タグを GazeInput クラスの上に追加して、シリアル化できるようにします。

    using UnityEngine;
    
    /// <summary>
    /// Class responsible for the User's Gaze interactions
    /// </summary>
    [System.Serializable]
    public class GazeInput : MonoBehaviour
    {
    
  5. GazeInput クラス内に次の変数を追加します。

        [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. CreateCursor()メソッドを追加してシーン内に HoloLens カーソルを作成し、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. 次のメソッドを使用すると、視線入力のレイキャストが有効になり、フォーカスされたオブジェクトが追跡されます。

    /// <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. Unity に戻る前に、Visual Studio で変更を保存します。

第 8 章 – Interactions クラスを作成する

次に、Interactions スクリプトを作成する必要があります。これは、次の操作を行います。

  • タップ対話式操作とカメラの視線入力を処理する。これにより、ユーザーは、シーン内のログイン "ボタン" を操作できます。

  • ユーザーが操作するログイン "ボタン" オブジェクトをシーン内に作成する。

スクリプトを作成するには:

  1. Scripts フォルダーをダブルクリックして開きます。

  2. Scripts フォルダー内で右クリックし、[Create](作成)>[C# Script](C# スクリプト) をクリックします。 スクリプトに「Interactions」という名前を付けます。

  3. スクリプトをダブルクリックして、それを Visual Studio で開きます。

  4. 次の名前空間を挿入します。

    using UnityEngine;
    using UnityEngine.XR.WSA.Input;
    
  5. Interaction クラスの継承を MonoBehaviour から GazeInput に変更します。

    パブリッククラスインターアクション : MonoBehaviour

    public class Interactions : GazeInput
    
  6. Interaction クラス内に次の変数を挿入します。

        /// <summary>
        /// Allows input recognition with the HoloLens
        /// </summary>
        private GestureRecognizer _gestureRecognizer;
    
  7. Start メソッドを置き換えます。これが、"基底の" Gaze クラス メソッドを呼び出すオーバーライド メソッドであることに注意してください。 クラスが初期化されると Start() が呼び出され、入力認識の登録が行われ、シーン内にサインイン "ボタン" が作成されます。

        /// <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. CreateSignInButton() メソッドを追加します。これにより、シーン内のサインイン "ボタン" がインスタンス化され、そのプロパティが設定されます。

        /// <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. "タップ" ユーザー イベントに応答する GestureRecognizer_Tapped() メソッドを追加します。

        /// <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. Unity に戻る前に、Update() メソッドを削除し、Visual Studio で変更を保存してください。

第 9 章 - スクリプト リファレンスを設定する

この章では、Interactions スクリプトを メイン カメラに配置する必要があります。 それにより、このスクリプトによって、他のスクリプトを必要な場所に配置する処理が行われます。

  • 次の図に示すように、"[Project](プロジェクト) パネル" の Scripts フォルダーから、Interactions スクリプトをメイン カメラ オブジェクトにドラッグします。

    相互作用スクリプトをドラッグする場所を示すスクリーンショット。

第 10 章 - タグを設定する

視線入力を処理するコードでは、SignInButton タグを使用して、ユーザーが Microsoft Graph にサインインするために操作するオブジェクトが識別されます。

タグを作成するには、次のようにします。

  1. Unity エディターで、"[Hierarchy](階層) パネル" 内の Main Camera をクリックします。

  2. "[Inspector](インスペクター) パネル" で、MainCamera "タグ" をクリックしてドロップダウン リストを開きます。 [Add Tag...](タグの追加) をクリックします。

    [タグの追加]が強調表示されているスクリーンショット。..オプション。

  3. [+] ボタンをクリックします。

    [+] ボタンを示すスクリーンショット。

  4. タグ名として「SignInButton」と入力し、[保存] をクリックします。

    SignInButton タグ名を追加する場所を示すスクリーンショット。

第 11 章 - Unity プロジェクトを UWP にビルドする

このプロジェクトの Unity セクションに必要なすべての手順が完了したため、次に Unity からそれをビルドします。

  1. [ビルド設定] ([ファイル> のビルド設定]) に移動します。

    [ビルド設定] ダイアログ ボックスを示すスクリーンショット。

  2. [Unity C# プロジェクト] をオンにします (まだオンにしていない場合)。

  3. [ビルド] をクリックします。 Unity によって [エクスプローラー] ウィンドウが起動されます。そこで、アプリのビルド先のフォルダーを作成して選択する必要があります。 ここでそのフォルダーを作成して、「App」という名前を付けます。 次に、App フォルダーが選択された状態で、[フォルダーの選択] をクリックします。

  4. Unity でプロジェクトのビルドが開始され、App フォルダーに保存されます。

  5. Unity によるビルドが完了すると (多少時間がかかる場合があります)、エクスプローラー ウィンドウが開いて、ビルドの場所が表示されます (必ずしも最前面に表示されるとは限らないため、タスク バーを確認してください。新しいウィンドウが追加されたことがわかります)。

第 12 章 – HoloLens にデプロイする

HoloLens にデプロイするには、次の手順を実行します。

  1. HoloLens の IP アドレスが必要になります (リモート デプロイの場合)。また、HoloLens が開発者モードになっていることを確認する必要があります。このためには、次のようにします。

    1. HoloLens の装着中に、[設定] を開きます。

    2. [ネットワーク & インターネット>Wi-Fiの詳細オプション]> に移動します

    3. IPv4 アドレスを書き留めます。

    4. 次に、[設定] に戻り、[Update & Securityfor Developers]\(開発者向けセキュリティ>の更新\) に移動します

    5. [開発者モード] を [オン] に設定します。

  2. 新しい Unity ビルド (App フォルダー) に移動し、Visual Studio を使用してソリューション ファイルを開きます。

  3. [ソリューション構成] で、[デバッグ] を選択します。

  4. [ソリューション プラットフォーム] で、[X86]、[リモート コンピューター] を選択します。 リモート デバイスの (この場合は、メモした HoloLens の) IP アドレスを挿入するように求められます。

    x86 とリモート コンピューターを選択する場所を示すスクリーンショット。

  5. [ビルド] メニューに移動して [ソリューションの配置] をクリックし、アプリケーションを HoloLens にサイドロードします。

  6. HoloLens にインストールされたアプリの一覧にアプリが表示され、起動できる状態になります。

Microsoft Graph HoloLens アプリケーション

これで、Microsoft Graph を利用してユーザーの予定表データを読み取って表示する Mixed Reality アプリが作成されました。

完成した Mixed Reality アプリを示すスクリーンショット。

ボーナス演習

演習 1

Microsoft Graph を使用して、ユーザーに関するその他の情報を表示します

  • ユーザーの電子メール / 電話番号 / プロファイルの画像

演習 1

音声コントロールを実装して Microsoft Graph UI をナビゲートします。