【Unity入門】OculusGoでLaserPointerで遊んでみた♬

<a href="http://www.youtube.com/watch?feature=player_embedded&v=oTu6GCE_kMw" target="_blank"><img src="http://img.youtube.com/vi/oTu6GCE_kMw/0.jpg" 
 </a>
※画像をクリックするとYouTube動画につながります

こういうのある意味難しい。。。情報が難しすぎたり不足だったりということで、ハマりやすいので記載しておく

【参考】
①[Oculus Goでレーザーポインターを表示する](https://qiita.com/noitak/items/f442a45c2d772d9d8269)
②[LineRendererで線を引いてみよう](http://appdeappuappu.hateblo.jp/entry/2014/06/03/212121)
③[Use GearVrController with Selection Ray - Unity](https://forums.oculusvr.com/developer/discussion/54513/use-gearvrcontroller-with-selection-ray-unity)
④[【Unity / Oculus Go】Oculus Goコントローラーについて](http://rikoubou.hatenablog.com/entry/2018/06/11/161538)

だいたいは参考①のとおりなんだけど、大切なことが記載して無いので、再現できなかった。そこで、参考②を少しだけ引用したい。

さらに、参考③の途中に出ているようにLaserPointerをポインター操作で操りたいということで、参考④を追加している。

###やったこと
（１）例のOculusGoのための設定
（２）OVRCameraRigとCubeとLaserPointerを張り付ける
（３）LaserPointerのC#Script
（４）反射は。。。
###（１）例のOculusGoのための設定
[【Unity入門】OculusGoで静止画集めて動画作成して遊んでみた♬](https://qiita.com/MuAuan/items/acaaf1800763455ff812)の（１）Oculus用に設定するを見てください。
###（２）OVRCameraRigとLaserPointerを張り付ける
①まず、OVRCameraRigを張り付けてください。
![Laser_OVRCameraRig.jpg](https://qiita-image-store.s3.amazonaws.com/0/233744/44ffe4a9-984e-80d6-f3ea-e66e7e8b9272.jpeg)
②LeftHandAnchorとRightHandAnchorにVRのTrackedRemoteを張り付ける
③Create-３DObject-CubeでCubeを張り付けて以下のように設定する
![Laser_Cube.jpg](https://qiita-image-store.s3.amazonaws.com/0/233744/70f8cafc-df1a-9877-0102-bc4bce9b261a.jpeg)
④Create-CreateEmptyを選んで張り付けて、名前をLaserPointerに変更し、AddComponetでEffects-LineRendererでLine Rendererを張り付ける。
そして、以下のとおり設定する。ここで、レーザーの幅は右真ん中のグラフなんだけど、ほとんど０です。他の設定は適当なのでいろいろ変えて試してください。
![Laser_Pointer.jpg](https://qiita-image-store.s3.amazonaws.com/0/233744/f5da7854-bef2-96b7-81c2-c06727961899.jpeg)
###（３）LaserPointerのC#Script
今回のメインディッシュはこれですね。一応、ポインターの操作【参考】④のようになっているので、ここの参考コード

```cs
・押されている間Trueが返るもの
// コントローラーのトリガー
bool trigger = OVRInput.Get(OVRInput.Button.PrimaryIndexTrigger);
// タッチバッドのボタン
bool touchButton = OVRInput.Get(OVRInput.Button.PrimaryTouchpad);
・押された瞬間のみTrueが返るもの
bool backButton = OVRInput.Get(OVRInput.Button.Back);
```
を使わせていただいて、以下の通りにしてみました。
これでポインターの前面を押しているときだけ、レーザーが出るようになりました。

```LaserPointer.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class LaserPointer : MonoBehaviour
{
    [SerializeField]
    private Transform _RightHandAnchor;
    [SerializeField]
    private Transform _LeftHandAnchor;
    [SerializeField]
    private Transform _CenterEyeAnchor;
    [SerializeField]
    private float _MaxDistance = 100.0f;
    private float _MinDistance = 0.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 _RightHandAnchor;   //_CenterEyeAnchor;
        }
    }
    void Update()
    {
        var pointer = Pointer;
        if (pointer == null || _LaserPointerRenderer == null)
        {
            return;
        }
        // コントローラーのトリガー
        if (OVRInput.Get(OVRInput.Button.PrimaryIndexTrigger))
            {
            // コントローラー位置から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);
            }
        }
        else
        {
            // コントローラー位置からRayを飛ばす
            Ray pointerRay = new Ray(pointer.position, pointer.forward);
            // レーザーの起点
            _LaserPointerRenderer.SetPosition(0, pointerRay.origin);
            // コントローラーのトリガー無しの場合、Rayが向いている方向にMinDistance（０）
            _LaserPointerRenderer.SetPosition(1, pointerRay.origin + pointerRay.direction * _MinDistance);
        }
    }
}
```
少し長いですが、意味は分かりやすいコードだと思います。
レーザーの起点をポインターと一緒に持ちまわっていますが、こうしないと最後に押すのを止めたところから紐が伸びてなんか嫌なので冗長な気がしますが、こうしました。
###（４）反射は。。。
反射は、

```
// Rayがヒットしたらそこまで
_LaserPointerRenderer.SetPosition(1, hitInfo.point);
```
を

```
_LaserPointerRenderer.SetPosition(1, hitInfo.point +reflectionRay.direction * _MaxDistance );
```
として、このreflectionRay.directionを計算してやればいいんですが、。。。

といういことで、以下のようにいい加減にやってみると。。。
。。。参考ググっていたら、以下の参考が出てきて、これレーザー光線の先を絞る方法まで出ていました。でも、今回の記事の話はないので、このまま続けます。
【参考】
[【Unity / Oculus Go】Oculus Goコントローラー表示方法とボタン取得方法 ](http://rikoubou.hatenablog.com/entry/2018/06/04/193607)

折角なので、Laserのラインを曲線化しました。
以下のようにグラフ上でダブルクリックすると曲線に変更できます。というか、途中の点を追加して曲線近似できます。
![Laser_LineRenderer.png](https://qiita-image-store.s3.amazonaws.com/0/233744/af379431-bbb7-3fdf-3d54-110debe04071.png)

反射の計算は近くにはないみたいですが、とにかく適当な方向に反射させてしまいましょう。
ということで、以下のようなコードを書いてみましたが、失敗しました。。。

```
RaycastHit hitInfo;
if (Physics.Raycast(pointerRay, out hitInfo, _MaxDistance))
{
    // Rayがヒットしたらそこまで
    _LaserPointerRenderer.SetPosition(1, hitInfo.point);
    //ヒットしたところから、反射させてみる 
    Vector3 incomingV = hitInfo.point - pointerRay.origin;
    Vector3 reflectV = Vector3.Reflect(incomingV, hitInfo.normal);
    _LaserPointerRenderer.SetPosition(2, reflectV);
}
```
一応解説すると、incomingが入力でヒットした壁の法線ベクトルと入力ベクトルから反射ベクトルを計算してそちらへLaserPointerを出しているつもり、。。。しかしなぜか発射してくれなかった。。。なれど(1,hitInfo.pointL+reflectV)などとすると、反射すると光線が変な方向へ行くので、計算は正しくしているようだ。。。今日のところはどうしても解決しなかったので、この課題は後日に持ち越すこととする。
【参考】
①[Vector3.Reflect](https://docs.unity3d.com/ScriptReference/Vector3.Reflect.html)
②[RaycastHit.normal](https://docs.unity3d.com/ScriptReference/RaycastHit-normal.html)

[OculusGo：Laser_Pointerをポインター操作する。。離すと消える♬](https://youtu.be/oTu6GCE_kMw)

<a href="http://www.youtube.com/watch?feature=player_embedded&v=oTu6GCE_kMw" target="_blank"><img src="http://img.youtube.com/vi/oTu6GCE_kMw/0.jpg" 
 </a>
※画像をクリックするとYouTube動画につながります

###まとめ
・LaserPointerを実装してみた
・OculusGoのポインターで操作できるようになった

・反射に挑戦してみたが、どうもうまくいかなかったが、計算はあっているようだ。
描画を工夫すればできそうな気がするところまで来た。。。