Mixed Reality Toolkit V2.2.0 の新しいCamera SystemおよびiOS/Android対応について

# ついにMixed Reality Toolkit V2がARFoundation対応(Experimentalですが)
夏頃にMixed Reality Toolkit(以下MRTK) V2を単純にAndroidにデプロイしたことがありました。その時にエラーなく起動してしまったので、もしかするとARCoreと組合わせたら動くのでは？ということで色々試した結果最終的にARFouncdationによる6Dof制御を使えば、モバイルアプリとしてMRTK V2が使えることが判明。

* Android上で動作させたプロトタイプ
 
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Mixed Reality Toolkit V2.0.0でもAndroidで動いた。6DofはARFoundationで実現。タッチ操作は標準のものを少しだけ手直し。<a href="https://twitter.com/hashtag/MRTKV2?src=hash&amp;ref_src=twsrc%5Etfw">#MRTKV2</a> <a href="https://twitter.com/hashtag/MixedRealityToolkit?src=hash&amp;ref_src=twsrc%5Etfw">#MixedRealityToolkit</a> <br>MRTK V2.0.0 is running on Android <a href="https://t.co/un86C4li0T">https://t.co/un86C4li0T</a> <a href="https://twitter.com/YouTube?ref_src=twsrc%5Etfw">@YouTube</a>さんから</p>&mdash; takabrz1@MRTKV2とAzure Spatial Anchorsと格闘中 (@takabrz1) <a href="https://twitter.com/takabrz1/status/1167403677629313025?ref_src=twsrc%5Etfw">August 30, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 
* iPhone上で動作させたプロトタイプ

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Mixed Reality Toolkit V2.0.0をiPhoneでも動かす。6DofはAndroidと同じARFoundationで実現。Unityのプラットフォームを変更する以外は同じコードで動きます。<a href="https://twitter.com/hashtag/MRTKV2?src=hash&amp;ref_src=twsrc%5Etfw">#MRTKV2</a> <a href="https://twitter.com/hashtag/MixedRealityToolkit?src=hash&amp;ref_src=twsrc%5Etfw">#MixedRealityToolkit</a> <br>MRTK V2.0.0 is running on iPhone<a href="https://t.co/dMb7ZmdsWX">https://t.co/dMb7ZmdsWX</a></p>&mdash; takabrz1@MRTKV2とAzure Spatial Anchorsと格闘中 (@takabrz1) <a href="https://twitter.com/takabrz1/status/1167435258855182337?ref_src=twsrc%5Etfw">August 30, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

その後、MRTKのサンプルとプルリク出したり色々した結果、機能として実現してくれました！

## なぜそもそもMRTKをARFoundationと組合わせてやりたかったのか
これには明確な理由が自分の中であります。  
近年、「AR Cloud」という言葉が出始めてきて、MicrosoftもHoloLens 2の発表と同時に「[Azure Spatial Anchors]」をPreviewながら提供を開始。Googleも同じようなサービスを最近になって始めたりと「クラウドに空間情報を格納して活用する」という取組みが進んでいます。  
このうち、[Azure Spatial Anchors]はHoloLens/iOS/Androidとマルチデバイス環境でAR Cloudを利用できる有効な技術です。一方でサンプル等を見ているとUnityで作るサンプルでも「3デバイスに対応するための苦労」が見え隠れしていました。  
当然ですが３デバイス向けの対応ができるといってもコード自体は共有化できるわけではなくUnityでやっていてもやはり個別対応部分が出ていてコードがわかりにくくなっていました。  
そんな中、MRTKのクロスプラットフォームに対応できる技術があればこの問題解決できるのでは？ということで始めたのがきっかけです。その後、予想通り拡張すれば３デバイスの実装をMRTK配下でほぼ共通化してしまえることが分かったので、情報をMRTKのDevチームと共有して今に至る感じです。  
Azure Spatial Anchorsを駆使したAR Cloudの取組みは色々取組んでいて以下のような話はもう少し整理が進めば公開したいと思います。

* [Azure Spatial Anchors]の解説と、MRTK V2.2.0で共通化したサンプル
* [Azure Spatial Anchors] + [Photon Engine](https://www.photonengine.com/ja-JP/Photon)+ MRTK V2.2.0によるSharing System for Cross Platform.

## MRTK V2.2.0から試験的に導入されている

また、Android,iPhone対応は現状Experimentalsなので正式に機能には追加されていない別パッケージとなっています。
使い倒してフィードバックをぜひしましょう！

## MRTK V2.2.0 をAndroid/iOSで動かす

ということで早速やってきましょうの前に今回のお題を整理します。

1. 開発環境
1. iOS/Androidで動作させる
    1. 共通の手順
    2. Androidでの導入手順
    3. iOS(iPhone)での導入手順
2. Camera Systemの変更について

以上の流れで解説します。
ちなみに、ガイドラインがいかにあるのでこれ見てもおおむね行けると思います。

[How to configure MRTK for iOS and Android [Experimental]](https://microsoft.github.io/MixedRealityToolkit-Unity/version/releases/2.2.0/Documentation/CrossPlatform/UsingARFoundation.html)

### 1.開発環境

今回は色々と使うので詳細の説明。特にバージョン周りは注意してください。今回は以下の環境で行っています。

* ソフトウェア
    * [Mixed Reality Toolkit V2.2.0]
    * Unity 2019.1.3.f1
    * ARFoundation(2.1.2)
    * Android SDK (API 24以降）
    * Xcode 10.3
* ハードウェア
    * Zenfone AR(Android 7.0)
    * ROG Phone 2(Android 9.0)
    * iPhone XR (iOS 13) 
    * Windows機(Windows 10 Pro)
    * Mac mini(iOS 10.15.2)
    
Unityは2019系、2018系どちらで大丈夫です。安定したバージョンのものを選択してください。今回はAndroid/iOSで動かすためそれぞれのビルド環境を用意する必要があります。  
また、開発環境はWindowsの方にUnityをセットアップしています。Mac miniはあくまでxcodeをビルドする目的で導入していると思ってください。
MRTK V2のコードは基本的にはUWPにも対応できるようになっている関係で、Mac上のUnityだとうまくいかない場合があります。
HoloLensまで見据えて考えると結果的にWindows上でする方が都合がいいというのもありますが。

#### Android開発環境
AndroidはARCoreが対応しているOSバージョン以降となるのでAndroid OS 7.0(API 24)以降を使用します。
Android端末についてはARCoreに対応したデバイスであればOKです。

参考サイト：[ARCore supported devices](https://developers.google.com/ar/discover/supported-devices)

後はAndroid SDKをUnityをインストールしている開発機にセットアップしておき、パスを通します。

#### iOS開発環境

iOSはiPhone系でARKitを使う場合大きくARKit2とARkit3に分かれることになると思います。

|バージョン|動作iOS|Xcodeバージョン|
|:--|:-:|:-:|
|~ARkit 2|iOS 13未満でARKitに対応しているデバイス|10.X|
|ARKit 3~|iOS 13以降でA9以降のプロセッサを搭載しているデバイス|11.X|

検証に使うデバイスの状況を確認し調整を行う必要があります。

#### ARFoundationのバージョン

上記までの話を踏まえて整理すると以下の形になります。

##### Unity 2018.4.x用

注意点として Unity 2018系の場合ARFaoundationはPreview版となるため以下の設定を行わないとリストに上がってきません。必ず変更の上設定を行ってください。
[Package Manager]ウィンドウを開いて上部の[Advanced]が表示されているコンボボックスから[Show Preview Packages]を選択します。

* ARKit2の場合  

|Android|iOS|
|:--|:--|
|AR Foundation Version: 1.5.0 - preview 6|AR Foundation Version: 1.5.0 - preview 6|
|ARCore XR Plugin Version: 2.1.2|ARKit XR Plugin Version: 2.1.2	|

* ARKit3の場合  

  最新のARKitをアプリケーションで利用したい場合は以下のバージョンで設定します。ただしこの場合はXCode 11でのビルドが必要になりますので、開発環境も併せて調整が必要です。

|Android|iOS|
|:--|:--|
|AR Foundation Version: 1.5.0 - preview 6|AR Foundation Version: 1.5.0 - preview 6|
|ARCore XR Plugin Version: 2.2.2|ARKit XR Plugin Version: 2.2.2	|


##### Unity 2019.x用

Unity2019については、Previewではなく正規なPackageとして提供されています。Androidについては特に問題ないですが、ARKit側は先ほどのiOSのバージョンによって使い分けを行う必要が出てきます。

* ARKit2の場合  

|Android|iOS|
|:--|:--|
|AR Foundation Version: 2.1.4|AR Foundation Version: 2.1.4|
|ARCore XR Plugin Version: 2.1.2|ARKit XR Plugin Version: 2.1.2|

* ARKit3の場合  

  最新のARKitをアプリケーションで利用したい場合は以下のバージョンで設定します。ただしこの場合はXCode 11でのビルドが必要になりますので、開発環境も併せて調整が必要です。

|Android|iOS|
|:--|:--|
|AR Foundation Version: 2.2.0 preview-6|AR Foundation Version: 2.2.0 preview-6|
|ARCore XR Plugin Version: 2.2.0 preview-6|ARKit XR Plugin Version: 2.2.0 preview-6|

上記設定に気を付けながら実際にMRTK V2をモバイルデバイスで動かしてみましょう

### 2. iOS/Androidで動作させる

以降はUnity 2019系で説明します。Unity 2018系でも基本的な流れは特に変わりません。
今回はMRTK V2ではド定番となっている（？）サンプル[HandTrackingInteraction]を利用します。
これはHoloLens 2向きのハンドトラッキング用のサンプルなのですが、ボタン押下や、物体の移動等オブジェクトの操作を一通りできるため、ベンチマーク的によく使われています。

#### 2.1. 共通の手順

まずはAndroid/iOS共通の設定を行っていきます。

##### ARFoundationとMRTK V2のセットアップ

まずUnityを3Dプロジェクトタイプで生成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/e0ef677c-b79d-79d7-75d6-865693b21c0b.png)

なお、パス階層は浅めがおすすめです。既知の問題としてパスが長いとエラーになる事がある記載がありました。

次にPackage ManagerからARFoundationに関連する3つのパッケージをインストールします。
[Window] > [Package Manager]を選択してPackage Managerを表示します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/4b6d3a78-7798-c5f4-a3c3-b70078ed589f.png)

バージョンなどは上記で説明した内容を参考に適切なものを選択してください。Unity 2019系の場合は以下のバージョンとなります。基本的にはAR Foundationのバージョンにすべて合わせます（この場合2.1系に合わせるというイメージ）

* AR Foundation 2.1.4
* ARKit XR Plugin 2.1.2
* ARCore XR Plugin 2.1.2

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/5ab9df71-c2f8-3ff4-e3c6-0d7a4be5e3af.png)

参考までにですがUnity 2018系で動作させた場合、利用するAR FoundationがPreview版のため、ダイアログの [Advanced] > [Show preview Package]を選択しPreview版パッケージを表示する必要があります。Unity2019系でこれを表示するとAR Foundation 2.2.0が表示されます。これが先ほど説明したiOS 13用でXcode 11系でないとビルドできないものになります。

インストールが完了したら[Mixed Reality Toolkit V2.2.0]を適用します。適用順は以下の通りで進めてください。

1. Microsoft.MixedReality.Toolkit.Unity.Foundation.2.2.0.unitypackage  
MRTK V2のベース機能となるパッケージです。まずは初めにこれをインポートしてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/c97ba35e-2a40-2857-f815-edb6a1be263f.png)
2. Microsoft.MixedReality.Toolkit.Unity.Examples.2.2.0.unitypackage  
MRTK V2のサンプル集です。今回はこの中にある[HandTrackingInteraction]をビルドします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/5989bd14-adfd-8301-028a-40f90f15c35e.png)
3. Microsoft.MixedReality.Toolkit.Unity.Providers.UnityAR.2.2.0.unitypackage  
最後に今回のメインとなるUnityARコンポーネントを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/bb0213fd-65eb-9b0a-97a0-1e746ffdf6b6.png)

この時点で、namespaceに関係するエラーが出るはずです。一部のアセンブリの参照がないためなのですが、
ここについては手作業で修正を行う必要があります。

1. [Hierarchy]タブ内の[MixedRealityToolkit.Staging] > [UnityAR] > [Microsoft.MixedReality.Toolkit.Providers.UnityAR]を選択
2. [Inspector]タブ内の[Assembly Definition Reference]内の+を押して新しい参照を追加
3. 追加した行の検索窓を表示し、[UnityEngine.SpatialTracking]を追加
4. 下部の[Apply]を押下して設定を反映

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/6aa597c7-3543-fdc0-1821-f4de08a6e498.png)

上記でNamespace関連のエラーはなくなり、開発ができる準備が整いました。
次にサンプルをAndroid/iOSで動くように設定を変更します。

##### サンプルをAndroid/iOSに対応する
サンプルシーン[HandInteractionExamples.unity]を開きます。
[File] > [Open Scene]を選択し、[MixedRealityToolkit.Examples] > [Demos] > [HandTracking] > [Scenes]内の[HandInteractionExamples.unity]を選択します。
初回起動時はTMPのインポートをするようにダイアログが表示されます。MRTKの文字出力はこのコンポーネントに依存しているため初回にこのようなダイアログが表示されます。必要になりますのでインポートを行いましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/fcb947ea-e34e-bcea-d851-6dbd834518c2.png)

全ての作業が完了すると以下のようにサンプルが表示された状態になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/8f1f4094-cdc4-9c13-4a93-5babba6844a3.png)

このシーンはHoloLens用の設定になっているので、これをARFoundationで動くように修正していきます。
[Hierarchy]タブから[MixedRealityToolkit]コンポーネントを選択し[Inspector]からパラメータの変更を行います。
まずは変更するためにProfileのコピーを行います。
[Inspector]タブ内の[Mixed Reality Toolkit]コンポーネントの中にある[Copy & Customize]を押してダイアログを表示します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/d39e8598-6bcf-88d4-8dbe-3bf0b1487839.png)

[Clone Profile]ダイアログが表示されるので以下の通り設定してコピーを作成してください。

* [Cloning Profile]は[DefalutHoloLens2ConfigurationProfile]を選択
* [Profile Name]に任意の名前を入力
* [Clone]ボタンを押下

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/d23624f4-d039-e2a4-f6e3-3d1b1c62c574.png)

次にCamera Systemのプロファイルを変更します。同じく[Inspector]タブ内の[Mixed Reality Toolkit]コンポーネントの[camera]メニュー選択します。この中にある設定を変更します。
変更するためには先ほどと同様にProfileのCloneが必要になります。[Clone]ボタンを押下して[Clone Profile]ダイアログを表示します。
表示後[Profile Name]を任意の名前に変更して[Clone]ボタンを押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/a1c8985f-f771-3472-5469-8203fba75f37.png)

保存に成功すると先ほど非活性だったら入力部分が入力可能になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/50255d3f-9574-2dd2-f31b-ba81f1295913.png)

最後に[Camera Settings Provider]にARFaoundation用のカメラ設定を追加します。
[Camera Settings Provider]の中にある[Add Camera Settings Provider]を押すと、行が追加されます。
追加された行を展開して中にある[Type]のコンボボックスを選択し[UnityARCameraSettings]を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/f7d4f1c2-a2ab-99b5-83d8-3772157dd53d.png)

設定が完了すると以下のようになります。
今回はそのまま変更しなくても大丈夫です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/e85905be-7f3c-a4ce-271c-0a028a0208c0.png)

以上で設定は完了です。最後に各デバイスにデプロイするための設定を追加します。

#### 2.2. Androidでの導入手順

AndroidについてはビルドしてAPKを作成し、デバイスにデプロイすることができます。テストレベルであれば特に問題はないかと思います。正式に出す場合は正規の手続きを経て公開するようにしてください。

File > Build Settingsからビルド設定を開きます。
対象のシーンに[HandTrackingInteractionExamples]を加えておきます。
[Platform]は[Android]にして[Switch Platform]を行います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/6d1458ee-3268-31ef-93ce-a65850794c43.png)

そのあと、[Player Settings]でAndroid用のビルドパラメータを調整します。
と言いながら必要な最小限の設定はメニューから行えるようになっています（素晴らしい！）
[Mixed Reality Toolkit] > [Utilities] > [Configure Unity Project]を選択して[MRTK Project Configure]ダイアログを表示します。
 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/d8a16964-85c2-c5c1-7543-605a3008018c.png)

このような形で環境に必要な情報を自動で設定できるようになっています。
緑でチェックが入っている状態のものはすでに設定済みで特に変更不要の項目になります。[Apply]を押して設定を反映させます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/d9a1f18e-fbc5-b8e8-afdf-0957b513ea6e.png)

その後、[Build Settings]に戻り、[Player Settings]ボタンを押下して、設定を開きます。
[Product Name]および、[Identification] > [Package Name]をデフォルトから変更します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/398caab5-20d5-b4ee-6889-70b5f2deb626.png)


最後に[Build Settings]の[Build]ボタンを押下して、APKファイルを作成してください。
生成されたAPKファイルをデバイスにインストールすれば完了です。

#### 2.3. iOS(iPhone)での導入手順

iOSについても、Unity上での作業はAndroidとそう変わらないですが、iOSで動作させるためには一度XCode用にUnityからビルドを実施、その後Mac上でビルドしてiPhone等にデプロイします。

File > Build Settingsからビルド設定を開きます。
対象のシーンに[HandTrackingInteractionExamples]を加えておきます。
[Platform]は[iOS]にして[Switch Platform]を行います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/af1d6f5e-e274-2bcd-f7ee-62da61b90a8b.png)

そのあと、[Player Settings]でiOS用のビルドパラメータを調整します。
[Mixed Reality Toolkit] > [Utilities] > [Configure Unity Project]を選択して[MRTK Project Configure]ダイアログを表示します。
 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/d8a16964-85c2-c5c1-7543-605a3008018c.png)

このような形で環境に必要な情報を自動で設定できるようになっています。
緑でチェックが入っている状態のものはすでに設定済みで特に変更不要の項目になります。[Apply]を押して設定を反映させます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/5c0a9594-1029-ae7d-c252-292020adc279.png)

その後、[Build Settings]に戻り、[Player Settings]ボタンを押下して、設定を開きます。
[Product Name]および、[Identification] > [Bundle Identifier]をデフォルトから変更します。 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/fd5857e5-bc87-a097-ee9a-7c6f6da793a8.png)

Buildを実行し、出力先を選択するとビルドが開始されたXcode用の資産が生成されます。
生成された資産をフォルダごとをMacにコピーし、Xcodeを立ち上げます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/7a9fddcf-b546-0326-e06a-180833fc96da.png)

フォルダーのルートを選択し、プロジェクトの設定を行い、
USB接続でiPhone等を繋ぎデプロイをすれば実行できます。


### 3. Camera Systemの変更について

今回V2.2.0のリリースにあたりCameraSystemの変更が入っています。V2.1.0まではXRデバイス毎の設定変更を[Camera System Type]で変更する必要がありました。
V2.2.0からはCameraSystemにCameraSettingsProviderが追加され、このProviderを変更することで各デバイスの切替えを行うようになりました。
この構成自体はInputSystemで実現されている方式と同じになります。CameraSystemでMRTK内のCamera機能の有効/無効を定義し、その中にあるProviderを実行するデバイスタイプに応じてロードするという構造になります。

#### UnityARCameraSettings

今回試験導入されたARFaoundation用のカメラ設定クラスです。やっていることは非常に簡単で、MRTKが動作しているデバイスが「Android」「iOS」の場合にARFoundation用のコンポーネント構成されたカメラを生成するというものです。
中身についていくつか紹介します。

##### 初期設定
まずは、初期設定部分になります。UnityARCameraSettingsにはクラス属性MixedRealityDataProviderが付与されています。この属性は、以下の情報と定義することができます。

* どの機能に属しているか　⇒ typeof(IMixedRealityCameraSystem)
* どのデバイスに対応しているか ⇒ SupportedPlatforms.Android | SupportedPlatforms.IOS
* 個別の設定ファイルの定義 ⇒ "UnityAR/Profiles/DefaultUnityARCameraSettingsProfile.asset"
* 名称等...

比較的直観的でわかりやすいかと思いますが、CameraSystemの機能として利用するもので、Android/iOSに対応、DefaultUnityARCameraSettingsProfileをProfileとして設定するという形になっています。
クラス属性に書かれているものはあくまで初期設定ですので、先ほどの[Mixed Reality Toolkit]のProfile設定から容易に変更できます。

``` csharp:UnityARCameraSettings.cs
/// <summary>
/// Camera settings provider for use with the Unity AR Foundation system.
/// </summary>
[MixedRealityDataProvider(
    typeof(IMixedRealityCameraSystem),
    SupportedPlatforms.Android | SupportedPlatforms.IOS,
    "Unity AR Foundation Camera Settings",
    "UnityAR/Profiles/DefaultUnityARCameraSettingsProfile.asset",
    "MixedRealityToolkit.Staging")]
public class UnityARCameraSettings : BaseCameraSettingsProvider
```
##### Profile関連
このProvider専用のProfileは初期提供が[DefaultUnityARCameraSettingsProfile]になります。
中身は現時点では3つ提供されている形です。
これらはARFoundationの機能として提供されている一部のパラメータです。変更が必要なものだけ記載されている感じですね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/3d02c8df-634b-eeae-33f3-712dc9d6f507.png)

このProfile設定はクラスのコンストラクタで呼び出されます。

Profile自体は簡単なデータ格納型のクラスとして以下のように定義されています。

``` csharp:UnityARCameraSettingsProfile.cs
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.﻿

using Microsoft.MixedReality.Toolkit.CameraSystem;
using UnityEngine;
using UnityEngine.SpatialTracking;

namespace Microsoft.MixedReality.Toolkit.Experimental.UnityAR
{
    /// <summary>
    /// Configuration profile for the XR Camera settings provider.
    /// </summary>
    [CreateAssetMenu(menuName = "Mixed Reality Toolkit/Profiles/Unity AR Foundation Camera Settings Profile", fileName = "DefaultUnityARCameraSettingsProfile", order = 100)]
    [MixedRealityServiceProfile(typeof(UnityARCameraSettings))]
    public class UnityARCameraSettingsProfile : BaseCameraSettingsProfile
    {
#region Tracked Pose Driver settings

        [SerializeField]
        [Tooltip("The portion of the device (ex: color camera) from which to read the pose.")]
        private ArTrackedPose poseSource = ArTrackedPose.ColorCamera;

        /// <summary>
        /// The portion of the device (ex: color camera) from which to read the pose.
        /// </summary>
        public ArTrackedPose PoseSource => poseSource;

        [SerializeField]
        [Tooltip("The type of tracking (position and/or rotation) to apply.")]
        private ArTrackingType trackingType = ArTrackingType.RotationAndPosition;

        /// <summary>
        /// The type of tracking (position and/or rotation) to apply.
        /// </summary>
        public ArTrackingType TrackingType => trackingType;

        [SerializeField]
        [Tooltip("Specifies when (during Update and/or just before rendering) to update the tracking of the pose.")]
        private ArUpdateType updateType = ArUpdateType.UpdateAndBeforeRender;

        /// <summary>
        /// Specifies when (during Update and/or just before rendering) to update the tracking of the pose.
        /// </summary>
        public ArUpdateType UpdateType => updateType;

#endregion Tracked Pose Driver settings
    }
}
```

##### Providerの初期化
Providerが呼び出される際の最初に実行する処理としてInitializeメソッドが用意されています。
今回はARFoundationを利用しているため、AndroidでVR機能を有効化しているかどうかでこのProviderの実行可否を判断する処理が入っています。

``` csharp:UnityARCameraSettings.cs
/// <inheritdoc />
public override void Initialize()
{
    base.Initialize();

    // Android platforms support both AR Foundation and VR.
    // AR Foundation does not use the player's XR Settings.
    // If the loaded device name is not an empty string, then a VR
    // SDK is in use (not using AR Foundation).
    if (Application.platform == RuntimePlatform.Android)
    {
       isSupportedArConfiguration = string.IsNullOrWhiteSpace(XRSettings.loadedDeviceName);
    }
}
```

##### Providerの有効化
ProvierがCameraSystemから呼び出された際に実行される処理としてEnableメソッドが用意されています。
Enableメソッドが呼び出されるとARFoundationで利用する[AR Session]および[AR Session Origin]を模倣してオブジェクトを生成するように構築しています。

``` csharp:UnityARCameraSettings.cs
public override void Enable()
{
    base.Enable();
       
    if (!isInitialized)
    {
        InitializeARFoundation();
    }
}

/// <summary>
/// Initialize AR Foundation components.
/// </summary>
/// <remarks>
/// This method ensures AR Foundation required components (ex: AR Session, Tracked Pose Driver, etc) are
/// exist or are added to the appropriate scene objects. These components are used by AR Foundation to
/// communicate with the underlying AR platform (ex: AR Core), track the device and perform other necessary tasks.
/// </remarks>
private void InitializeARFoundation()
{
    if (!isSupportedArConfiguration) { return; }

    if (isInitialized) { return; }

    FindARFoundationComponents();

    if (arSessionObject == null)
    {
        arSessionObject = new GameObject("AR Session");
        arSessionObject.transform.parent = null;
    }
    arSession = arSessionObject.EnsureComponent<ARSession>();
    arInputManager = arSessionObject.EnsureComponent<ARInputManager>();

    if (arSessionOriginObject == null)
    {
        arSessionOriginObject = MixedRealityPlayspace.Transform.gameObject;
    }
    CameraCache.Main.transform.parent = arSessionOriginObject.transform;

    arSessionOrigin = arSessionOriginObject.EnsureComponent<ARSessionOrigin>();
    arSessionOrigin.camera = CameraCache.Main;

    GameObject cameraObject = arSessionOrigin.camera.gameObject;

    arCameraManager = cameraObject.EnsureComponent<ARCameraManager>();
    arCameraBackground = cameraObject.EnsureComponent<ARCameraBackground>();
    trackedPoseDriver = cameraObject.EnsureComponent<TrackedPoseDriver>();

    trackedPoseDriver.SetPoseSource(
        TrackedPoseDriver.DeviceType.GenericXRDevice,
        ArEnumConversion.ToUnityTrackedPose(SettingsProfile.PoseSource));
    trackedPoseDriver.trackingType = ArEnumConversion.ToUnityTrackingType(SettingsProfile.TrackingType);
    trackedPoseDriver.updateType = ArEnumConversion.ToUnityUpdateType(SettingsProfile.UpdateType);
    trackedPoseDriver.UseRelativeTransform = false;

    isInitialized = true;
}
```
##### Providerの無効化
Providerが不要になった場合の後始末を行う処理としてDestroyメソッドが用意されています。
この処理では、有効化で実装した[AR Session]および[AR Session Origin]の実装を破棄して元のMRTKの標準カメラの設定に戻す処理が入っています。

``` csharp:UnityARCameraSettings.cs
/// <inheritdoc />
public override void Destroy()
{
    UninitializeARFoundation();

    base.Destroy();
}

/// <summary>
/// Uninitialize and clean up AR Foundation components.
/// </summary>
private void UninitializeARFoundation()
{
    if (!isInitialized) { return; }

    if (!preExistingArSessionOriginObject &&
        (arSessionOriginObject != null))
    {
        UnityObjectExtensions.DestroyObject(trackedPoseDriver);
        trackedPoseDriver = null;
        UnityObjectExtensions.DestroyObject(arCameraBackground);
        arCameraBackground = null;
        UnityObjectExtensions.DestroyObject(arCameraManager);
        arCameraManager = null;
        UnityObjectExtensions.DestroyObject(arSessionOrigin);
        arSessionOrigin = null;
    }

    if (!preExistingArSessionObject &&
        (arSessionObject != null))
    {
        UnityObjectExtensions.DestroyObject(arInputManager);
        arInputManager = null;
        UnityObjectExtensions.DestroyObject(arSession);
        arSession = null;
        UnityObjectExtensions.DestroyObject(arSessionObject);
        arSessionObject = null;
    }

    isInitialized = false;
}
```


#### 新しいXRデバイスへの対応

今回CameraSettingsProviderの仕組みが入ったことで（以前のCameraSystemでも容易でしたが）、現在MRTKがサポートしていないXRデバイスへの対応も実は容易になっています。
MRTKで提供されていないXRデバイスを追加する場合は、BaseCameraSettingsProviderを継承したクラスを作成し、CameraSystemで設定すれば利用可能です。実装のポイントになるのは以下の部分になります。

1. 追加したいXRデバイスのCameraオブジェクトに関する機能を調査
2. CameraSettingsProviderで復元と廃棄を実装
3. 可変値は専用のProfileを用意する
4. MRTKに別パッケージでインポートできるようにする。


##### 1.追加したいXRデバイスのCameraオブジェクトに関する機能を調査
CameraSettingsProviderで実装すべき機能は「追加したいXRデバイスのCamera関連の機能」をMRTK内で再現する実装を行います。今回のUnityARCameraSettingsProviderの場合はARFaoundationのCamera関連のオブジェクトを再現しています。
やり方は非常に簡単でとりあえずARFoundationのサンプル見てCameraに関連するオブジェクトに何があるかをみる形です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/fd66593d-6f8d-b4c8-051f-ee5559c9202e.png)

後はこの情報をもとに復元と破棄を実装する形になります。

##### 2.CameraSettingsProviderで復元と廃棄を実装
CameraSettingsProviderには先ほども説明したEnableメソッド、Destroyメソッドがあるのでここで「追加したいXRデバイスのCamera関連の機能」の復元と廃棄を実装します。もし定常的なパラメータ（オブジェクトの取得等何度も呼出すと負荷がかかるもの）はInitializeメソッドに実装します。

##### 3. 可変値は専用のProfileを用意する
追加したいXRデバイスのコンポーネントにユーザが任意に変更する必要があるパラメータを整理してProfileを用意することで、可変値として扱うことができます。基本的にはプロパティを列挙するだけなのでUnityARCameraSettingsProfileを参考にするといいと思います。

##### 4. MRTKに別パッケージでインポートできるようにする。
基本的にはMRTK内に組み込むのではなく別にインポートできるようにしておくといいと思います。参照関係として**別パッケージで用意した機能をMRTKのコア機能から参照しない**ように注意してください。そのような実装が必要な場合は実装ヶ所を見直すべきです。

## まとめ

MRTKはV2になってからクロスプラットフォームをかなり意識した構造になっています。
今回、ARFoundationを実験とはいえ導入が図られたことで、HoloLensのようなバイザー型、Oculusのような没入型、さらにAndroid端末やiPhone等のスマートフォンやタブレットとかなり多彩なデバイスにおいてXR技術を活用したコンテンツの展開が可能になりました。いくつものデバイスへ対応するのが大変！と思っていた方は是非一度MRTKなら解決できないか？と考えてみるのもいいかと思います。

[Azure Spatial Anchors]: https://docs.microsoft.com/ja-jp/azure/spatial-anchors/overview?WT.mc_id=MR-MVP-5003104 "Azure Spatial Anchors overview"
[Mixed Reality Toolkit V2.2.0]: https://github.com/microsoft/MixedRealityToolkit-Unity/releases/tag/v2.2.0 "Mixed Reality Toolkit V2.2.0"