【Unity(C#)】GVRのRay、コントローラーのボタン入力まとめ

##GVRとは？
Google VRという、iOSおよびAndroid用にGoogleが開発しているVRのSDKです。

最近、MirageSoloで開発することが多かったので忘れないようにメモしときます。

ほとんどが英語のドキュメントからの情報で、英語力うんちっちの私がまとめますので
間違っていた際は教えてくださると助かります。

![手がきGVRcontroller.png](https://qiita-image-store.s3.amazonaws.com/0/244071/dadcef17-dfbd-4e77-6c00-5671dea57830.png)


GVRにはこんな感じ↑のコントローラーでのプレイを想定した機能が用意されています。


このコントローラーに`GvrPointerPhysicsRaycaster`をAdd Componentすることで
Rayが出るようになり、レーザーポインターのようにして使うことができます。

HelloVRというデモシーンが用意されているので中身を覗いてみます。
![HelloVR.png](https://qiita-image-store.s3.amazonaws.com/0/244071/2eee4eeb-8745-31d1-580f-7191042ebda6.png)


![GifCapture-201903301528380546.gif](https://qiita-image-store.s3.amazonaws.com/0/244071/c76a6e03-bda4-9518-66bc-cfcfc3793e11.gif)

ポインターが多面体にEnter、Exitでマテリアルが変わるようになってます。
タッチパッドをクリックしたら別の多面体が出現します。

多面体側に`Event Trigger`がAdd Componentされており、
ここで処理が行われているようです。
![EventTrigger.png](https://qiita-image-store.s3.amazonaws.com/0/244071/3185b01e-0361-f8d0-fe36-5473fb3ba38e.png)

仕組みは極めて単純になっており、
・PointerがEnter
・PonterがExit
・Pointerが当たった状態でタッチパッドを押す
等のそれぞれ用意されたイベントハンドラにイベントを登録することができます。

他にもイベントはたくさん用意されてましたが、よく使いそうなものだけメモしときます。

| イベントハンドラ  |条件　　　　　　　　　　　　|    
|:-----------------|------------------:|
| PointerEnter     | ポインター(Ray)がオブジェクトに衝突|   
| PonterExit       | ポインター(Ray)がオブジェクトから離れる|      
| PonterDown       | ポインター(Ray)がオブジェクトに衝突した状態でタッチパッドを押す|       
| PonterUp　       | ポインター(Ray)がオブジェクトに衝突した状態で押し込まれたタッチパッドを離す |        
| PonterClick      | ポインター(Ray)がオブジェクトに衝突した状態でタッチパッドを押して離す |       
    


しかし、これだとポインターが特定のオブジェクトに衝突したら...という処理は実装できますが、
単純にボタンを押したら...という処理はできません。
また、オブジェクトに毎回つけるの正直めんどくさいです。

なので、別の方法を調べました。

**・RayがHitしたら...**
**・指定したボタンを押したら...**

を調べれば`Event Trigger`と同様のことも再現できるはずです。

## GvrPointerPhysicsRaycaster
unityにすでに[Ray](https://qiita.com/OKsaiyowa/items/5bf32ec513bcd987ab8e)の機能はあるのでそれでいいかな～と思ってたのですが、
反応があまりよくありませんでした。
なので`GvrPointerPhysicsRaycaster`を使います。

ついでに、なぜ普通のRayだと反応が悪いのか気になるので調べてみました。

まず、比較しやすいように
`Laser`の設定から`Direct`を選んで、コントローラーの正面からまっすぐGVR用のRayが出るようにしました。
![Laser.png](https://qiita-image-store.s3.amazonaws.com/0/244071/5676c78e-7281-c71c-0cb3-7da1a18c999a.png)

そして、Rayを`Debug.DrawRay`で表示。
![LaserDebug.png](https://qiita-image-store.s3.amazonaws.com/0/244071/7045f988-609d-864e-de1d-1c5359f51905.png)

赤線がUnityのRay、青線がGVRのRayです。

GVR用のRayには若干角度がついていました。

結局なぜこんなことになってるのかよくわかりませんでしたが、
ポインターもあった方がわかり易いので、
やはり`GvrPointerPhysicsRaycaster`を使ったほうがよさそうです。

```C#

 if (GvrPointerInputModule.CurrentRaycastResult.isValid)
 {
    if (GvrPointerInputModule.CurrentRaycastResult.gameObject.tag == "Cube" )
    {
       print("TagがCubeのオブジェクトに衝突");
    }
 }

```
上記コードで通常のRayと同様、Tagで特定のゲームオブジェクトにのみRayの衝突判定を行えます。

## GvrControllerInputDevice
ボタンの入力は`GvrControllerInputDevice`からbool値を引っ張ってきます。

| コード  |処理内容　　　　　　　　　　　|    
|:-----------------|------------------:|
| GetButtonDown(GvrControllerButton.TouchPadTouch)| タッチパッドに触れたら瞬間(1フレーム)、Trueを返す|   
| GetButtonUp(GvrControllerButton.TouchPadTouch)| 触れているタッチパッドから離れた瞬間(1フレーム)、Trueを返す|      
| GetButton(GvrControllerButton.TouchPadTouch)| タッチパッドに触れている間、Trueを返す|       
| GetButtonDown(GvrControllerButton.TouchPadButton)| タッチパッドを押した瞬間(1フレーム)、Trueを返す|   
| GetButtonUp(GvrControllerButton.TouchPadButton)| 押したタッチパッドを離した瞬間(1フレーム)、Trueを返す|      
| GetButton(GvrControllerButton.TouchPadButton)| タッチパッドを押している間、Trueを返す|       
| GetButtonDown(GvrControllerButton.App)| Appボタンを押した瞬間(1フレーム)、Trueを返す|   
| GetButtonUp(GvrControllerButton.App)| 押したAppボタンを離した瞬間(1フレーム)、Trueを返す|      
| GetButton(GvrControllerButton.App)| Appボタンを押している間、Trueを返す|    
| GetButtonDown(GvrControllerButton.System)| Homeボタンを押した瞬間(1フレーム)、Trueを返す|   
| GetButtonUp(GvrControllerButton.System)| 押したHomeボタンを離した瞬間(1フレーム)、Trueを返す|      
| GetButton(GvrControllerButton.System)| Homeボタンを押している間、Trueを返す|    

Homeボタンの入力も用意されていますが、
中心にトラッキングし直したり、文字通りHome画面を表示したりするボタンなので使うことはないと思います。

以上の入力で戻ってきたbool値を下記のように使います。

```C#
GvrControllerInputDevice gvrControllerInput;

 void Start()
    {
        gvrControllerInput = GvrControllerInput.GetDevice(GvrControllerHand.Dominant);
    }

void Update()
    {
        if (gvrControllerInput.GetButtonDown(GvrControllerButton.App))
        {
            print("あっぷぼたんおした")
        }
    }

```

`GetDevice`の引数は`GvrControllerHand.Dominant`でOKです。

##まとめ
**`Rayの衝突`かつ`ボタンの入力`** ＝　**`Event Trigger`の`Ponter Click`**

```C#
//Rayの衝突＆Appボタンを押したら
 if (GvrPointerInputModule.CurrentRaycastResult.isValid && gvrControllerInput.GetButtonDown(GvrControllerButton.App))
 {
    if (GvrPointerInputModule.CurrentRaycastResult.gameObject.tag == "Cube" )
    {
      //やりたい処理
       print("Cubeに衝突");
    }
 }
```

せっかくタッチパッドがあるので、スワイプとかも調べてまとめたいと思ってます。

---
関連記事：[GVRのコントローラーに操作説明を表示する](https://qiita.com/OKsaiyowa/items/0fde964f9f797ac17850)