yotiky Tech Blog

とあるエンジニアの備忘録

Unity (HoloLens) - Httpクライアント の基本的な使い方

Unity (HoloLens) で通信するために使用できる Httpクライアントのサンプルコード集です。
GETとPOSTそれぞれで、コルーチン、UniRx、async/await(TaskとUniTask)などを織り交ぜながら例を書いています。

目次

TL;DR

  • 最新の環境でアセットも自由に使えるのであれば、UnityWebRequest + UniTask を使うのが良いです
  • 環境の制約などある場合はそれぞれの実装例を参考にしてください

概要

対象クラス

対象とするクラスは次の通りです。

クラス 提供元 名前空間 環境
WWW Unity UnityEngine Unity 5.4 より前から
UnityWebRequest Unity UnityEngine.Networking Unity 5.4 以降
HttpWebRequest .NET System.Net .NET Framwork 初期から
HttpClient .NET System.Net.Http .NET Framework 4.5 以降
HttpClient .NET Windows.Web.Http UWP Windows 10.0.10240.0 以降

開発環境

  • Unity : 2019.3.15f.1
  • UniRx : 6.2.2
  • UniTask : 2.0.19
  • MRTK : 2.4.0
  • Visual Studio : 2019

コード全体のプロジェクトはこちらのリポジトリにあります。

github.com

サンプル

WWW

GET

コルーチン

コルーチンを使った実装例です。

    void Start()
    {
        StartCoroutine("GetRequestCoroutine");
    }

    private IEnumerator GetRequestCoroutine()
    {
        var www = new WWW(url);
        yield return www;

        if (www.error != null)
        {
            Debug.Log("failure.");
            Debug.Log(www.error);
        }
        else
        {
            Debug.Log(www.text);
        }
    }

ヘッダをつけたい場合は次のようにします。

    var header = new Dictionary<string, string>() { { "foo", "hoge" } };
    var www = new WWW(url, null, header);

Observable from コルーチン

UniRx を使ってコルーチンを Observable に変換する実装例です。

    void Start()
    {
        Observable.FromCoroutine<string>(observer => GetRequestCoroutine(observer))
            .Subscribe(x => Debug.Log(x))
            .AddTo(this);
    }

    private IEnumerator GetRequestCoroutine(IObserver<string> observer)
    {
        var www = new WWW(url);
        yield return www;

        if (www.error != null)
        {
            Debug.Log("failure.");
        }
        else
        {
            observer.OnNext(www.text);
            observer.OnCompleted();
        }
    }

ObservableWWW

UniRx の ObservableWWW を使った実装例です。

    ObservableWWW.Get(url)
        .Subscribe(
            x => Debug.Log(x),
            ex => Debug.Log(ex.Message),
            () => Debug.Log("completed."))
        .AddTo(this);

進捗を通知したい場合は、 ScheduledNotifier を作って、Get の引数 progress に渡します。

    var progress = new ScheduledNotifier<float>();
    progress.Subscribe(prog => Debug.Log(prog))
        .AddTo(this);
    
    ObservableWWW.Get(url, progress: progress)
        ...

UniTask

UniTask を使った実装例です。UniTask を使うと、yield returnawaitに置き換える(await する)ことができます。

    void Start()
    {
        await GetRequestAsync();
    }
    private async UniTask GetRequestAsync()
    {
        var www = new WWW(url);
        await www;

        if (www.error != null)
        {
            Debug.Log("failure.");
        }
        else
        {
            Debug.Log(www.text);
        }
    }

POST

WWW

POST する場合の実装例です。 WWWForm を使います。それ以外の処理の流れは GET と同じになります。

    var form = new WWWForm();
    form.AddField(key, zipcode);

    var www = new WWW(url, form);

Formのデータと画像(バイナリ)を送信する場合は、WWWFormに追加します。

    var postData = new byte[] { 1, 2 };
    form.AddBinaryData("image", postData, "sample.png", "image/png");

ヘッダをつけて、画像(バイナリ)だけを送信する場合、WWW のコンストラクタに渡します。

    var header = new Dictionary<string, string>() 
    {
        { "foo", "hoge" },
        { "Content-Type", "application/octet-stream" },
    };
    var postData = new byte[] { 1, 2 };
    var www = new WWW(url, postData, header);

ObservableWWW

UniRX の ObservableWWW の実装例です。

    var form = new WWWForm();
    form.AddField(key, zipcode);

    ObservableWWW.Post(url, form)
        .Subscribe(x => Debug.Log(x))
        .AddTo(this);

UnityWebRequest

GET

コルーチン

コルーチンを使った実装例です。

    void Start()
    {
        StartCoroutine("GetRequestCoroutine");
    }

    private IEnumerator GetRequestCoroutine()
    {
        var request = UnityWebRequest.Get(url);
        yield return request.SendWebRequest();

        if (request.isHttpError || request.isNetworkError)
        {
            Debug.Log("failure.");
            Debug.Log(request.error);
        }
        else
        {
            Debug.Log(request.downloadHandler.text);
        }
    }

ヘッダを付ける場合は、次のようになります。

    var request = UnityWebRequest.Get(url);
    request.SetRequestHeader("foo", "hoge");
    request.SetRequestHeader("bar", "fuga");

Observable from コルーチン

UniRx を使ってコルーチンを Observable に変換する実装例です。

    void Start()
    {
        Observable.FromCoroutine<string>(observer => GetRequestCoroutine(observer))
            .Subscribe(x => Debug.Log(x))
            .AddTo(this);
    }

    private IEnumerator GetRequestCoroutine(IObserver<string> observer)
    {
        var request = UnityWebRequest.Get(url);
        yield return request.SendWebRequest();
        
        if (request.isHttpError || request.isNetworkError)
        {
            Debug.Log("failure.");
        }
        else
        {
            observer.OnNext(request.downloadHandler.text);
            observer.OnCompleted();
        }
    }

UniTask

UniTask を使った実装例です。UniTask を導入することで await できるようになります。

    void Start()
    {
        await GetRequestAsync();
    }

    private async UniTask GetRequestAsync()
    {
        var request = UnityWebRequest.Get(url);
        await request.SendWebRequest();

        if (request.isHttpError || request.isNetworkError)
        {
            Debug.Log("failure.");
        }
        else
        {
            Debug.Log(request.downloadHandler.text);
        }
    }

UniTask を Observable に変換したい場合は、Hot/Coldの注意が必要です。

    // 呼び出しと同時に実行される(Hot)
    GetRequestAsync().ToObservable();
    
    // Subscribeするまで実行されない(Cold)
    Observable.Defer(() => GetRequestAsync().ToObservable());

POST

POST する場合の実装例です。 WWWForm を使います。

   var form = new WWWForm();
   form.AddField(key, zipcode);

   var request = UnityWebRequest.Post(url, form);

Form のデータと画像(バイナリ)を送信する場合は、WWWForm に追加します。

    var postData = new byte[] { 1, 2 };
    form.AddBinaryData("image", postData, "sample.png", "image/png");

ヘッダをつけて、画像(バイナリ)だけを送信する場合、UnityWebRequestのヘッダとUploadHandlerを設定します。

    var request = UnityWebRequest.Post(url, form);
    request.SetRequestHeader("foo", "hoge");
    request.SetRequestHeader("Content-Type", "application/octet-stream");

    var postData = new byte[] { 1, 2 };
    request.uploadHandler = new UploadHandlerRaw(postData);
    request.uploadHandler.contentType = "application/octet-stream";
    request.downloadHandler = new DownloadHandlerBuffer();

HttpWebRequest

[Api Compability Level] が [.NET 4.x] であれば直接使えます。

GET

Callbak

コールバックを使った非同期の実装例です。

        try
        {
            var request = WebRequest.Create(url);

            request.BeginGetResponse(x =>
                {
                    var response = (HttpWebResponse)request.EndGetResponse(x);
                    if (response.StatusCode != HttpStatusCode.OK)
                    {
                        Debug.Log("failure.");
                    }
                    else
                    {
                        using (var reader = new StreamReader(response.GetResponseStream()))
                        {
                            var text = reader.ReadToEnd();
                            Debug.Log(text);
                        }
                    }
                },
                null);
        }
        catch (WebException e)
        {
            Debug.Log(e.Message);
        }
        catch (Exception e)
        {
            Debug.Log(e.Message);
        }

ヘッダをつけたい場合は次のようにします。

    request.Headers.Add("foo", "hoge");

Observable

UniRx で Observable に実装する例です。

    Observable.Start(() =>
        {
            var request = WebRequest.Create(url);
            var response = request.GetResponse();

            using (var reader = new StreamReader(response.GetResponseStream()))
            {
                return reader.ReadToEnd();
            }
        })
        .Subscribe(x => Debug.Log(x))
        .AddTo(this);

GetResponse には代替となる拡張メソッドが用意されてたりもします。

    WebRequest.Create(url)
        .GetResponseAsObservable()
        .Select(res =>
        {
            using (var reader = new StreamReader(res.GetResponseStream()))
            {
                return reader.ReadToEnd();
            }
        })
        .Subscribe(
            x => Debug.Log(x),
            error => Debug.Log(error.Message),
            () => Debug.Log("completed."))
        .AddTo(this);
}

UniTask (Task)

async/await を使った実装例です。UniTask は Task に書き換えても動きます。

    void Start()
    {
        await GetRequestAsync();
    }

    private async UniTask GetRequestAsync()
    {
        var request = WebRequest.Create(url);
        var response = await request.GetResponseAsync();
        using (var reader = new StreamReader(response.GetResponseStream()))
        {
            var text = reader.ReadToEnd();
            Debug.Log(text);
        }
    }

POST

POST する場合の実装例です。

    var request = WebRequest.Create(url);
    request.Method = "POST";

    var param = $"{key}={zipcode}";
    var data = Encoding.ASCII.GetBytes(param);

    request.ContentType = "application/x-www-form-urlencoded";
    request.ContentLength = data.Length;
    using (var stream = request.GetRequestStream())
    {
        stream.Write(data, 0, data.Length);
    }

画像(バイナリ)を送信する場合の実装例です。

    var postData = new byte[] { 1, 2 };
    request.ContentType = "image/png";
    request.ContentLength = postData.Length;
    using(var stream = request.GetRequestStream())
    {
        stream.Write(postData, 0, postData.Length);
    }

ヘッダを付けて、画像(バイナリ)を送信する場合の実装例です。

    request.Headers.Add("foo", "hoge");
    request.Headers.Add("Content-Type", "application/octet-stream");
    var postData = new byte[] { 1, 2 };
    request.ContentType = "image/png";
    request.ContentLength = postData.Length;
    using (var stream = request.GetRequestStream())
    {
        stream.Write(postData, 0, postData.Length);
    }

HttpClient (System.Net.Http)

GET

Task を使った実装例です。 UniTask に書き換えても動きます。

    private async Task GetRequestAsync()
    {
        var content = new FormUrlEncodedContent(new Dictionary<string, string>
        {
            { key, zipcode },
        });
        var encodedQuery = await content.ReadAsStringAsync();

        var response = await httpClient.GetAsync($"{url}?{encodedQuery}");

        if (!response.IsSuccessStatusCode)
        {
            Debug.Log("failure.");
            Debug.Log(response.ReasonPhrase);
        }
        else
        {
            var text = await response.Content.ReadAsStringAsync();
            Debug.Log(text);
        }
    }

ヘッダを付ける場合の実装例です。

    var request = new HttpRequestMessage(HttpMethod.Get, url);
    request.Headers.Add("foo", "hoge");
    var response = await httpClient.SendAsync(request);

次のようにもできますが、HttpClient のデフォルトとなっているので注意が必要です。

    httpClient.DefaultRequestHeaders.Add("foo", "hoge");

async/await が使えない場合には、Task.Run が使えます。

    Task.Run(async () =>
    {
        await Task.Delay(3000);
    });

POST

POST する場合の実装例です。 GetAsync の代わりに PostAsync を使います。

    var content = new FormUrlEncodedContent(new Dictionary<string, string>
    {
        { key, zipcode.ToString() },
    });

    var response = await httpClient.PostAsync(url, content);

画像(バイナリ)を送信する場合の実装例です。

    var postData = new byte[] { 1, 2 };
    var byteContent = new ByteArrayContent(postData);
    byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
    var res = await httpClient.PostAsync(url, byteContent);

こちらは HttpRequestMessage を使った例です。呼び出す時は SendAsync に代わります。

    var request = new HttpRequestMessage(HttpMethod.Post, url);
    var postData = new byte[] { 1, 2 };
    var byteContent = new ByteArrayContent(postData);
    request.Content = byteContent;
    request.Content.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
    var res = await httpClient.SendAsync(request);

MultipartFormDataContent を使った実装例です。

    var request = new HttpRequestMessage(HttpMethod.Post, url);
    request.Headers.Add("foo", "hoge");
    var postData = new byte[] { 1, 2 };
    var byteContent = new ByteArrayContent(postData);
    byteContent.Headers.Add("Content-Type", "image/jpeg");

    var data = new MultipartFormDataContent();
    data.Add(new StringContent(zipcode.ToString()), key);
    data.Add(byteContent, "file", "test.jpg");
    request.Content = data;
    var res = await httpClient.SendAsync(request);

サーバーサイドでバイナリを取り出す時には方法が異なるので注意が必要です。
以下は Azure Functions での例です。

ByteArrayContent を直接使った場合は、Body から取り出します。

    using(var ms = new MemoryStream())
    {        
        req.Body.CopyTo(ms);
        ms.Position = 0;
    }

MultipartFormDataContent を使った場合は、File から取り出します。

    using(var ms = new MemoryStream())
    {
        req.Form.Files[0].CopyTo(ms);
        ms.Position = 0;
    }

HttpClient (Windows.Web.Http)

UWP の WinRT API なので、すべてのコードは #if WINDOWS_UWP で囲む必要があります。

GET

Task を使った実装例です。 UniTask に書き換えても動きます。

using Windows.Web.Http;
    private static readonly HttpClient httpClient = new HttpClient();

    public async UniTask GetRequestAsync()
    {
        var builder = new UriBuilder(url);
        builder.Query = query;

        var response = await httpClient.GetAsync(builder.Uri);

        if (!response.IsSuccessStatusCode)
        {
            Debug.Log("failure.");
            Debug.Log(response.ReasonPhrase);
        }
        else
        {
            var text = await response.Content.ReadAsStringAsync();
            Debug.Log(text);
        }
    }

ヘッダを付ける場合の実装例です。HttpRequestMessage を使って設定します。

    var request = new HttpRequestMessage(HttpMethod.Get, builder.Uri);
    request.Headers.Add("foo", "hoge");
    var response = await httpClient.SendRequestAsync(request);

次のようにもできますが、HttpClient のデフォルトとなっているので注意が必要です。

    httpClient.DefaultRequestHeaders.Add("foo", "hoge");

POST

POST する場合の実装例です。 GetAsync の代わりに PostAsync を使います。

    var content = new HttpFormUrlEncodedContent(new Dictionary<string, string>
        {
            { key, zipcode.ToString() },
        });

    var uri = new Uri(url);
    var response = await httpClient.PostAsync(uri, content);
    var text = await response.Content.ReadAsStringAsync();
    Debug.Log(text);

ヘッダを付けて画像(バイナリ)を送信する場合の実装例です。MultipartFormDataContent を使っています。

    var request = new HttpRequestMessage(HttpMethod.Post, uri);
    request.Headers.Add("foo", "hoge");
    var file = await KnownFolders.CameraRoll.GetFileAsync("test.jpg");
    var buffer = await FileIO.ReadBufferAsync(file);
    var byteContent = new HttpBufferContent(buffer);
    byteContent.Headers.Add("Content-Type", "image/jpeg");

    var data = new HttpMultipartFormDataContent();
    data.Add(new HttpStringContent(zipcode.ToString()), key);
    data.Add(byteContent, "file", "test.jpg");
    request.Content = data;

    var res = await httpClient.SendRequestAsync(request);

参考

後から見直したくなりそうなものや、一度目を通しておいたほうが良さそうなものなどを残しておきます。

Unity

.NET 一般

サービス

  • PTS - V2
    • POSTの検証用サーバー「Post Test Server V2」
    • IDを入力するか、ランダムのボタンを押すと専用APIのページが表示される
    • リクエストを投げて画面を更新すると、POSTの内容がダンプされるので便利
    • POSTだけでなくGETでも使える
      f:id:yotiky:20200620010650p:plain:w400
      f:id:yotiky:20200620010858p:plain:w300

Unity と C# のバージョン

対応表

Unity Scripting Runtime API Compability Level C# Version Doc
2017.1 .NET 3.5 / .NET 4.6 .NET 2.0 / .NET 4.6 4 / 6 platform毎
2017.2 .NET 3.5 / .NET 4.6 .NET 2.0 / .NET 4.6 4 / 6 platform毎
2017.3 .NET 3.5 / .NET 4.6 .NET 2.0 / .NET 4.6 4 / 6 platform毎
2017.4 .NET 3.5 / .NET 4.6 .NET 2.0 / .NET 4.6 4 / 6 platform毎
2018.1 .NET 3.5 / .NET 4.X .NET 4.X /
.NET Standard 2.0
4 / 6 platform毎
2018.2 .NET 3.5 / .NET 4.X .NET 4.X /
.NET Standard 2.0
4 / 6 platform毎
2018.3 .NET 3.5(非推奨) / .NET 4.X (4.6相当) .NET 4.X /
.NET Standard 2.0
4 / 7.3 compiler
2018.4 .NET 3.5(非推奨) / .NET 4.X (4.6相当) .NET 4.X /
.NET Standard 2.0
4 / 7.3 compiler
2019.1 .NET 3.5(非推奨) / .NET 4.X (4.6相当) .NET 4.X /
.NET Standard 2.0
4 / 7.3 compiler
2019.2 (.NET 4.6 相当) .NET 4.X /
.NET Standard 2.0
7.3 compiler
2019.3 (.NET 4.6 相当) .NET 4.X /
.NET Standard 2.0
7.3 compiler
2019.4 (.NET 4.6 相当) .NET 4.X /
.NET Standard 2.0
7.3 compiler
2020.1 (.NET 4.6 相当) .NET 4.X /
.NET Standard 2.0
7.3 compiler
2020.2 (.NET 4.6 相当) .NET 4.X /
.NET Standard 2.0
8.0 compiler
2020.3 (.NET 4.6 相当) .NET 4.X /
.NET Standard 2.0
8.0 compiler
2021.1 (.NET 4.6 相当) .NET 4.X /
.NET Standard 2.0
8.0 compiler
2021.2 (.NET 5 ?) .NET Framework /
.NET Standard 2.1
9.0 compiler
2021.3 .NET Framework /
.NET Standard 2.1
9.0 compiler
2022.1 .NET Framework /
.NET Standard 2.1
9.0 compiler

(2021.2.1f1)

(2022.1.0a16.2319)

C# 新機能

サイト「++C++; // 未確認飛行 C」より

HoloLens2 で AppPackage のバージョン番号を取得する

検証環境

  • Unity:2019.2.7f2
  • VisualStudio:2019
  • Device:HoloLens2

コード

UnityEngine.Application.version で取れるのは、PlayerSettings の Version である。

f:id:yotiky:20200616235600p:plain

一方、HoloLens のアプリケーションのバージョンはこれとは別で、Unity ビルドで出力したプロジェクトや MRTK の BuildWindow で見る(設定する)ことができる。

f:id:yotiky:20200616235718p:plain:w350

このバージョン番号をスクリプトで取得するには以下のようにする。

#if !UNITY_EDITOR && UNITY_WSA
using Windows.ApplicationModel;
#endif

    public static string GetAppVersion()
    {
#if !UNITY_EDITOR && UNITY_WSA
        var package = Package.Current;
        var packageId = package.Id;
        var version = packageId.Version;
        return $"{version.Major}.{version.Minor}.{version.Build}.{version.Revision}";
#else
        return Application.version;
#endif
    }

実行結果

f:id:yotiky:20200616235347p:plain:w300

HoloLens で使用する #define ディレクティブ

TL;DR

  • HoloLens 実機向けにUWPのAPIを使って実装するなら WINDOWS_UWP
  • マルチデバイスな環境で、Unityで使えるUWPのAPIを使うなら UNITY_WSA

プラットフォーム依存コンパイル

検証環境

  • Windows 10
  • Unity 2019.2.7f.2
  • プラットフォーム : UWP
  • Script Backend : IL2CPP
  • 実行環境 : HoloLens2 / Editor

環境に関連のある #define

docs.unity3d.com

Unity のプラットフォーム依存コンパイルでサポートしている #define ディレクティブのうち HoloLens 開発に関連してそうなものを抜粋する。
○がついているのはスクリプトが実行される環境である。
なお、Unity では使えない .NETAPIなどを使う場合は、使うものに合わせた #define を使用すること。

Define 機能 HoloLens2 Editor
UNITY_EDITOR エディターコードのための #define ディレクティブ
UNITY_WSA UWPのための #define ディレクティブ
NETFX_CORE UWPで .NET Core 向けに .NET Scripting Backend を使用する場合の #define ディレクティブ
UNITY_WSA_10_0 Windows10 向けのUWPのための #define ディレクティブ
WINDOWS_UWP Windows10 向けのUWPで .NET Core 向けにコンパイルする場合の #define ディレクティブ
ENABLE_IL2CPP IL2CPP のスクリプティングバックエンド #define ディレクティブ

Editor で実行した場合
f:id:yotiky:20200616203836p:plain

HoloLens2 で実行した場合
f:id:yotiky:20200616233314p:plain:w300

MRTKのコーディングガイドラインにも参考になるものがあると教えて頂いたので掲載。

microsoft.github.io

HoloLens2 の初期セットアップ

  1. 購入直後、もしくは設定から初期化、もしくはリカバリーツールによる復旧などの状態

    Windows Update を手動で元に戻す方法はこちらに。
    OSのリカバリでおよそ50min程度かかります。

  2. HoloLens2 のセットアップ(チュートリアル

    1. 言語と地域
    2. 視線調整
    3. ネットワーク設定
    4. アカウント設定と虹彩認証のセットアップ
    5. 音声認識の設定

    docs.microsoft.com

  3. 設定 > 更新とセキュリティ

    1. Windows Update でアップデートを実行する
      ※場合によっては Update しないほうが良いケースもあり
    2. 開発者向け で[開発者モード]と[デバイスポータル]をON
  4. 設定 > 時刻と言語 > 日付と時刻 でタイムゾーンを設定する

  5. 設定 > アカウント > サインインオプション でサインインを求める に [なし]を設定する(取り扱い注意)

  6. 設定 > システム > 調整 で「視線調整」の自動実行をOFF(任意)

  7. PCとUSB接続して、デバイスポータルにアクセスしてペアリングする
    ※証明書エラーが出る場合は、以下のページを参考にする

    docs.microsoft.com

  8. バイスポータルで、System > Preferences を開く

    1. Device name に管理しやすい名前をつける
    2. Sleep settings の2つの項目を[30 minutes]に設定する
  9. Visual Studio からインストールする際にPINを求められたら、設定 > 更新とセキュリティ > 開発者向け で[ペアリング]を実行する

  10. ストアからインストールする

    • Holographic Remoting Player

MRTK v2 - 2.3.0から2.4.0へのアップデート

公式はこちら。

microsoft.github.io

2.4.0 での重大な変更に対応するために migration tool が提供されており、MRTK を更新した後に実行することが強く推奨されている。migration tool は、Tools パッケージに同梱されている。

準備

下記のパッケージをダウンロードする

  • Microsoft.MixedReality.Toolkit.Unity.Foundation.2.4.0.unitypackage
  • Microsoft.MixedReality.Toolkit.Unity.Tools.2.4.0.unitypackage
  • Microsoft.MixedReality.Toolkit.Unity.Extensions.2.4.0.unitypackage (option)
  • Microsoft.MixedReality.Toolkit.Unity.Examples.2.4.0.unitypackage (option)

手順

  1. Unity と Visual Studio を閉じる
  2. プロジェクトのバックアップを取る
  3. Assets フォルダのMRTKのフォルダとそれらの .meta ファイル を削除する

    • MixedRealityToolkit
    • MixedRealityToolkit.Examples
    • MixedRealityToolkit.Extensions
    • MixedRealityToolkit.Providers
    • MixedRealityToolkit.SDK
    • MixedRealityToolkit.Services
    • MixedRealityToolkit.Staging
    • MixedRealityToolkit.Tools

    ※MixedRealityToolkit.Generated フォルダは削除しない

  4. Library フォルダを削除する

  5. Unity でプロジェクトを開く
  6. MRTK 2.4 を import する
    1. Foundation
    2. Tools
    3. Extentions (option)
    4. Examples (option)
  7. Unity を閉じて、 Library フォルダを削除する
  8. Unity でプロジェクトを開く
  9. シーンごとに、
    1. MixedRealityToolkit と MixedRealityPlayspace を削除する
    2. MixedRealityToolkit > Add to Scene and Config を実行する
    3. MixedRealityToolkit > Utilities > Update > Controller Mapping Profiles を実行する
  10. MixedRealityToolkit > Utilities > Migration Window を開く
    f:id:yotiky:20200616163342p:plain:w400

    マイグレーション対象だと警告が出ている。
    f:id:yotiky:20200616155919p:plain:w300

    1. 対象の機能 (Handler) を選択する
    2. マイグレーションする範囲のタブを選択する
      • オブジェクトモード
        • 対象のオブジェクトを個別に選べる
        • 複数登録して一括で実行できるが、登録するのは1個ずつしかできなそう
        • 個別に実行する場合は対象を検索すると良い f:id:yotiky:20200616161346p:plain
      • シーンモード
        • 対象のシーンを個別に選べる(Projectウィンドウからドラッグ&ドロップ)
        • 同じく1個ずつしか登録できなそう
      • プロジェクトモード
        • [Add full projcet for migration]を実行する
        • シーンが読み込まれるので、[Migration]を実行する
        • 使ってない prefab がどうなるか要確認
        • Handler 毎に実行する必要があるか要確認

    Migration Window の詳細はこちらから。

    microsoft.github.io

  11. Build Settings で Platform を UWP にする