【Unity(C#)】Oculus Integration内の出来合いの物だけで少しリッチなレーザーポインターを実装する

##Oculus Integration

2019/12/10 追記
サンプル上げました。Oculus Integrationもそのままで上げてるので少し重いです。
https://github.com/ForJobOk/OVR_SimpleLaserPointer

今回はOculus IntegrationのScriptを利用してレーザーポインターを実装します。
今回実装するレーザーポインターは
スライドショーなどで差し棒代わりに使用したり、メニュー画面でUIを選択したり、、、などの用途です。

実際に実装したものがこちらになります。


![hand.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/2b72dd02-70ca-7d20-2a8c-8104f23385e5.gif)

UIにかざした場合のみレーザーポインターが出現するようになってます。
※手は[こちら](https://qiita.com/OKsaiyowa/items/eeb659c38739ad1ec2c9)

---
2019/11/25 追記

[Githubにプロジェクトファイルをアップしました](https://github.com/ForJobOk/OVR_SimpleLaserPointer)

##OVRGazePointer
`OVRGazePointer`は本来は目線にポインターが追従するという用途で用意されています[^1]が、
レーザーを実装してレーザーポインターとして使ってしまいます。

`Awake`と`Update`に書き加えます。
6までナンバリングしたコメントの部分です。
~~必要ない機能などを消したかったので元のScriptに直接書き加えずに新しいScriptを作成しています。~~
必要ない機能などを消したかったので元のScriptに直接書き加えずに
まるまるコピーして新しいScriptとして作成しています。

```C#:OVRGazePointerをコピーして書き換え

    //1.変数を宣言
    LineRenderer lineRenderer;

    [SerializeField]
    GameObject startPointObj,endPointObj;

    public void Awake()
    {
        //2.GetComponentします
        lineRenderer = this.gameObject.GetComponent<LineRenderer>();

        currentScale = 1;
        
        if (_instance != null && _instance != this)
        {
            enabled = false;
            DestroyImmediate(this);
            return;
        }

        _instance = this;

        //3.PointerIconはポインターアイコンのオブジェクト名が入る
        gazeIcon = transform.Find("PointerIcon");
        progressIndicator = transform.GetComponent<OVRProgressIndicator>();
    }

    void Update()
    {
        //4.レーザーの位置を決定
        lineRenderer.SetPosition(0, startPointObj.transform.position);
        lineRenderer.SetPosition(1, endPointObj.transform.position);

        if (rayTransform == null && Camera.main != null)
            rayTransform = Camera.main.transform;

        
        transform.position = rayTransform.position + rayTransform.forward * depth;

        
        if (visibilityStrength == 0 && !hidden)
        {
            Hide();
            //5.消す
            lineRenderer.enabled = false;
        }
        else if (visibilityStrength > 0 && hidden)
        {
            Show();
            //6.出す
            lineRenderer.enabled = true;
        }
    }
```

何回も使い回すようであれば、
**LineRendererは必須**なので`RequireComponent`を使っておくといいと思います。

```C#:OVRGazePointerを元にコピーして新たに作成したOculusLaserPointer
[RequireComponent(typeof(LineRenderer))]
public class OculusLaserPointer: OVRCursor
```

##レーザーの下準備
作成したScriptを適当なオブジェクトにアタッチします。

Inspectorの設定は画像の通りです。(※一部不要なパラメータを削除しています)
![OculusLaserPointer.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/d2a1849b-7150-0003-9ccc-3b0fbb66f194.png)

---
###パラメータの説明
**Hide By Default** - True時はUIにRayがヒットした場合のみレーザーポインターが出現します。Falseだとずっと出てます。
**Show Timeout Period** - UIにヒットした場合にレーザーポインターが出現するまでの秒数です。
**Hide Timeout Period** - レーザーポインターがUIから離れた場合に消滅するまでの秒数です。
**Dim On Hide Request** - 外部からオンオフできます(たぶん)
**Depth Scale Multiplier** - ポインターのスケールです。
**Ray Transform** - 設定したオブジェクトの正面方向(Z軸正の方向)に向かってRayを飛ばします。手のアンカーの階層に入れるといい感じです。
**Start Point Obj** - レーザーポインターの発射位置です。基本的にRay Transformと同じです。
**End Point Obj** - レーザーポインター終了位置です。ポインターの表示されたポジションを利用しています。

##アイコンの下準備
`GazaPointer`というMaterialを使います。

先ほど作成したレーザーポインターをアタッチしたオブジェクトの子にQuadを作成し、MeshColliderを削除します。
そして、`OVROverlay`というScriptをアタッチします。
![OVROverRay.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/f485e2a1-3c90-849f-1cf3-6c1a9eb912ce.png)

Override Color Scaleでポインターの色を変えることができます。

##OVR Raycaster
Canvasに`OVR Raycaster`をアタッチします。`Graphic Raycaster`は必要ありません。

##EventSystem
既存の`Input Module`ではNGなので`OVR Input Module`を利用します。

必要な設定はここだけです。

![EventSystem.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/0ba85db6-479e-8e75-5bab-7b5694e5d1d4.png)

`Joy Pad Click Button`に好きな選択キーを設定できます。(画像の場合、人差し指でボタンやトグルなどを選択可能)
キーについては[こちらの記事](https://qiita.com/nkjzm/items/96cd9cddc645c45dd5e5)がとてもわかり易くまとまっております。


##おまけ
コントローラーが手などの場合は、手から直接レーザーが出ていると違和感があります。
なので画像のようにだんだんと濃くなる感じのShaderを利用しました。

ドンピシャでほしいShader[^2]を書いてる方がいらっしゃたので改良して使わせていただきました。
![HandLaser.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/edcd2cd5-1a49-d94e-680a-dbc83c7f0606.png)


改良と言ってもuvの方向を変更しただけですが。。。

```C++
            fixed4 frag(v2f i) : COLOR{
              /*fixed amount = clamp(abs(_TopColorPos - i.uv.y) + (0.5 - _TopColorAmount), 0, 1);*/
                fixed amount = clamp(abs(_TopColorPos - i.uv.x) + (0.5 - _TopColorAmount), 0, 1);
                i.color = lerp(_TopColor, _ButtomColor, amount);
                return i.color;
               }
```


かなり遠回りですが、出来合いの物だけでいいかんじになったんじゃないでしょうか。
![hand.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/483d6740-9026-688a-f961-fef46c3ff766.gif)

なんかOculusの権利関係ややこしそうなので、
コピーライティング書いときます。

```
Copyright © Facebook Technologies, LLC and
its affiliates. All rights reserved.
```


[^1]: [グラデーションのマテリアルを作る](https://qiita.com/nknk/items/e07ad9f1b2efda382cef)
[^2]: [OculusGoでuGUIを使ってみる＃１](https://qrunch.net/@horukasu/entries/E7miiFShwPdubuJQ)