WinMRで始めるMixed Reality Toolkit - Unity ～モーションコントローラ操作で発生するイベントについて

# Mixed Reality Tookit - Unityはモーションコントローラをサポートしています。
最新のMixed Reality Tookit - Unity(以降MRTK)はモーションコントローラをサポートしています。当然のごとくモーションコントローラの各種操作をイベントで制御できます。。。のはずなのですが実際には全て使えるわけではないです。
現状が開発途中のようにも見えているんですが、一方で先日公開された[
Mixed Reality 213: Motion controllers](https://github.com/Microsoft/MixedReality213)の中でもイベントの制御はInteractionManagerを直接使ったローレベルでの制御になっています。
やりたいこと次第でInteractionManager使うことは理解できるのですが、実際のところどこまでMRTKの提供機能で制御できているのか少し調べてみました。

## 環境
今回の環境は以下の通りです。

* Windows 10 Pro Fall Creators Update
* Unity 2017.2.1f1
* Mixed Reality Toolkit - Unity 2017.2.0.1

<font color="red">注！：MRTK-Unity 2017.2.0.1はUnity 2017.2.1f1以降でなければモーションコントローラの特定イベントでアプリがハングします。</font>

## MTRKのイベント制御

MRTKのイベント制御の基本的な使い方は以下の通りです。
MRTKではイベントの種類ごとにインターフェースが用意されているのでそれを実装することと、全体で発生させるイベントではSetGlobalListenerコンポーネントを追加します。

1. HierarchyにAssets/HoloToolkit/Input/Prefabs/ImputManager.prefabを追加する。
2. 任意のスクリプトを作成する。
3. 作成したスクリプトにイベント制御のためのインターフェース（後述）を実装する。
4. インターフェースのメソッドに必要な処理を実装する。
5. 3.で追加したスクリプトをイベント制御の対象オブジェクトに追加する。
6. イベントをいつでも発生させたい場合は5のオブジェクトにSetGlobalListenerコンポーネントを追加する。（フォーカス系のイベントについてはSetGlobalListenerしても意味がないので不要です。）

### 利用可能なインターフェース一覧

現在MRTKで提供されているインターフェースは以下の通りです。
使用可否についてはモーションコントローで利用できるものを〇としています。

|インターフェース|説明|使用可否|
|:-:|:--|:-:|
|IControllerInputHandler|タッチパッドや親指スティックの操作時のイベントを利用できます。|〇|
|IControllerTouchpadHandler|タッチパッドのボタンとしての操作時のイベントを利用できます。|〇|
|IDictationHandler|音声入力時のイベントを利用できます。|-|
|IFocusable|フォーカスの制御に反応するイベントに利用できます。|-|
|IGamePadHandler|ゲームパッドの検出に反応するイベントを利用できます。|-|
|IHoldHandler|ボタン押下状態に関するイベントを利用できます。|〇|
|IInputClickHandler|クリック操作（AirTapやボタンクリック）に対するイベントを利用できます。|〇|
|IInputHandler|ボタンの押下に対するイベントを利用できます。|〇|
|IManipulationHandler|ボタン押下中等の操作に対するイベントを利用できます。|〇|
|INavigationHandler|ナビゲーションジェスチャーに反応するイベントを利用できます。|〇|
|IPointerSpecificFocusable|ポインターごとのフォーカスの入力/終了に反応するイベントを利用できます。|〇|
|ISelectHandler|モーションコントローラのSelectボタンの押下に反応するイベントを利用できます。|〇|
|ISourcePositionHandler|入力ソース（モーションコントローラ等）の位置移動に反応するイベントを利用できます。|〇|
|ISourceRotationHandler|入力ソース（モーションコントローラ等）の回転に反応するイベントを利用できます。|〇|
|ISourceStateHandler|入力ソースの変更に反応するイベントを利用できます。|〇|
|ISpeechHandler|音声入力のキーワード検出に反応するイベントを利用できます。|-|
|IXboxControllerHandler|Xboxのスティック操作に反応するイベントを利用できます。|-|

次はそれぞれのインターフェースに関する内容を整理します。主にモーションコントローラに関連するインターフェースを中心に説明します。

#### モーションコントローラで使用可能なインターフェース
モーションコントローラを利用した場合に発生するイベントに対して対応できるインターフェースは以下のものになります。

##### IControllerInputHandlerインターフェース

タッチパッド、親指スティックを操作した際に発生する以下のメソッドを実装します。なお、スティックの反応が良いため、モーションコントローを動かしてるときにスティックいじっていなくてもイベントとして反応することがあります。
引数のInputPositionEventDataにはxy軸の操作値が-1～1の範囲でVector2クラスで取得可能です。

```csharp
//タッチパッドや、親指スティックの操作を行うと発生。
void OnInputPositionChanged(InputPositionEventData eventData);
```

##### IControllerTouchpadHandlerインターフェース

タッチパッドに触れた場合、また離した場合に発生する以下のメソッドを実装します。タッチパッドは特性上、押すということもできるのですがこのイベントではそこまで判別できないです。

```csharp
//タッチパッドに触れた場合に発生。
 void OnTouchpadTouched(InputEventData eventData);
 //タッチパッドから離れた場合に発生。
 void OnTouchpadReleased(InputEventData eventData);
```

##### IHoldHandlerインターフェース
SelectボタンによるHold状態を利用する場合に以下のメソッドを実装します。考え方として、ボタンの押下でしばらくするとHoldStarted、そのままボタンを離すとHoldCompletedとなります。HoldCanceledはボタン押下したまま、<font color="red">モーションコントローラを動かすと発生します。</font>この時、後述のManipulationUpdateが発生します。つまり「その場で動かさずにボタンを押す/離す」で１セットになっている形です。

```csharp
//ボタンを押した状態で発生
void OnHoldStarted(HoldEventData eventData);
//ボタンを離した状態で発生。ただし、Canceledイベントが発生と排他。
void OnHoldCompleted(HoldEventData eventData);
//Holdを開始した状態でモーションコントローラを動かすと発生。
void OnHoldCanceled(HoldEventData eventData);
```

##### IInputClickHandlerインターフェース
おなじみクリック時に発生する以下のメソッドを実装します。クリックなので、ボタンの押下、AirTap等で発生します。<font color="red">モーションコントローラではSelectボタンのクリックのみこのメソッドが呼び出されます。</font>

```csharp
void OnInputClicked(InputClickedEventData eventData);
```

##### IInputHandlerインターフェース
ボタンなどの「押す」と「離す」ときに発生する以下のメソッドを実装します。クリックの前後で発生するイベントですね。<font color="red">現状、このインターフェースのイベントについてはモーションコントローラのすべてのボタンが反応します。つまり、Select,Grasped,Menu,Thumbstick,Touchpad全てのボタンのDownとUPは検出されます。</font>

```csharp
//ボタンなどを押した場合に発生
void OnInputDown(InputEventData eventData);
//ボタンなどを離した場合に発生
void OnInputUp(InputEventData eventData);
```

##### IManipulationHandlerインターフェース
ボタンHold状態からモーションコントローラを動かす際に発生する以下のメソッドを実装します。INavigationHandlerインターフェースとイベントの発生タイミングは同じです。このイベントではボタンを押下した時点の位置を原点にコントローラの変化量を取得することができます。

```csharp
//操作の開始時に発生
void OnManipulationStarted(ManipulationEventData eventData);
//操作中に発生。
void OnManipulationUpdated(ManipulationEventData eventData);
//ボタンを離す等して操作状態を解除した場合に発生
void OnManipulationCompleted(ManipulationEventData eventData);
//色々試したんですがこれの発生条件が不明。。。
void OnManipulationCanceled(ManipulationEventData eventData);
```

##### INavigationHandlerインターフェース
ボタンHold状態からモーションコントローラを動かす際に発生する以下のメソッドを実装します。IManipulationHandlerインターフェースをイベントの発生タイミングは同じで、ボタンを押下した時点の位置を原点にコントローラを変化量を-1~1の範囲で正規化して取得することができます。

```csharp
//操作の開始時に発生
void OnNavigationStarted(NavigationEventData eventData);
//操作中に発生。
void OnNavigationUpdated(NavigationEventData eventData);
//ボタンを離す等して操作状態を解除した場合に発生
void OnNavigationCompleted(NavigationEventData eventData);
//色々試したんですがこれの発生条件が不明。。。
void OnNavigationCanceled(NavigationEventData eventData);
```

##### IPointerSpecificFocusableインターフェース
モーションコントローラのポインタが対象物に当たる/外れる場合に発生するイベントに対応する以下のメソッドを実装します。このインターフェースは対象物のオブジェクトに対して設定します。モーションコントローラのポインタがこのオブジェクトに当たるとイベントが発生します。

```csharp
//フォーカス取得した場合に発生
void OnFocusEnter(PointerSpecificEventData eventData);
//フォーカスを喪失した場合に発生
void OnFocusExit(PointerSpecificEventData eventData);
```

##### ISelectHandleriインターフェース
モーションコントローラのSelectボタンはアナログ値で押し具合を取得できます。この値が変化したときのイベントを以下のメソッドで実装します。引数のオブジェクトからその時の値を取得可能です。

```csharp
//Selectボタンの押し具合を変えている間発生。
void OnSelectPressedAmountChanged(SelectPressedEventData eventData);
```

##### ISourcePositionHandlerインターフェース
入力ソースの移動が発生した場合のイベントを以下のメソッドで実装します。モーションコントローラを振ってるときに発生するイベントです。入力ソースとなっているのでHoloLensでは手の移動がとれるはず。

```csharp
//入力ソースが移動した場合に発生
void OnPositionChanged(SourcePositionEventData eventData);
```

##### ISourceRotationHandlerインターフェース
入力ソースの回転が発生した場合のイベントを以下のメソッドで実装します。モーションコントローラをねじったり回したりしているときに発生するイベントです。

```csharp
//入力ソースが回転した場合に発生
void OnRotationChanged(SourceRotationEventData eventData);
```

##### ISourceStateHandlerインターフェース
入力ソースの状態が変化した場合のイベントを以下のメソッドで実装します。コントローラの電源が入りPCで認識されるとDetected、コントローラの電源落とすとLostイベントが発生します。おそらく電波状況が悪くて疎通ができなかったときも発生はすると思いますが、普通に手で操作してる状況では電源のON/OFFでしか発生しないのかもしれないです。

```csharp
//入力ソースが検出された場合に発生
void OnSourceDetected(SourceStateEventData eventData);
//入力ソースが喪失した際に発生
void OnSourceLost(SourceStateEventData eventData);
```

#### モーションコントローラでは使わないインターフェース
こちらはモーションコントローラでは使わない、もしくは使えないイベントを持っているインターフェースになります。

##### IDictationHandlerインターフェース
Unityでも機能としてある音声認識の機能と同じ考え方のイベントです。

```csharp
void OnDictationHypothesis(DictationEventData eventData);
void OnDictationResult(DictationEventData eventData);
void OnDictationComplete(DictationEventData eventData);
void OnDictationError(DictationEventData eventData);
```

##### IFocusableインターフェース
フォーカスイン/フォーカスアウトのイベントを利用する場合に実装します。このインターフェースはどちらかというと対象のオブジェクトに対して設定して使うタイプですね。

```csharp
void OnFocusEnter();
void OnFocusExit();
```

##### IGamePadHandlerインターフェース
ゲームパッドが認識されているかどうかがわかるイベントです。

```csharp
//ゲームパッドが検出された場合に発生。
void OnGamePadDetected(GamePadEventData eventData);
//ゲームパッドが未検出になった場合に発生。（概ね電源切ったとき）
void OnGamePadLost(GamePadEventData eventData);
```

##### ISpeechHandlerインターフェース
これもUnityにもある音声認識で使用するイベントに対応するメソッドを実装するために使用します。音声によるキーワード認識をした際に発生するイベントを利用することができます。

```csharp
void OnSpeechKeywordRecognized(SpeechEventData eventData);
```

##### IXboxControllerHandlerインターフェース
Xboxコントローラの軸の値が変化した際に対応する以下のメソッドを実装します。

```csharp
void OnXboxAxisUpdate(XboxControllerEventData eventData);
```

## 最後に
以前に比べると使えるイベントが増えているんですが、意外とHoloLensでも使えるものが多そうなイメージ。せっかくなのでHoloLensの場合はどうかもこの後調査したいと思います。