OculusGoでEventSystemを使ってオブジェクトをクリックする方法(not uGUI)

OculusGoでEventSystemを利用して、OculusGoコントローラーからポイントしたオブジェクトのイベントを実行する方法です。
uGUIだといくつか記事が見つかりましたが、普通の3Dオブジェクトをクリックする記事は見つからなかったので書いてみました。

環境: Unity 2018.1.5f1 (64-bit) / OculusUtilities(1.26.0 2018/06/20)

## 手順

### 1. GameObjectにイベントハンドラを設定する
IPointerClickHandlerなどの、普通はマウスクリックを受け取るイベントハンドラを実装したコードを書いて、適当なGameObjectにアタッチします。
以下のようなコードを書いて適当にCubeなどにアタッチしてください。

```c#
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems; // この行も追加が必要なので注意

public class CubeClickSample : MonoBehaviour, IPointerExitHandler, IPointerEnterHandler, IPointerClickHandler {
    public void OnPointerClick(PointerEventData pointerEventData) {
        gameObject.GetComponent<Renderer>().material.color = Color.green;
    }
    public void OnPointerEnter(PointerEventData pointerEventData) {
        gameObject.GetComponent<Renderer>().material.color = Color.red;
    }
    public void OnPointerExit(PointerEventData pointerEventData) {
        gameObject.GetComponent<Renderer>().material.color = Color.white;
    }
}
```

### 2. EventSystem、OVRInputModuleを追加
ゲームオブジェクトの追加でUI→EventSystemを追加します。
EventSystemからStandaloneInputModuleを削除して代わりにOVRInputModuleを追加します。
OVRInputModuleのRayTransformを、RightHandAnchorに設定します。

(OVRCameraRigは既に配置してある前提。手順がわからないならこちらを参照：https://qiita.com/culage/items/fe9b5ab9e0e286ba167e)

### 3. OVRPhysicsRaycsterを追加
OVRCameraRigにOVRPhysicsRaycsterを追加します。
これはOVRCameraRigスクリプトがアタッチされているオブジェクトに追加する必要があります。

### 4. レーザーポイント先を表示するためのOVRGazePointerを追加
/Oculus/VR/Scenes/UI の中にある OVRGazePointer をプレハブ化します。
そのプレハブを自分のシーンに追加します。
追加したOVRGazePointerのRayTransformを、RightHandAnchorに設定します。

### 5. 実行する

以上で、Cubeに設定されしたコンポーネントのOnPointerClick(), OnPointerEnter(), OnPointerExit()がOculusGoコントローラーでポイントして呼び出せるようになります。
**OculusGoコントローラーは右手の設定にしておかないと動きません。対策は後述。**

OculusGoコントローラーでCubeを指したとき、その状態でタッチパッドボタンを押したときにCubeオブジェクトの色が変わるはずです。


## 感想

「4. レーザーポイント先を表示するためのOVRGazePointerを追加」が必須の作業であったことが意外な落とし穴でした。
OVRGazePointerが存在しないと、イベント発火するまえにエラーで止まってしまいイベントシステムが機能しないみたいです。


## 付録1. 自分でOVRGazePointerを作る方法
1. 空のGameObjectを作成する。名前はなんでもいいが"OVRGazePointer"とかにしとく
2. QuadのGameObjectを作成して名前を"GazeIcon"にする。スクリプト内で名前が使われているのでこの名前にする必要あり。
これがポインタ表示になるので適当に画像などを設定。
3. "GazeIcon"を"OVRGazePointer"の子にする
4. "OVRGazePointer"にOVRGazePointerコンポーネントをアタッチする
5. OVRGazePointerコンポーネントのRayTransformプロパティをRightHandAnchorに設定

## 付録2. 左右コントローラー切り替えに対応する
どうも自動で左右コントローラーに対応するような設定は無いみたいなので、Updateイベントで現在のコントローラー設定を見てRayTransformプロパティを切り替えて対応するしかないようです。
以下のようなコードを作成して適当なオブジェクトにアタッチしてください。

参考twitter発言：
https://twitter.com/ooba/status/1001239375068491776

```C#
public class EventRayTransfomChanger : MonoBehaviour {
	private Transform _rightHandAnchor = null;
	private Transform _leftHandAnchor  = null;
	private Transform _centerEyeAnchor = null;

	void Start()
	{
		_rightHandAnchor = GameObject.Find("RightHandAnchor").transform;
		_leftHandAnchor  = GameObject.Find("LeftHandAnchor" ).transform;
		_centerEyeAnchor = GameObject.Find("OVRCameraRig"   ).transform;
	}

	Transform GetActiveRayTransform() {
		var controller = OVRInput.GetActiveController();
		if (controller == OVRInput.Controller.RTrackedRemote) { return _rightHandAnchor; }
		if (controller == OVRInput.Controller.LTrackedRemote) { return _leftHandAnchor;  }
		return _centerEyeAnchor;
	}

	void Update () {
		if (Time.frameCount % 60 == 0) {
			var rayTransform = GetActiveRayTransform();
			GameObject.Find("EventSystem").GetComponent<UnityEngine.EventSystems.OVRInputModule>().rayTransform = rayTransform;
			GameObject.Find("OVRGazePointer").GetComponent<OVRGazePointer>().rayTransform = rayTransform;
		}
	}
}
```

## 付録3. OculusGoではなく、PC上で実行するプロジェクトでマウスに反応させる方法

これはもうOculusとは関係ない世界ですがOculus版との対比として、PC上で実行するプロジェクトで同じようにマウスでオブジェクトをクリックできるようにする方法を書いておきます。

1. 「GameObjectにイベントハンドラを設定する」は同じ
2. ゲームオブジェクトの追加でUI→EventSystemを追加 (InputModuleは既定でアタッチされているStandaloneInputModuleのままで問題ない)
3. メインカメラのGameObjectにPhysicsRaycsterを追加

これだけです。