次の方法で共有


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

注:

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 Service と Unity プロジェクトを統合する方法を説明するように設計されています。 このコースから得た知識を使用して、Mixed Reality アプリケーションを強化するのがあなたの仕事です。

デバイスのサポート

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

前提条件

注:

このチュートリアルは、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 プロジェクトを設定する

Mixed Reality を使用して開発するための一般的な設定を次に示します。そのため、他のプロジェクトに適したテンプレートです。

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

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

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

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

  3. Unity開いている場合は、既定の [スクリプト] エディターVisual Studio に設定されていることを確認する価値があります。 [編集>Preferences] に移動し、新しいウィンドウから [外部ツール] に移動します。 [外部スクリプト] エディターVisual Studio 2017 に変更します。 [基本設定] ウィンドウを閉じます。

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

  4. [ファイル>ビルドの設定] に移動し、[ユニバーサル Windows プラットフォーム] を選択し、[プラットフォームの切り替え] ボタンをクリックして選択内容を適用します。

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

  5. [File>Build Settings]\(ファイル][ビルドの設定\) に残っている間は、次の点を確認します。

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

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

    3. SDK[最新インストール済み] に設定されている

    4. Visual Studio のバージョン[最新インストール済み] に設定されている

    5. ビルドと実行ローカル コンピューターに設定されている

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

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

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

      2. このための新しいフォルダーと、将来のシーンを作成します。 [ 新しいフォルダー ] ボタンを選択して新しいフォルダーを作成し、 Scenes という名前を付けます

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

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

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

        重要

        Unity プロジェクトに関連付けられている必要があるため、Unity シーンを Assets フォルダーに保存する必要があることに注意してください。 scenes フォルダー (およびその他の同様のフォルダー) を作成することは、Unity プロジェクトを構築する一般的な方法です。

    7. [ ビルド設定] の残りの設定は、現時点では既定値のままにする必要があります。

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

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

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

    1. [その他の設定] タブで、次 の手順を実行 します。

      1. スクリプト ランタイムバージョン試験的 (.NET 4.6 同等) にする必要があります。これにより、エディターを再起動する必要がトリガーされます。

      2. スクリプト バックエンド.NET にする必要があります

      3. API 互換性レベル.NET 4.6 にする必要があります

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

    2. [発行設定] タブの [機能] で、次チェックします。

      • InternetClient

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

    3. パネルの下の [XR 設定] ([発行設定] の下にあります)、[サポートされている Virtual Reality] チェック、Windows Mixed Reality SDK が追加されていることを確認します。

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

  8. ビルド設定に戻ると、C# プロジェクトUnity灰色表示されなくなります。この横にあるチェックボックスをチェックします。

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

  10. シーンとプロジェクトを保存します (FILE>SAVE SCENES/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にインポートする方が簡単と見なされます。

注:

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

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

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

パッケージをインポートするには:

  1. [Assets>Import Package>Custom Package] メニュー オプションを使用してUnityにUnity パッケージを追加します。 ダウンロードしたパッケージを選択します。

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

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

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

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

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

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

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

    注:

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

  6. 次に、MSGraph フォルダー内の WSA フォルダーを開く必要があります。 構成したのと同じファイルのコピーが表示されます。 ファイルを選択し、インスペクターで次の手順を実行します。

    • [Any Platform]\(任意のプラットフォーム\) がオフになっていることを確認し、WSAPlayerのみがオンになっていることを確認します

    • SDKUWP に設定され、スクリプト バックエンドDot Net に設定されていることを確認します

    • [処理しない] がオンになっていることを確認します

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

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

第 4 章 - カメラのセットアップ

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

  1. [階層] パネルでメイン カメラを選択します。

  2. 選択すると、[インスペクター] パネルにメイン カメラのすべてのコンポーネントが表示されます。

    1. Camera オブジェクトには Main Camera という名前を付ける必要があります (スペルに注意してください)。

    2. メイン カメラ タグMainCamera に設定する必要があります (スペルに注意してください)。

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

    4. [クリア フラグ][純色] に設定する

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

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

  3. 階層パネルの最後のオブジェクト構造は、次の図のようになります。

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

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

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

このクラスを作成するには:

  1. プロジェクト パネル[Assets] フォルダーを右クリックし、[作成>Folder] を選択します。 [スクリプト] フォルダーに名前 を付けます

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

  2. [スクリプト] フォルダーを開き、そのフォルダー内で右クリックし、[作成>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. 会議 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. Update() メソッドを削除し、変更を Visual Studio に保存してから、Unityに戻ります。

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

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

このクラスを作成するには:

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

  2. [スクリプト] フォルダー内を右クリックし、[作成>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;
    
    

    注:

    appId の値を、第 1 章の手順 4 で確認したアプリ 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. Microsoft Graph からスケジュールされた会議を要求する ListMeetingsAsync()

        /// <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. [スクリプト] フォルダー内を右クリックし、[作成>C# スクリプト] をクリックします。 スクリプトに GazeInput という名前を 付けます

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

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

    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. [スクリプト] フォルダー内を右クリックし、[作成>C# スクリプト] をクリックします。 スクリプトに Interactions という名前を付けます

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

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

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

    public クラスの相互作用 : MonoBehaviour

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

        /// <summary>
        /// Allows input recognition with the HoloLens
        /// </summary>
        private GestureRecognizer _gestureRecognizer;
    
  7. Start メソッドを置き換えます。これはオーバーライド メソッドであり、'base' 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. Tap ユーザー イベントに応答する 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. Update() メソッドを削除し、変更 Visual Studio に保存してから、Unityに戻ります。

第 9 章 - スクリプト参照を設定する

この章では、 操作 スクリプトを メイン カメラに配置する必要があります。 その後、そのスクリプトは、必要な場所に他のスクリプトを配置します。

  • 次に示すように、プロジェクト パネル[スクリプト] フォルダーから、スクリプトの [相互作用]Main Camera オブジェクトにドラッグします。

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

第 10 章 - タグの設定

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

タグを作成するには:

  1. Unity エディターで、[階層] パネル[メイン カメラ] をクリックします。

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

    [タグの追加]を強調表示したスクリーンショット。..オプション。

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

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

  4. タグ名を SignInButton として書き込み、[保存] をクリックします。

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

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

このプロジェクトのUnityセクションに必要なものはすべて完了したので、Unityからビルドします。

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

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

  2. まだない場合は、[C# プロジェクト] Unityチェック ボックスをオンにします。

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

  4. Unityは、App フォルダーへのプロジェクトのビルドを開始します。

  5. ビルドUnity完了すると (時間がかかる場合があります)、ビルドの場所にエクスプローラー ウィンドウが開きます (タスク バーチェック、常にウィンドウの上に表示されるとは限りませんが、新しいウィンドウが追加されたことを通知します)。

第 12 章 - HoloLens へのデプロイ

HoloLens にデプロイするには:

  1. HoloLens の IP アドレス (リモート展開用) と、HoloLens が 開発者モード であることを確認する必要があります。これを行うには:

    1. HoloLens を装着している間に 、[設定] を開きます。

    2. [ネットワーク & インターネット>Wi-Fi>Advanced Options] に移動します

    3. IPv4 アドレスをメモします。

    4. 次に、[ 設定] に戻り、[ Update & Security>For Developers

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

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

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

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

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

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

  6. これで、HoloLens にインストールされているアプリの一覧にアプリが表示され、起動する準備が整いました。

Microsoft Graph HoloLens アプリケーション

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

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

ボーナス演習

演習 1

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

  • ユーザーメール/電話番号/プロフィール画像

演習 1

音声コントロールを実装して、Microsoft Graph UI を移動します。