Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.


※画像をクリックするとYouTube動画につながります

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

【参考】
Oculus Goでレーザーポインターを表示する
LineRendererで線を引いてみよう
Use GearVrController with Selection Ray - Unity
【Unity / Oculus Go】Oculus Goコントローラーについて

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

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

やったこと

(1)例のOculusGoのための設定
(2)OVRCameraRigとCubeとLaserPointerを張り付ける
(3)LaserPointerのC#Script
(4)反射は。。。

(1)例のOculusGoのための設定

【Unity入門】OculusGoで静止画集めて動画作成して遊んでみた♬の(1)Oculus用に設定するを見てください。

(2)OVRCameraRigとLaserPointerを張り付ける

①まず、OVRCameraRigを張り付けてください。
Laser_OVRCameraRig.jpg
②LeftHandAnchorとRightHandAnchorにVRのTrackedRemoteを張り付ける
③Create-3DObject-CubeでCubeを張り付けて以下のように設定する
Laser_Cube.jpg
④Create-CreateEmptyを選んで張り付けて、名前をLaserPointerに変更し、AddComponetでEffects-LineRendererでLine Rendererを張り付ける。
そして、以下のとおり設定する。ここで、レーザーの幅は右真ん中のグラフなんだけど、ほとんど0です。他の設定は適当なのでいろいろ変えて試してください。
Laser_Pointer.jpg

(3)LaserPointerのC#Script

今回のメインディッシュはこれですね。一応、ポインターの操作【参考】④のようになっているので、ここの参考コード

・押されている間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(0)
            _LaserPointerRenderer.SetPosition(1, pointerRay.origin + pointerRay.direction * _MinDistance);
        }
    }
}

少し長いですが、意味は分かりやすいコードだと思います。
レーザーの起点をポインターと一緒に持ちまわっていますが、こうしないと最後に押すのを止めたところから紐が伸びてなんか嫌なので冗長な気がしますが、こうしました。

(4)反射は。。。

反射は、

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

_LaserPointerRenderer.SetPosition(1, hitInfo.point +reflectionRay.direction * _MaxDistance );

として、このreflectionRay.directionを計算してやればいいんですが、。。。

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

折角なので、Laserのラインを曲線化しました。
以下のようにグラフ上でダブルクリックすると曲線に変更できます。というか、途中の点を追加して曲線近似できます。
Laser_LineRenderer.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
RaycastHit.normal

OculusGo:Laser_Pointerをポインター操作する。。離すと消える♬


※画像をクリックするとYouTube動画につながります

まとめ

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

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

MuAuan
2020年目標;いい記事を書く 記事350いいね1500フォロワー150 2019年の実績/目標 記事275/300いいね1035/1000フォロワー97/100 1/7/2019 記事219いいね784フォロワー76 2018年の実績/目標 記事140/200いいね423/500フォロワー48/50 7/8/2018 記事90いいね227フォロワー25
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away