Oculus Goでレーザーポインターを表示する

Oculus Go でコントローラーからレーザーポインター的なガイドを表示する方法を調べたので備忘録として。

![out.gif](https://qiita-image-store.s3.amazonaws.com/0/241491/2e12c3fc-8b71-b8f9-5224-6d18bbdc85a5.gif)

## 参考
[Use GearVrController with Selection Ray - Unity](https://forums.oculusvr.com/developer/discussion/54513/use-gearvrcontroller-with-selection-ray-unity)

基本的にここに書かれている通りにやればOK。

## 使用中のコントローラー（左右どちら？）を取得する
Oculus Go では左右どちらの手でコントローラーを使うか設定できるので、アプリ内での表示もこの設定に沿う必要があります。現在コントローラーを左右どちらの手で使っているかはOVRInput.GetActiveController で取得できます。

```C#
// 現在アクティブなコントローラーを取得
var controller = OVRInput.GetActiveController();
if (controller == OVRInput.Controller.RTrackedRemote)
{
    // 右手
    return _RightHandAnchor;
}
else if (controller == OVRInput.Controller.LTrackedRemote)
{
    // 左手
    return _LeftHandAnchor;
}
```

OVRInput.GetActiveController はStart やAwake のタイミングでは取得できなかった（OVRInput.Controller.None が返ってくる）ので、Update 中で取得しています（ちょっとハマった。素直に参考記事のとおりにすればよかった）。

[Oculus 公式のOVRInput ドキュメント](https://developer.oculus.com/documentation/unity/latest/concepts/unity-ovrinput/)によると、OVRInput.IsControllerConnected でも判定できる模様。

## コントローラー位置の取得する
コントローラー(手)の位置そのものはOVRCameraRig に付いているLeftHandAnchor/RightHandAnchor から取得できます。

コントローラーのモデルを表示したい場合はOculus Integration に付属のPrefav、TrackedRemote をLeftHandAnchor/RightHandAnchor 以下にそれぞれ配置すると、右手左手の設定に応じていい具合に表示を切り替えてれます。

![ovrcamerarig.png](https://qiita-image-store.s3.amazonaws.com/0/241491/791470fc-dba4-b7ef-9396-f161c35e9a32.png)

あとはこの位置からRay を飛ばしてレーザーを表示すればOK。

## コード
```LaserPointer.cs
public class LaserPointer : MonoBehaviour 
{
    [SerializeField]
    private Transform _RightHandAnchor;

    [SerializeField]
    private Transform _LeftHandAnchor;

    [SerializeField]
    private Transform _CenterEyeAnchor;

    [SerializeField]
    private float _MaxDistance = 100.0f;

    [SerializeField]
    private LineRenderer _LaserPointerRenderer;

    private Transform Pointer
    {
        get
        {
            // 現在アクティブなコントローラーを取得
            var controller = OVRInput.GetActiveController();
            if (controller == OVRInput.Controller.RTrackedRemote)
            {
                return _RightHandAnchor;
            }
            else if (controller == OVRInput.Controller.LTrackedRemote)
            {
                return _LeftHandAnchor;
            }
            // どちらも取れなければ目の間からビームが出る
            return _CenterEyeAnchor;
        }
    }

	void Update () 
    {
        var pointer = Pointer;
        if (pointer == null || _LaserPointerRenderer == null)
        {
            return;
        }
        // コントローラー位置からRayを飛ばす
        Ray pointerRay = new Ray(pointer.position, pointer.forward);

        // レーザーの起点
        _LaserPointerRenderer.SetPosition(0, pointerRay.origin);

        RaycastHit hitInfo;
        if (Physics.Raycast(pointerRay, out hitInfo, _MaxDistance))
        {
            // Rayがヒットしたらそこまで
            _LaserPointerRenderer.SetPosition(1, hitInfo.point);
        }
        else
        {
            // Rayがヒットしなかったら向いている方向にMaxDistance伸ばす
            _LaserPointerRenderer.SetPosition(1, pointerRay.origin + pointerRay.direction * _MaxDistance);
        }
	}
}
```

![lp.png](https://qiita-image-store.s3.amazonaws.com/0/241491/83fcd977-3e91-f221-1e77-2be4b7f82ef8.png)

## まとめ
ポインター操作にGaze を使うゲームは分かりやすいんですが首が疲れるので、せっかくなのでコントローラーを使っていきたいと思います。