GearVR x Unity  - 視線や専用コントローラーで操作するサンプル


GearVRアプリで、視線でメニューを選んだり、別売りのリモコンを使って操作するサンプルです。

(ビルド方法などは省略します。コチラが参考になります→ https://framesynthesis.jp/tech/unity/gearvr/ )


# ①視線入力で選択
向いた方向の視線で操作するサンプルです。
外付けのコントローラーがない場合は、
メニュー選択など様々な場面で使うことになると思います。

## アセットのインポート
Unityが提供しているVR Sampleアセットは、VRアプリを作る上で必要な機能が詰め込まれていて勉強になります。
こちらからインポートして下さい。
https://www.assetstore.unity3d.com/jp/#!/content/51519

## MainCameraの置き換え

まずはMainCameraをVR対応のものに置き換えます。

デフォルトのMainCamraを削除して
VRSampleScenes/Prefabs/Utils/MainCamera
に置き換えましょう。

![スクリーンショット 2017-07-15 15.10.18.png](https://qiita-image-store.s3.amazonaws.com/0/29775/02fdd205-5539-c4d9-97ad-e77aa7f2d82d.png "スクリーンショット 2017-07-15 15.10.18.png")

このMainCamraには視線のターゲットとなる印や、キー入力をハンドルするスクリプトが組み込まれています。

MainCamera配下のGUIReticleが照準を示す点、UISelectionBarが照準が合っているときにギューンと増えるゲージです。
今回はゲージは必要ないので、UISelectionBarとBackgroundは無効にし、GUIReticleのImageだけ使うので有効にしておきます。

次に、Editorでの開発用にカメラをキーボードで動かせるようにしておきます。
以下のスクリプトをMainCameraにアタッチします。
これでEditorで開発中はカーソルで向いてる方向を変えることが出来ます。

```EditorCameraRotation.cs
public class EditorCameraRotation : MonoBehaviour
{

  void Update()
  {
#if UNITY_EDITOR
    if (Input.GetKey(KeyCode.RightArrow))
    {
      this.transform.Rotate(0, 1, 0);
    }
    else if (Input.GetKey(KeyCode.LeftArrow))
    {
      this.transform.Rotate(0, -1, 0);
    }
    else if (Input.GetKey(KeyCode.UpArrow))
    {
      this.transform.Rotate(-1, 0, 0);
    }
    else if (Input.GetKey(KeyCode.DownArrow))
    {
      this.transform.Rotate(1, 0, 0);
    }
#endif
  }
}
```

## 対象のオブジェクトの配置

視線が合うと反応する対象のオブジェクトを作成します。

Cubeを適当な位置に配置し、
VRStarndardAssets/Scripts/VRInteractiveItem.cs
をCubeにアタッチします。

VRInteractiveItem.csは、先ほどのMainCameraが発するRayCastを受け取ってイベントを発行してくれるスクリプトになります。

## 視線が当たったときのアクション

先ほどのVRInteractiveItem.csの発行するイベントを受け取って、何らかのアクションをするようにしてみましょう。

Cubeに以下のスクリプトをアタッチします。


```ChangeColor.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using VRStandardAssets.Utils;

public class ChangeColor : MonoBehaviour
{
  [SerializeField] private VRInteractiveItem m_InteractiveItem;
  public bool isGazeOver;

  private void OnEnable()
  {
    m_InteractiveItem.OnOver += HandleOver;
    m_InteractiveItem.OnOut += HandleOut;
  }

  private void OnDisable()
  {
    m_InteractiveItem.OnOver -= HandleOver;
    m_InteractiveItem.OnOut -= HandleOut;
  }

  // 視線が当たったとき
  private void HandleOver()
  {
    isGazeOver = true;
    gameObject.GetComponent<Renderer>().material.color = Color.red;
  }

  // 視線が外れたとき
  private void HandleOut()
  {
    isGazeOver = false;
    gameObject.GetComponent<Renderer>().material.color = Color.white;
  }
}


```

Editor上でVRInteractiveItemに自分自身のCubeを指定しておきます。
![qiita1.png](https://qiita-image-store.s3.amazonaws.com/0/29775/37d078d8-3659-616b-eeb8-92cb57874e34.png "qiita1.png")


## 動作確認
Editorで起動してみると、視線が当たったときにCubeの色が変わっていることが確認できます。
この手順で、例えばどのメニューに今視線が当たってるかを判断したり出来ます。

![gearvr_cube.mov.gif](https://qiita-image-store.s3.amazonaws.com/0/29775/221c63cc-1121-bf52-36f2-03ec47bbdba1.gif "gearvr_cube.mov.gif")


# ②GearVRタッチパッドの入力を受け取る

GearVRには側面にタッチパッドが付いています。
先ほどの続きで、タッチパッドがタップされたらCubeが緑になるように改修してみます。

ChangeColor.csに以下のコードを追加します。
これで視線が当たっているときに、タップするとCubeが緑色に変化するようになります。

```ChangeColor.cs

public class ChangeColor : MonoBehaviour
{
  /*中略*/
  
  private void OnEnable()
  {
    /*中略*/
    m_InteractiveItem.OnClick += HandleClick;
  }

  private void OnDisable()
  {
    /*中略*/
    m_InteractiveItem.OnClick -= HandleClick;
  }

  /*中略*/
  // Clickされたとき
  private void HandleClick()
  {
    if(isGazeOver)
    {
      gameObject.GetComponent<Renderer>().material.color = Color.green;
    }
  }
}
```


実行結果です。
この要領で、メニューをタップで選択する機能などが実装できると思います。
![qiita3.mov.gif](https://qiita-image-store.s3.amazonaws.com/0/29775/898acfa8-e9c9-c2d1-ddf4-b6b12533d5dd.gif "qiita3.mov.gif")


# ③Gear VR コントローラーを使ってみる
GearVRには別売りで専用のコントローラーがあります。
これを使うとレーザーポインターのような感覚でメニューを選んだり、コントローラーで狙ってガンシューティングしたりと操作の幅が広がります。


## Oculus Utilities for Unity 5

Oculusの提供するUtilityをインポートします。
https://developer.oculus.com/downloads/package/oculus-utilities-for-unity-5/
からダウンロードします。

## Main Cameraの差し替え
デフォルトのMainCameraを削除して
OVR/Prefabs/OVRCameraRig
を配置します。

![スクリーンショット 2017-07-15 22.13.11.png](https://qiita-image-store.s3.amazonaws.com/0/29775/c7614227-11be-1f39-a05c-834af7873801.png "スクリーンショット 2017-07-15 22.13.11.png")

## コントローラーのモデル表示
RightHnadAnchorの配下に
Assets/OVR/Prefabs/GearVrController
を配置し、以下のように設定します。
![スクリーンショット 2017-07-15 22.16.15.png](https://qiita-image-store.s3.amazonaws.com/0/29775/3d58b1b0-5d4f-58f8-4630-fb0c12a284a6.png "スクリーンショット 2017-07-15 22.16.15.png")

同様にLeftHandAnchorも設定します。
![qiita2.png](https://qiita-image-store.s3.amazonaws.com/0/29775/e521f267-a667-5cc4-2121-28baa26e4e57.png "qiita2.png")

## 動作確認

手元を見るとコントローラーが表示されていて、手首の動きに連動しているはずです。
![gearv_controller1.mov.gif](https://qiita-image-store.s3.amazonaws.com/0/29775/ffb133f1-833b-c038-2518-6adc171ee933.gif "gearv_controller1.mov.gif")


## コントローラーで指して選択する

先ほどの視線入力を応用します。

VRSampleScenes/Prefabs/Utils/MainCamera
のプレファブ内のVRCameraUIだけコピーして、RightHandAnchorの配下に配置します。

![qiita3.png](https://qiita-image-store.s3.amazonaws.com/0/29775/271a16eb-f581-3957-6e57-1666c6172ce9.png "qiita3.png")

RightHandAnchorに以下のスクリプトをアタッチします。
- VRInput.cs
- Reticle.cs
- VREyeRaycaster.cs

そして以下のように設定します。
![スクリーンショット 2017-07-15 22.41.15.png](https://qiita-image-store.s3.amazonaws.com/0/29775/a9d661da-b35a-1ab8-3fdd-9dde00678a1e.png "スクリーンショット 2017-07-15 22.41.15.png")

対象のオブジェクト(Cube)を配置します。前回と同じくVRInteractive.csとChangeColor.csをアタッチします。

ビルドして実機で確認すると、こんな感じでコントローラーでCubeを指すと色が変わります。

![gearvr_controller_demo.mov.gif](https://qiita-image-store.s3.amazonaws.com/0/29775/57d7cd51-1083-fb63-e709-d936b5b79f8a.gif "gearvr_controller_demo.mov.gif")

# ④GearVRコントローラーのトリガーやタッチパッドの入力を受け取る

GearVRコントローラーにはトリガー、タッチパッドが付いています。
これらを利用すればモノを掴んだり、銃のトリガーを引くなど面白い操作が可能になります。

これらの入力を扱いやすくするスクリプトを用意してみました。
（Unity公式のVRInput.csを参考にしています）

```GearVRInput.cs

using System;
using UnityEngine;

public class GearVRInput : MonoBehaviour
{
  public event Action OnTriggerDown;
  public event Action OnTriggerUp;
  public event Action OnSwipeUp;
  public event Action OnSwipeDown;
  public event Action OnSwipeLeft;
  public event Action OnSwipeRight;
  public event Action OnTouchPadTouchDown;
  public event Action OnTouchPadTouchUp;
  public event Action OnTouchPadButtonDown;
  public event Action OnTouchPadButtonUp;

  private void Update()
  {
    CheckInput();
  }

  private void CheckInput()
  {
    // トリガー
    if (OVRInput.GetDown(OVRInput.Button.PrimaryIndexTrigger))
    {
      if (OnTriggerDown != null)
          OnTriggerDown();
    }

    if (OVRInput.GetUp(OVRInput.Button.PrimaryIndexTrigger))
    {
      if (OnTriggerUp != null)
          OnTriggerUp();
    }

    // スワイプでButton.Up, Down, Left, Rightが反応するっぽい
    if (OVRInput.GetDown(OVRInput.Button.Up))
    {
      if (OnSwipeUp != null)
          OnSwipeUp();
    }

    if (OVRInput.GetDown(OVRInput.Button.Down))
    {
      if (OnSwipeDown != null)
          OnSwipeDown();
    }

    if (OVRInput.GetDown(OVRInput.Button.Left))
    {
      if (OnSwipeLeft != null)
          OnSwipeLeft();
    }

    if (OVRInput.GetDown(OVRInput.Button.Right))
    {
      if (OnSwipeRight != null)
          OnSwipeRight();
    }

    // タッチパッドのタッチ
    if (OVRInput.GetDown(OVRInput.Touch.PrimaryTouchpad))
    {
      if(OnTouchPadTouchDown != null)
        OnTouchPadTouchDown();
    }

    if (OVRInput.GetUp(OVRInput.Touch.PrimaryTouchpad))
    {
      if(OnTouchPadTouchUp != null)
        OnTouchPadTouchUp();
    }

    // タッチパッドのクリック
    if (OVRInput.GetDown(OVRInput.Button.PrimaryTouchpad))
    {
      if(OnTouchPadButtonDown != null)
        OnTouchPadButtonDown();
    }

    if (OVRInput.GetUp(OVRInput.Button.PrimaryTouchpad))
    {
      if(OnTouchPadButtonUp != null)
        OnTouchPadButtonUp();
    }

  }

  private void OnDestroy()
  {
    OnTriggerDown = null;
    OnTriggerUp = null;
    OnSwipeUp = null;
    OnSwipeDown = null;
    OnSwipeLeft = null;
    OnSwipeRight = null;
    OnTouchPadTouchDown = null;
    OnTouchPadTouchUp = null;
    OnTouchPadButtonDown = null;
    OnTouchPadButtonUp = null;
  }
}
```

このGearVRInput.csをCameraなど適当なオブジェクトにアタッチして、
使う側では以下のような感じで使えます。

```
// トリガー入力を受け取って何かする例
public class GearVRInputTest : MonoBehaviour
{
  [SerializeField] GearVRInput m_GearVRInput;

  private void OnEnable()
  {
    m_GearVRInput.OnTriggerDown += HandleTriggerDown;
  }

  private void OnDisable()
  {
    m_GearVRInput.OnTriggerDown -= HandleTriggerDown;

  }

  private void HandleTriggerDown()
  {
    Debug.Log("Trigger Down!");
  }
}

```


以上になります。
GearVR、またその専用コントローラーを使った基本的なUI操作を実装する際の参考になれば幸いです。