Mixed Reality Toolkit V2.3.0の追加機能 - Hand Coach、Non-Native Keyboard、Hand Physics Extension Service

# Mixed Reality Toolkit V2.3.0で追加された新機能
先日整理したリリースノートの解説に続き Mixed Reality Toolkit V2.3.0のいくつか面白い機能について紹介したいと思います。
それぞれ便利で色々なコンテンツ作成に役立つと思います。

## 検証に使った環境

* Windows 10 pro 1903
* Visual Studio 2019 Enterprise
* Unity 2019.1.10f1
* Mixed Reality Roolkit V2.3.0(Foundationのみ)

## Hand Coach

Hand Coachは以前にもその実装自体はありました。Mixed Reality関連のもう1つのOSSプロジェクト「Mixed Reality Design Labs」のサンプルの1つ「Lunar module」です。いわゆる月面着陸ゲームなのですが、このゲームのチュートリアルで手のモデルで実際の操作方法を提示するUX表現がありました。
今回、MRTK V2.3.0用に追加された形になります。

![Handcoach.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/b0d5a39d-d93b-a4f6-b52a-3ee7d32a98b7.gif)


例えば、目の前の仮想の物体に対して何していいかわからない場合や、体験会でのアテンドの時に
補助的に使うと非常にわかりやすくなります。個人的にはMRDLのころから結構気に入っていて時々入れてほしいとか
旧Verの時はMRDLから切り出して使っていたりしました。

### ジェスチャーとして組み込まれている種類

Hand Coachとして再現できるジェスチャーの種類はアイドル状態入れて8つになります。
この8つが左右分ある形です。コンポーネントは末尾が「_R」が右手系、「_L」が左手系で分けて用意されています。

それぞれの種類と動きのイメージは以下の通りです。動作イメージは右手系になっています。

｜ジェスチャー内容|動作イメージ|Animate State値<br/>()内は左手用|
|:--|:-:|:-:|
|アイドル状態のアクション|![Idle.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/292b7152-70b0-93f1-6b14-ae4c1db9715e.gif)|Idle_R(Idle_L)|
|近接のSelectアクション|![NearSelect.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/4d9c5be9-51ae-56fc-fffe-e45ef1233247.gif)|NearSelect_R(NearSelect_L)|
|AirTapによるSelectアクション|![AirTap.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/f7b1ab55-7a8b-10e9-fab5-8d0f64bcc6b9.gif)|AirTap_R(AirTap_L)|
|BoundingBoxのRotationアクション|![Rotate.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/c3ca6921-3a8b-fcae-9c89-4f37efc83418.gif)|Rotate_R(Rotate_L)|
|BoundingBoxのManipulationアクション|![Move.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/0cbb3202-72dc-ddbc-bf5e-ab3249d630b7.gif)|Move_R(Move_L)|
|BoundingBoxのサイズ変更のアクション|![Scale.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/2acec190-8020-44a8-ce13-d2784d04d030.gif)|Scale_R(Scale_L)|
|スクロールアクション|![Scroll.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/ff169d78-32dc-5ddc-e337-11306f81739d.gif)|Scroll_R(Scroll_L)|
|手のひらを反すアクション|![HandFlip.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/bf6ef891-15ae-800b-3001-5c6215967cce.gif)|HandFlip_R(HandFlip_L)|
|手のひらを上向きにするアクション|![PalmUp.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/37655f46-8c7b-e49d-368b-3571c2efad34.gif)|PalmUP_R(PalmUP_L)|

サンプルのシーンは「Assets/MixedRealityToolkit.Examples/HandCoach/HandCoachExample.unity」にあるので開くと右手系のジェスチャーが動くサンプルが用意されています。
例えば、以下の赤枠はNearSelect用のHandCoachの部分です。HandCoachExampleのシーンを開き[Hierarchy]タブ内の「HandCoachExample/SceneContent/Near Select Example/StaticHandCoachRoot_R」を参照してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/90cd8128-c3e7-aeaa-4ca6-44a8179690e7.png)

HandCoachはてのRigに関するオブジェクトとHandInteractionHintコンポーネントを組合わせて利用できます。

### 実装方法

HandCoachからのExammpleからコピーしても問題ないですが、ゼロから作る場合は以下の手順で可能です。

1. [Hierarchy]タブ内でからのオブジェクトを作成する  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/672d3da7-a3d1-5a20-25f0-86ddba9f9af1.png)
2. [Project]タブから[HandRig_R]を手順1.で作成したオブジェクトの子に追加する。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/6db97637-8490-75f7-955f-c57174963695.png)
3. [Project]タブから追加した[HandRig_R]を選択します。
[Inspector]  の[Animator]コンポーネントの[Controller]に[Hierarchy]から[HandCoach_R_Animator]をドラッグ&ドロップで追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/fe40c7b7-1852-61a3-44fd-8f24a6e2fd43.png)
4. 作成したオブジェクト選択し[Inspector]タブで[Hand Interaction Hint]を追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/24d45558-6e8c-b242-5dea-3299dae18121.png)
5. 追加した[Hand Interaction Hint]の[Animation State]に利用したいジェスチャーの値（例；Rotate_R)を入力します。

左手で作成したい場合は「_R」を「_L」に変更します。

### Hand Interaction Hint

Hand Coachのメインになるコンポーネントで子の[HandRig]の挙動を制御する部品です。
パラメータは以下のものを指定することが可能です。

|パラメータ|説明|設定例|
|:-:|:--|:-:|
|Hide if Hand Tracked|Hand Tracking時非表示にする|false|
|Min Delay|表示の開始までのラグ(最小：秒)|5|
|Max Delay|表示の開始までのラグ(最大：秒)|10|
|Use Max Delay|Max Delayを使うかどうか|true
|Repeats|繰り返し回数|2
|Auto Activate|自動で開始するかどうか|true|
|Animation State|どのジェスチャーを利用するか|Rotate_R|
|Repeat Delay|繰返し間の遅延時間|1|

例えば上記設定であれば以下の動きになります。

* Rorateのアクションをヒントとして表示
* アプリケーション開始と同時に自動的に実行
* 5秒後にヒントの表示
* ヒントは2回連続1秒間感覚で表示

Max Delayは保険で設定するパラメータのようです。この設定はほかのHand Tracking検出時に表示するなど、
開始のタイミングがずれることで5秒以上遅延したときにアニメーションの動作などに違和感が出ないよう、
調整する意味でつかわれます。上記の設定の場合5～10秒の間で遅延が発生するイメージになります。

## Non-Native Keyboard

ネイティブの機能を使わない仮想キーボードです。キーボードの配列などはHoloLens 1の仮想キーボードと同じ配列になっています。

![NonNativeKeyboard.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/bd2cb73c-6664-9912-d931-069e74b15fe9.gif)

3Dオブジェクトとして開発されているためクロスプラットフォームで利用できますが、現状日本語入力などのネイティブのキーボードの機能は使えないです。
実装例は「Assets/MixedRealityToolkit.Examples/NonNativeKeyboard/NonNativeKeyboardExample.unity」にあるので開いたテキストフィールドを確認します。

### 使い方

キーボード自体はPrefabで構成されているので[Hierarchy]の任意の場所に[Project]タブから[NonNativeKeyboard]を追加したのち、キーボードで入力するテキストフィールドを紐づけるための実装を行う形になります。

```csharp:キーボードの起動とテキスト反映（テキストフィールドのコンポーネントとして実装）
private NonNativeKeyboard keyboard = null;

public void OnSomethingEvent()
{
    keyboard.PresentKeyboard();

    keyboard.OnClosed += DisableKeyboard;
    keyboard.OnTextSubmitted += DisableKeyboard;
    keyboard.OnTextUpdated += UpdateText;
}

private void UpdateText(string text)
{
    GetComponent<TMP_InputField>().text = text;
}

private void DisableKeyboard(object sender, EventArgs e)
{
    keyboard.OnTextUpdated -= UpdateText;
    keyboard.OnClosed -= DisableKeyboard;
    keyboard.OnTextSubmitted -= DisableKeyboard;

    keyboard.Close();
}
```

## Hand Physics Extension Service

HoloLens 2のHand Trackingで検出している手に当たり判定を入れることができる拡張サービスです。
仕掛けは単純でHand Trackingで検出したHand JointのいくつかにRigidBodyを仕込むことでColliderを検出して物体を制御する仕組みです。

![HandPhysicsService.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/829fedc2-999e-44f5-f27b-4f5d206b6033.gif)


Colliderが設定されるJointは現在のところ指先5本＋手のひら部分になります。
が、現状手のひら部分使うとエラーで落ちるのでとりあえずは指先だけで楽しみましょう。
Hand Physics Serviceが有効になると自動的に[HandPhysicsService]オブジェクトが生成されます。このオブジェクトの中には左右の手のJoint（関節）にリンクするオブジェクトが入っており、Colliderの制御を行います。
おそらくPalmについてもこの中に入ってくるはず（いれたらエラー無くなるのかな。。。）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/fa14f986-1511-e47a-caf5-f20031d27a7f.png)

Unity Editor上では上記のようになります。実行すると指先のJointにRigidBodyが入って当たり判定が入るようになっています。

### 使い方

使い方はHand Physics ServiceをExtensionsに設定します。
この機能は拡張機能のため、Extensionsパッケージのインポートをしてください。

1. [Hierarchy]タブ内の[MixedRealityToolkit]を選択し[Inspector]タブを開きます。
2. Profileの設定が必要になります。まず、[ルートのProfile]をCloneします。
3. [Extensions]メニュを選択し、[ExtensionsのProfile]をConeします。
4. [Register a new Service Provider]をクリックして新しいProvider設定を追加します。
5. ComponentTypeに[HandPhysicsService] を選択します。
6. 現状は初期設定のままで問題ないです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152548/4633155d-63fe-c77a-a264-529bd6cbc4b5.png)


ExtensionsのProfileについては以下の通りです。

|パラメータ名|説明|設定値|
|:-:|:--|:-:|
|Component Name|コンポーネントの名前（変更不可）|Hand Physics Service|
|Component Type|コンポーネント型です。今回の場合はHandPhysicsServiceを設定します。|Hand Physics Service|
|Priority|優先度。ServiceProviderの競合などで読込みタイミングの調整する必要がある場合この値で調整します。|10|
|Runtime Platform|有効化するPlatform。このHandPhysicsServiceの場合はUWPのみなので、Windows Universalのみ設定|Windows Universal|

HandPhysicsServiceのProfileについては以下の通りです。

|パラメータ名|説明|設定値|
|:-:|:--|:-:|
|Hand Physics Layer|この機能が作用するレイヤーを指定します。|0|
|Finger Tip Kinemtic Body Prefab|指先の当たり判定をするためのPrefab|FingerTipKinematicBody|
|User Palm Kinematic Body|手のひらも当たり判定対象にするかどうか|false|
|User Palm Kinematic Body Prefab|手のひらの当たり判定をするためのPrefab|None|

## まとめ
ということろでMixed Reality Toolkit V2.3.0で追加された試験的な面白い機能を紹介しました。これからも色々と便利で面白い機能が追加されると思うので、ぜひ色々試してみてください。