Microsoft Mesh - Custom Immersive Experience : Mesh Toolkitの導入と空間作成

# Microsft Meshのサービスの1つとして提供される Custom Immersive Experience
今年リリースされたMicrosft Mesh。皆さん使ってますか？Meshの一部の機能はMicrosft Teams内に統合されているので使ったことがある人もいらっしゃるかもしれないです。Teamsから利用できる機能としては「Avatar for Teams」、「Immersive Spaces」があります。
avatar for MeshはWebカメラが自撮り参加するのではなくアバターを利用してTeamsに参加できる機能です。そしてImmerSive SpacesはTeams上から没入型の3D空間内で交流が可能になる機能です。Teamsで利用できるImmersive Spacesは用途に応じた４つの空間が提供されています。利用者はこの空間に入って交流できるのですが、あくまでTeamsをクライアントアプリとして利用している環境にあわせるため、できることがあまり多くありません。特に独自の空間を使った交流を行いたい場合は、この機能では役不足になります。
そこでMicrosoft Meshではカスタム環境を作るために機能も提供されています。
それがCustom Immersive Experienceです。Custom Immersive Experienceは大きく２つの機能が提供されています。
１つは、既存の環境内に画像や画面共有オブジェクトを配置してカスタムが行える機能です。
もう１つは、Unityによる実装を通してカスタム環境を構築する手段になります。

本記事では、Unityを使ってMicrosoft Meshの環境を構築し、デプロイする手順を紹介します。

## 今回作成する空間について
今回はゼロから新しい環境を構築することを想定し、以下の手順で実際にMeshで体験できる環境を構築します。
今回は最小限のコンテンツとして、シンプルに平面と落下防止用の段差、アバターの制約を確認するためのオブジェクトを配置したものを作成します。

作成後の環境は以下のGithubで公開しています。

https://github.com/TakahiroMiyaura/MeshToolkit-Basic/tree/main/Unity/Mesh-CIE

## 開発環境
Microsft Meshのカスタム環境を構築する際に必要な環境です。今回(2024/11/10時点)は以下の環境で開発しています。経験上Microsoft Meshについては指定バージョン以外はうまくいかないことがあるので注意してください。

* Windows OS ※Mesh ToolkitがWindows OS依存のため
* Unity 2022.3.34f1
    * Andoroid
        * Open JDK
        * Android SDK & NDK Tools
    * Windows Build Support(IL2CPP)
* Mesh Toolkit V5.2414.128

## Mesh Toolkitの導入

Microsoft Meshでカスタム環境を構築するためには専用のライブラリ[Mesh Toolkit]を導入します。導入はUnity Package Manager経由で行います。
プロジェクトにMesh Toolkit用のレジストリを登録し、現在リリースされているバージョンをインポートします。

:::note info
Mesh Toolkitにはライブラリの機能を利用したサンプルが用意されています。まずはサンプルを試してみたい場合、Githubダウンロードします。構成や実装が参考になります。
https://github.com/microsoft/Mesh-Toolkit-Unity
:::

### 空のUnityプロジェクトを作成する。
最初にUnityの空プロジェクトを作成します。プロジェクトのタイプはURP(3D)です。

![2024-11-10-11-07-45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/221f0409-7f91-31c5-4c97-a0456888c4e6.png)

### Mesh Toolkitを設定する。
プロジェクトが開いたらMesh Toolkitのパッケージを利用できるようにレジストリの登録を行います。メニューから[Edit]-[Project Settings]-[Package Manager]を選択し、以下を参考に情報を入力してください。

|パラメータ|設定値|
|:--:|:---|
|Name|Mesh Toolkit|
|URL|https://registry.npmjs.org|
|Scope|com.microsoft|

![2024-11-10-11-01-14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/fa1b79a3-1155-fa36-48f8-f654d52adc55.png)

設定を保存後メニューから[Window]-[Package Manager]を選択しPackage Managerを開きます。
ウィンドウ上部の[Packages:Unity Registry]を[Packages:My Registry]を選択するとMesh Toolkit（と関連モジュール）のみが表示されるので、MeshToolkitをインポートします。

![2024-11-10-11-17-09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/dda5d4ec-b399-14af-10fe-5aeb6147161a.png)

## カスタム環境を作る

次に実際に最小限とアバターの制約がわかるカスタム環境を構築します。完成図は以下のような形になります。

![2024-11-12-15-34-41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/6e43d5a5-2399-9e08-1945-56b5ebffe1b3.png)

### 空シーンを作成する

最初に空のシーンを作ります。Unityプロジェクトを開くとデフォルトのシーンが開いていると思うのでそれを使いましょう。

![2024-11-12-09-18-55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/284c8b9b-7b0d-ac92-e51b-acaa4543f979.png)

Mesh用で使う場合は以下の不要なオブジェクトをHierarchyから削除します。

* Global Volume

次に実際の空間を構築していきます。

### 空間の作成

実際に空間を作成する部分については一般的なUnityでの開発と違いはありません。今回はアバターの制約等も検証するために以下のオブジェクトを用意したいと思います。

* 床
* 落下防止用の段差
* アバターが乗り越えられない障害物
* アバターが乗り越えられる障害物
* アバターが通過できる隙間(幅)
* アバターが通過できない隙間(幅)
* アバターが通過できる隙間(高さ)
* アバターが通過できない隙間(高さ)
* アバターが登坂できる坂
* アバターが登坂できない坂

完成後の空間は以下のようになります。


最初にシーンのルートに空のGameObjectを追加し[Environment]とします。これは空間のGameObjectをまとめるためだけなので設置は任意です。なくても問題なく動作します。次に上記で作成予定のオブジェクトを配置していきます。設定は以下の表のとおりです。Transformの情報以外は自由に設定しても問題ありません。床や壁などはColliderを設定しておくことでアバターが突き抜けずに動き回ることが可能です。

![2024-11-12-09-59-51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/6fb04c2b-e9f9-6588-4afe-faeac696a139.png)

|名称|形状|名前-Transformの設定|
|:-:|:-:|:--|
| 床|Plane|Floor - Position(0,0,0),Rotation(0,0,0),Scale(1,1,1)|
| 落下防止用の段差|Cube| <UL><LI>Fence1 - Position(0,0.2,5),Rotation(0,0,0),Scale(10,0.4,0.1)</LI><LI>Fence2 - Position(0,0.2,-5),Rotation(0,0,0),Scale(10,0.4,0.1)</LI><LI>Fence3 - Position(5,0.2,0),Rotation(0,0,0),Scale(0.1,0.4,10)</LI><LI>Fence4 - Position(-5,0.2,0),Rotation(0,0,0),Scale(0.1,0.4,10)</LI><UL>|
| アバターが乗り越えられる障害物|Cube|Step1 - Position(3,0.15,3),Rotation(0,0,0),Scale(2,0.3,1)|
| アバターが乗り越えられない障害物|Cube|Step2 - Position(1,0.2,3),Rotation(0,0,0),Scale(2,0.4,1)|
| アバターが通過できる隙間(幅)|Cube|<UL><LI>Wall1 - Position(0,0.9,3),Rotation(0,0,0),Scale(0.1,1,1)</LI><LI>Wall2 - Position(-0.71,0.9,3),Rotation(0,0,0),Scale(0.1,1,1)</LI><UL>|
| アバターが通過できない隙間(幅)|Cube|Wall3 - Position(-1.32,0.9,3),Rotation(0,0,0),Scale(0.1,1,1)|
| アバターが通過できる隙間(高さ)|Cube|Ceiling1 - Position(-2.5,2.1,3),Rotation(0,0,0),Scale(1,0.1,1)|
| アバターが通過できない隙間(高さ)|Cube|Ceiling2 - Position(-3.5,2,3),Rotation(0,0,0),Scale(1,0.1,1)|
| アバターが登坂できる坂|Cube|Slope1 - Position(-3,0.9,-3),Rotation(45,0,0),Scale(1,0.01,3)|
| アバターが登坂できない坂|Cube|slope2 - Position(-1.5,0.9,-3),Rotation(45.1,0,0),Scale(1,0.01,3)|

### テレポート領域の設定

次にテレポート領域の設定を行います。先ほど作った空間は、空間内で歩き回れるようにはなるのですが、ユーザがテレポート可能範囲を指定しないためテレポートはできません。テレポートを有効にするためには床面であることを定義する必要があります。Micorsoft MeshではLayer設定でこの定義を行います。このほかにも用途に応じて様々なLayer設定が存在します。
次のようにFloorオブジェクトを選択し[Inspector]パネル内のLayer設定を[Default]から[GroundCollision]に変更します。

![2024-11-12-10-08-40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/6fc63f82-11ea-150a-2cc8-941128f2beb2.png)

以上で、最低限Mesh内で使える環境が構築できました。次はアバターのスポーン地点の設定を行います。

:::note info
テレポートができない場合、Meshではテレポートマーカが赤で表示されます。
:::

### スポーン地点の設定

次にユーザがログインしてきた際のスポーン地点を設定します。Mesh Toolkitでは[Travel Point Group][Travel Point]という部品が提供されておりこれを空間内に配置することで出現位置を制御することができます。詳細は後半に説明します。ここでは原点位置を中心に半径2mの範囲でスポーンするように設定します。
Travel Pointの追加はHierarchyの右クリックメニューから[Mesh Toolkit]-[Travel Point Group]を選択すると追加することができます。

![2024-11-12-14-33-24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/a9543886-31a1-2222-0a22-f5fa4a5afec2.png)

先ほど追加したTravelPointGroupを選択しInspectorパネルからTravelPointGroupコンポーネントの[Default Spawn Group]にチェックを入れます。さらに、子要素の[Travel Point]を選択します。InspectorパネルからTravel Pointの[Radius]プロパティを2に変更します。
そうすると、以下のようにシーン内には赤色でスポーン地点と範囲が表示されます。

![2024-11-12-14-50-43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/be575ab7-72f1-3a7f-7d45-b3136af1b11a.png)

また、追加した各オブジェクトのPositionが全て(0,0,0)になっていることを確認してください。

### MeshThumbnailCameraの設定

次にMeshThumbnailCameraを追加します。カスタム環境は作成後最終的にはクラウド上にあるMeshポータルに登録します。この際にカスタム環境のサムネイル画像が必須となります。ThumnailCameraはMeshポータルへのアップロード時にMeshThumbnailCameraの画角で撮影し、サムネイル画像として利用します。この方法であれば、個別に画像を用意することなくシーン内の環境を表現することが出来るので便利です。

::: note info
MeshThumbnailCameraを配置せずに、画像を別途用意して取り込むことも可能です。この場合サムネイル画像は以下の要件を満たす必要があります。
* 1024 x 512
* 512 x 256
* 256 x 128
:::

MeshThumbnailCameraの追加はHierarchyの右クリックメニューから[Mesh Toolkit]-[Thumnal Camera]を選択すると追加することができます。

![2024-11-12-15-04-28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/f9a4ed7e-c082-371c-9816-9bd337ad2701.png)

追加したMeshThumbnailCameraに以下のTransformの設定を行います。設定自体は任意です。好きな画角で設定してください。
* Position(-3,3,1)
* Rotation(40,25,0)

### Play Modeでの動作確認

Mesh用の環境はUnityのPlay Modeで検証が可能です。アップロードする前にある程度の動作確認を行うことができます。
Play Modeで検証する場合は、HierarchyにMeshEmulatorSetup[NotUpload]というPrefabを追加します。[Project]パネルで[MeshEmulatorSetup]をパッケージ内から検索します。見つかったPrefabをHierarchyにコピーします。その後Play Modeを開始します。

![2024-11-12-15-45-35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/d513e2ce-0c31-ea48-9a1b-1da2a6be4d03.png)

:::note info
Prefabを追加せずにPlay Modeを実行すると以下のようなダイアログが表示されます。ここで[Add MeshEmulatorSetup prefab]を選択すれば同じPrefabが追加されます。
![2024-11-12-15-48-42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/e8606096-a68f-e2e2-84d2-f724e74acb3c.png)
:::


操作方法はゲームのFPSと同じでWSADで前後左右、QEで左右の回転、マウス操作で頭の向きの調整です。次回以降解説する予定ですが操作可能なオブジェクトを操作する場合は対象にマウスカーソルを当てて左クリックでアクションを行うことができます。

:::note info
F1を押すことでチートシートが表示されます。
:::

![2024-11-12-15-54-04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/c8159bc1-e45f-33d5-7296-7a41aca07517.png)

また、Game画面内の右上にある[Enter Split Screen]を押すと、画面が分割されユーザが追加されます。Meshのカスタム環境では複数のユーザが入る場合がほとんどなので、複数ユーザで体験が可能な検証したい場合はこの機能を利用する事が可能です。

![2024-11-12-15-57-30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/7257681b-03ce-5915-f906-1e4eac444046.png)


### Mesh Environmentによるカスタム環境の登録

Unity上で動作確認を行って問題なければ、実際にMesh App等で体験できるように環境をMeshポータルにアップロードします。
手順としては以下の通りです。

1. Meshポータルでコレクションを作成する
2. Mesh EnvironmentでMeshポータルにアップロードする

#### 1. Meshポータルでコレクションを作成する

最初にカスタム環境を管理するためのコレクションを作成します。これはMeshポータル上でカスタム環境をグルーピングする目的で作成します。
最初にMeshポータルにアクセスします（[https://mesh.cloud.microsoft/](https://mesh.cloud.microsoft/)）。メニューからコレクションを選択します。コレクションを開いたら[コレクションの作成]を押して新しくコレクションを作成します。

![2024-11-12-17-49-22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/4614c212-5a7e-ffce-93d3-24c856d46ca7.png)

必要事項を入力して作成を行います。現在のMeshはドメイン内での利用のみのためプライバシーがPrivateのみとなっています。
![2024-11-12-17-53-03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/ae92d8cf-9079-842e-de42-002de98dc1e9.png)

Meshポータルでの設定は以上です。この後UnityからMesh Environmentを操作して先ほど作成した環境をアップロードします。

#### 2. Mesh EnvironmentでMeshポータルにアップロードする

次にUnityからMeshポータルに環境をアップロードします。メニューから[Mesh Toolkit]-[Ecvironments]を選択すると以下のようにMesh Environmentウィンドウが表示されます。最初はアップロードのために新しい環境が必要となるため[Create Environment]タブを選択します。

![2024-11-12-17-58-02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/86b5ae5b-e7fd-b303-24e9-5c9ef02d4c11.png)

以下の項目を設定し[Create Environment]をクリックします。

* Name:アップロードするカスタム環境名
* Description:アップロードするカスタム環境の説明
* Environment Collections:先ほどMeshポータルで作ったコレクション
* Capacity:同時参加ユーザ数（デフォルト16）

:::note info
Setup Cloud Scripting Configurationについては、環境内で実装をクラウドを活用した独自処理を作る場合に設定します。
:::

作成が正常に完了すると以下のように成功ウィンドウが表示されます。
![2024-11-12-18-05-05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/e777b0b9-e38e-fb56-1183-296bade059e0.png)

次にアップロードを行います。[Update Environment]タブを選択するとMeshポータルの環境とアップロードするUnityシーンを紐づけることができます。

1. 最初にコンボボックスの中から先ほど作成したEnvironmentを選択します（環境名の前に(New)と表記がされているものを選択します）。
2. テキストボックスの領域をクリックするとUnityプロジェクト内のシーンが表示されます。先ほど作ったUnityシーンを設定してください。
3. Thumbnailの設定を行います。ThumbnailCameraを設定した場合は[Generate from Thumbnail Camera]を選択します。
4. Platformの設定を行います。ウィンドウ下のPCとAndroidのボタンを押すことで対象/非対称を切替えることが可能です。

![2024-11-12-18-07-34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/cbad77f0-9829-e837-8d27-12f5990a8b6a.png)

一通り入力が完了したら、[Build&Publish]ボタンを押してアップロードを行います。
アップロードが完了すると以下のようにpublishが **Succeed** になります。今回はContent PerformanceにWarningが出ていますが、今回は気にしなくても問題ありません（後日パフォーマンスについても投稿予定）。

![2024-11-12-18-20-26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/eff2b1b4-5b91-e455-3d5e-102b8c81db23.png)

アップロードが完了した後再度Meshポータルのコレクションを開くとアップロードされていることが確認できます。後はこの環境を使ってイベントを組むことで利用することができます。

## 実際に動かしてみる
### Meshポータルでのイベント設定

最後に実際に体験して見たいと思います。Meshポータルにアクセスし、イベントを選択すると、現在設定しているイベント一覧が表示されます。まず[イベントの作成]を押してイベントを新規に起こします。

![2024-11-12-19-11-27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/e4e459e8-3018-6b88-1426-27054cd0d508.png)

イベントではいくつかの設定項目があります。必要事項を埋めてイベントを作成します。項目については以下を参考にしてください。先ほど作った環境を選択するには項目の後半にある[最初から始める]を選択します。この項目を選択するとMicrosoftが提供するプリセットの環境と先ほど追加した環境を利用できます。今回は先ほど作ったコレクションを選択し、追加された環境を設定します。

![2024-11-12-19-16-20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/a49998e2-6c8c-552f-c011-3ef7b4f98516.png)

【設定項目（上から順）】

* イベント名
* イベント開催時間(開始・終了)
* イベントの詳細
* 会議室のタイプ
    * 1つの会議室:最大16名の単一空間で行う(24h制限)
    * 最大330人と16名のホスト。空間は16人毎にグループ化される。ホストは１つの会議室に集約され全体にブロードキャストされる(3h制限)
* 共同開催者（任意：イベント作成者が主催者です）
* 出席者(あらかじめ参加者が決まっている場合)
* テンプレートから選択 or 最初から選択
    * テンプレートはすでにある環境を簡易的にカスタマイズしたものを利用

以上でイベントの作成が完了しました。

### Mesh App/Meta Questでの動作確認

最後に実査にMesh Appでイベントに参加します。現在はPC版とMeta Quest版が提供されています。
PC版はMesh Appをダウンロードして実行します。主催者もしくは参加者として登録されているアカウントでログインするとイベント一覧に表示されます。

![2024-11-12-19-37-58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/603900cf-475e-9503-a703-403ccb1876fe.png)

該当のイベントを選択すると先ほど作成した環境に入ることが可能です。

![2024-11-12-19-40-02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/5cd6951e-691e-0e27-e471-ac4743aa7ce3.png)

## 解説

最後に今回使ったMeshに関する話を紹介します。

### Meshで利用可能なレイヤーについて

Mesh Toolkitを導入するとオブジェクトにいくつかのレイヤーを割り当てることが可能です。これらは用途に応じて便利に利用できるものです。
例えば、今回は床面にGround Cpllisionを設定しています。このレイヤーが設定された面ではテレポートマーカーが有効になり、テレポート移動が可能になります。

![2024-11-12-19-49-37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/355d896a-05a6-4b31-955d-b9f0ef11563f.png)

レイヤーについては以下の公式サイトが参考になります。

https://learn.microsoft.com/ja-jp/mesh/develop/build-your-basic-environment/configuring-for-avatar-movement-and-teleportation#layers-in-detail?wt.mc_id=WDIT-MVP-5003104

### アバターの制約

Microsoft Meshの環境内で行動するアバターには移動における制約があります。制約事項としては以下の通りです。

* 最大歩行可能勾配: <45度
* 最大ステップ高さ: 0.3
* アバター カプセルの半径: 0.3
* アバター カプセルの高さ: 2
* アバターの高さ: 1.8

今回のサンプルではこの制約に関する確認を行えるようにオブジェクトを一部配置しています。空間を作る時にはこれらの制約に対して余裕をもって空間を設計する必要があります。空間内で実際に試してみてください。

### TravelPoint

Meshでは空間内の特定の位置に移動させることができます。Mesh Toolkitではそれらの移動先をTravelPointとして定義します。これを利用すると、ボタンを押したときに場所を移動するといった事が可能になります。また、Meshの制約として空間内に最低限スポーン地点を１つ設置する必要があります。これは最初にスポーンする地点が必要という意味です。
移動先についてはグループ化することも可能で、Travel Point Groupの中には複数のTravel Pointを含めることができます。Groupを移動先に指定し、その配下のTravel Pointのいずれかに移動させることができます。また、Travel Pointに移動することも可能です。

![2024-11-12-14-36-02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/61a411a7-1c4f-bc23-5cfb-c9cd45ad8610.png)


空間内のどの位置にスポーンさせるかはTravel PointのPositionとRadiusで決まります。今回は原点位置として(0,0,0)としているため、この地点を中心にTravelPointコンポーネントの[Radius]を指定する半径の範囲内でスポーンされます。また、移動後に注視してほしいオブジェクト（説明文等）があれば、[Look At Transform]に対象コンポーネントを設定します。

![2024-11-12-14-39-31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/fa6a59e7-f95c-64a2-727c-37f498817a98.png)

また、対象のTravelPoint範囲内に参加者がいる場合に移動を禁止することができます。これをおこなうためには、SingleTravelプロパティにチェックを入れます。Travel Groupとして複数のTravel Pointを設定している場合は、すべてのTravel Pointのいずれかに空きがないと移動できません。

## 補足：Meshのサンプル環境について

Mesh Toolkitの機能を含めてどういったことができるか知りたい場合はMesh Toolkitの[Github](https://github.com/microsoft/Mesh-Toolkit-Unity)にサンプルが用意されています。それぞれの簡単な解説は以下の公式ドキュメントで紹介されています。

https://learn.microsoft.com/ja-jp/mesh/develop/getting-started/samples/pavilion?wt.mc_id=WDIT-MVP-5003104

https://learn.microsoft.com/ja-jp/mesh/develop/getting-started/samples/physics-effects-gallery?wt.mc_id=WDIT-MVP-5003104

https://learn.microsoft.com/ja-jp/mesh/develop/getting-started/samples/science-building?wt.mc_id=WDIT-MVP-5003104

https://learn.microsoft.com/ja-jp/mesh/develop/getting-started/samples/dart-room?wt.mc_id=WDIT-MVP-5003104

https://learn.microsoft.com/ja-jp/mesh/develop/getting-started/mesh-101-tutorial/mesh-101-01-overview-and-setup?wt.mc_id=WDIT-MVP-5003104

https://learn.microsoft.com/ja-jp/mesh/develop/getting-started/mesh-201-tutorial/mesh-201-01-overview-set-up-and-get-started?wt.mc_id=WDIT-MVP-5003104