WinMRでモーションコントローラをもう少し使いやすくしてみる - Mixed Reality Toolkit Unityの拡張

# ボタンごとにイベントの制御を書きたい
以前[説明した方法](https://qiita.com/miyaura/items/12d00950fb42eb36ada5)でモーションコントローラを使ったアプリケーションの実装が可能となります。InteractionManagerクラスでリアルタイムでの変更値、ボタンの状況を把握できます。また、MTRKのIInputClickHandlerインターフェースをモーションコントローラ全てのボタン押下イベントは取得可能です。
一方で、レーザポインタ（タッチパッドなどに触れることで表示されるガイドポインタ）の表示や、モーションコントローラのリアルタイム値を取得してボタン毎の制御を行うようなものはよく使うものとして同じような実装を繰返しそうです。
また、現状のMixed reality toolkit - Unityのモーションコントローラ周りのイベント制御については中途半端なものが多くすべての機能を利用するには物足りなさがあります（この件については前回の記事を参照）。
そこで、今回練習の意味で「MotionContollerManager」クラスを実装しました。これは各ボタン毎にイベントが発火するように制御したものです。また、ガイドとなるレーザポインタも合わせて出力できるように作成しました。

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

* 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>

## 使い方とサンプル
以下のGitに公開しています。一部MRTKの機能を利用しています
https://github.com/TakahiroMiyaura/MotionControllerManager

サンプルのアプリは以下の２つのデモになります。

1. 各種イベントに応じてCubeの色が変化する
2. 左手のSelectを押下している間cubeが生成。右手のSelectボタンでポインターを表示＆cubeに当てると動かすことができる。

使い方はAsset/MotionController/prefabs/MotionController.prefabをHierarchyに追加することで有効になります。その後MotionControllerManagerの各種イベントに実行するメソッドを割り当てます。

### MotionControllerのプロパティ

MotionControllerには以下のプロパティを用意してみました。モーションコントローラは左右あるので右手用と左手用でそれぞれプロパティを設定可能です。
![image](https://qiita-image-store.s3.amazonaws.com/0/152548/db384547-3abe-7087-f2a5-a9305b70761b.png)

|プロパティ|説明|初期値|
|:-:|:--|:--|
|Override Renderer|レーザポインターのデザインを変更する場合に設定します。未設定の場合は白の細いラインが表示されます。|None|
|Select Down|チェックがある場合、Selectボタン押下でポインターが表示されます|true|
|Grasp ed Down|チェックがある場合、Graspedボタン押下でポインターが表示されます|false|
|Menu Down|チェックがある場合、Menuボタン押下でポインターが表示されます|false|
|Thumstick Down|チェックがある場合、Thumbstickボタン押下でポインターが表示されます|false|
|Touchpad Touched|チェックがある場合、Touchpadのタッチ操作でポインターが表示されます|true|
|Touchpad Down|チェックがある場合、Touchpadの押下でポインターが表示されます|false|

### 発生できるイベントの種類
MotionControllerイベントで発生可能なイベントについては以下の通りです。

|イベント名|説明|
|:-:|:--|
|Grasped|Graspedボタンを押下しコントローラを握った時に発生します。|
|Released|Graspedボタンを話した時に発生します。|
|MenuDown|Menuボタンを押下した時に発生します。|
|MenuUp|Menuボタンを離した時に発生します。|
|MotionControllerManipulate|モーションコントローラが操作中に発生します。（このイベントは常に発生します。）|
|SelectDown|Selectボタンを押下したときに発生します。|
|SelectHold|Selectボタンの押しの強さを変えている間発生します。引数のSelectManipilateEventArgsにはアナログ値を取得できます。|
|SelectUp|Selectボタンを離した時に発生します。|
|ThumbstickDown|親指スティックを押した時に発生します。|
|ThumbstickHold|親指スティックを動かしている時発生します。|
|ThumbstickUp|親指スティックを離した時に発生します。|
|TouchpadTouch|タッチパッドに触れた時に発生します。|
|TouchpadDown|タッチパッドを押下した時に発生します。|
|TouchpadHold|タッチパッドを触れているor押下中に発しします。|
|TouchpadUp|タッチパッドを離した時に発生します。|

### 利用方法

基本的にはsingletonなクラスで定義しており、使いたいイベントに対してAddHandlerするだけで処理できるようになります。

#### 1.新規プロジェクトの作成とMixed Reality Toolkit -Unityの導入
まずはお約束のUnityで新プロジェクトとMixed Reality Toolkit - Unityの導入です。

#### 2.MotionControllerManagerの導入
[Github](https://github.com/TakahiroMiyaura/MotionControllerManager)から導入します。External/unitypackage/motioncontroller.unitypackageをインポートしてください。

#### 3.Prefabの導入
Asset/MotionController/Prefabs/MotionController.prefabをHierarchyに追加します。
![image](https://qiita-image-store.s3.amazonaws.com/0/152548/de2e3087-af24-5b0b-de7d-2d9520c31425.png)

#### 4.イベントの定義
任意のscriptを作成しstartメソッドの中でMotionControllerManagerの各イベントにAddHandlerする実装を行います。

```csharp
// Copyright(c) 2017 Takahiro Miyaura
// Released under the MIT license
// http://opensource.org/licenses/mit-license.php

void Start () 
{
    //タッチパッド系のイベントを設定する
    MotionControllerManager.Instance.TouchpadTouch += TouchpadTouch;
    MotionControllerManager.Instance.TouchpadHold+= TouchpadManupilate;
    MotionControllerManager.Instance.TouchpadUp+= TouchpadRelease;
    MotionControllerManager.Instance.TouchpadDown+= TouchpadPress;
}

private void TouchpadUp(object sender, MotionControllerManager.TouchpadManipilateEventArgs touchpadManipilateEventArgs)
{
    //Touchpadボタンを離した時の処理
}

private void TouchpadTouch(object sender, MotionControllerManager.TouchpadManipilateEventArgs touchpadManipilateEventArgs)
{
    //Touchpadボタンに触れたときの処理
}

private void TouchpadHold(object sender, MotionControllerManager.TouchpadManipilateEventArgs touchpadManipilateEventArgs)
{
    //Touchpadボタンを操作中の処理
}

private void TouchpadDown(object sender, MotionControllerManager.TouchpadManipilateEventArgs touchpadManipilateEventArgs)
{
    //Touchpadボタンを押した時の処理
}
```

TouchpadManipilateEventArgsクラスはTouchpadの以下の情報をプロパティで持ちます。

|プロパティ名|説明|
|:-:|:--|
|SourceState|モーションコントローラのInteractionSourceStateオブジェクト。イベントで簡易的に取得できない情報を取得する場合に使用します。|
|Handedness|イベントが発生したモーションコントローラの位置（右手/左手)|
|TouchpadPressed|touchpadが押下されているかをtrue/falseで取得します。|
|TouchpadTouch|touchpadに触れているかをtrue/falseで取得します。|
|TouchpadPosition|touchpadの位置（xy軸で-1～1）|

SourceStateおよびHandednessはMotionControllerManagerで発生するすべてのイベントのEventArgsに含めていますので、必要な情報はここからも取得可能です。


## 最後に
現状C#風なイベント制御にしています。また、個人的に使いそうなものをイベント化しています。他にもこういうイベントがあったほうがいいのでは？等あればコメント等で教えてもらえれば検討したいと思います。
なお、Mixed Reality Toolkit-Unityについては実はSingletonしか使っていないのでどこかの段階で自前でSingletonの実装して単独で処理できるようにしようと思ってます。。。
できれば、MRTKに抗してはどうですかしたいのでいずれ移植も考えます。