fc2ブログ
ヽ|∵|ゝ(Fantom) の 開発blog? ホーム » Unity »【Unity】VRレーザーポインター(ビーム)を作る

【Unity】VRレーザーポインター(ビーム)を作る  


 あくまで一例だが、簡単なレーザーポインター(ビーム)の実装方法を書いておこう。これは実際に私の作ったアプリに使っているものと同じだ。フリーなのでインストして試してみるのも良いだろう。

※Oculus Store で配信中(検索:sapphiart)

 はじめに大まかに仕様的なものを説明しておくと、今回の実装方法は「レーザーの発射位置(Anchor)から指している位置(Target)に向かってラインレンダラ(Line Renderer)で線を引く」という非常に簡単なものである(笑)。なので例として GearVR コントローラを使っているが、たぶん他のコントローラでも移植はそう難しくはないだろう。また SDK 依存を減らすためにも、レーザーを描く部分とそれを SDK(ここでは Unity 公式の VRSample)で使う部分を分けて書いておいた。レーザーで指すだけという最低限の機能しか付けてないが、コードの内容を理解すれば、他の SDK やコントローラでも簡単に応用できるだろう。

 また、今回は Oculus Utilities 1.16.0-beta を使用することにおいて、Unity2017.1f03 でやってみたら色々エラーが出るようなので、Unity 5.6.2p3 を使っている。なので自力で修正、または今後のバージョンアップで修正されたのなら、細かい仕様が変わってしまっている恐れがあるので、適宜置き換えて考えて欲しい。

(2018/07/21 追記)
 Oculus Utilities v1.27.0, VRSample 1.4, Unity2018.1.8f1 でデモシーンをパッケージ化したものを用意しました(Unity2017.3.0f3, Unity2017.4.2f2, Unity2018.1.8f1 で動作確認済み。簡単な仕様なので他のバージョン・環境でも大丈夫だと思う)。
とりあえず試したい方は Google Drive からダウンロードすることもできます(名称・構成などは記事とは多少異なってますが内容は同じです)。
また、流用・改変なども自由にやって貰って構いません。
>>レーザーポインタのデモをダウンロード
(Google Drive を利用。画面右上にあるダウンロードアイコンを押す)



(※) Unity 5.6.2p3 / Windows10(x64) で確認



■レーザー(ビーム)ポインタをラインレンダラで描く

using UnityEngine;

/// <summary>
/// LineRenderer によるレーザーポインタ
/// 2017/8/21 Fantom (Unity 5.6.2p3)
/// http://fantom1x.blog130.fc2.com/blog-entry-259.html
///(使い方)
///・空の GameObject にアタッチし、LineRenderer を追加する(線の幅[Width:0.005くらい]やマテリアル・色も設定する)。
///※LineRenderer のマテリアルを「Sprites-Default」などアルファが利くものにし、色のアルファを設定すれば半透明レーザーにできる。
///※Use World Space は true である必要がある(デフォルト値)。
///・インスペクタで Anchor にコントローラなどの GameObject をセットする。
///・認識させるオブジェクトにコライダを追加する。
///(仕様説明)
///・anchor → target へ LineRenderer で線(レーザー)を引く。
///・LineRenderer は厚さのないテープのようなものなので(ただし常にカメラの方を向いている)、カメラ位置とanchorが同じで、targetが真正面方向だと見えない(故にanchorのY軸をずらせば見える)。
///・外部の Raycaster で判定をするときは shotRay をオフにして、target に指す位置を入れる。
///・shotRay がオンのときは anchor 位置から anchor.forward 方向へ Ray を飛ばし、ヒットしたオブジェクトの transform が target に入る(ヒットが無いときは null)。ヒットした位置ではないことに注意(ヒットした位置は RaycastHit.point で取得できる)。
/// </summary>
public class LaserPointer : MonoBehaviour
{
public bool IsActive = true; //稼働フラグ
public float defaultLength = 0.5f; //ヒットなしのときのレーザーの長さ

public bool shotRay = true; //Ray を撃つ(false のときは target に指す位置を入れる)
public float rayLength = 500f; //Ray の長さ
public LayerMask rayExclusionLayers; //Ray 判定を除外するレイヤー

public Transform target; //指す位置(shotRay=true のときはヒットしたオブジェクトの transform が入る)
public Transform anchor; //発射位置(コントローラの位置)
public LineRenderer lineRenderer; //レーザーを描画するラインレンダラ

// Use this for initialization
void Awake()
{
if (lineRenderer == null)
lineRenderer = GetComponent<LineRenderer>();
}

// Update is called once per frame
void Update()
{
if (!IsActive)
{
lineRenderer.enabled = false;
return;
}

if (shotRay)
{
Ray ray = new Ray(anchor.position, anchor.forward);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, rayLength, ~rayExclusionLayers))
{
target = hit.transform;
DrawTo(hit.point); //ヒットした位置にしたいため
return;
}

target = null;
}

if (target != null)
DrawTo(target.position);
else
DrawTo(anchor.position + anchor.forward * defaultLength); //コントローラの正面方向へ一定の長さ
}

//レーザーを描く
void DrawTo(Vector3 pos)
{
lineRenderer.enabled = true;
lineRenderer.SetPosition(0, anchor.position);
lineRenderer.SetPosition(1, pos);
}
}

 使い方は空の GameObject にこのスクリプトをアタッチし、更に「Line Renderer」を追加する(インスペクタの一番下にある「Add Component で「LineRenderer」を入力すると楽)。Line Renderer を追加したら、インスペクタで線の幅(Width)を 0.005 くらいにしておくと良い。またマテリアル・色はデフォルトでも構わないが、半透明レーザーにしたいなら、アルファの効くマテリアル(例えば「Sprites-Default」など)にし、色の設定でアルファを 200 くらいにしておくと良いだろう。線の太さの変化やグラデーション変化を加えても良い。これらは任意で構わない。ただし「Use World Space」(位置指定を World Space にする)だけは true にしておく必要がある(デフォルトでは true になっている)。

 あとはインスペクタで「Laser Pointer」の「Anchor」にコントローラなどレーザーを発射するオブジェクトをヒエラルキーからドラッグ&ドロップなどでセットするだけだ。


 オブジェクトの判定方法はコントローラから前方に Ray を飛ばして、ヒットしたオブジェクトを取得するだけのもので(target にその transform が入る)、特に何の機能も付いてない。レンスポンスなどは何らかの SDK などを使用することになるだろう。なお、ヒットさせたいオブジェクトにはコライダがアタッチされてる必要がある。

 1つ注意点としては、Line Renderer は厚さのないテープのようなものなので(ただし常にカメラの方を向いている)、カメラ位置とレーザー発射位置(Anchor)が同じで、かつ指す位置(Target)が真正面方向だと Gameビューでは見えないことだ。要するにエディタ上で初期位置がすべて (0, 0, 0) のようになってると見えないので、デバッグ用に表示したいなら、コントローラ等のY軸をずらせば見えるようになる(Sceneビューでは角度を変えれば見える)。実機ではコントローラは普通見下ろす感じになるので、初期位置が(0, 0, 0) でも特に問題はない(手の位置と連動して Transform が変わる)。



■GearVR コントローラでレーザーポインタを使う

 では具体的に Oculus Utilites(掲載時点:v1.16.0-beta) を使って、GearVR コントローラでレーザーポインタを使う方法をやってみよう。Oculus Utilites 自体のインポートからはじめるなら「GearVR ビルドをする」の記事を参照して欲しい。

 サンプルとしてシーン「Assets/OVR/Scenes/GearVrControllerTest」を開いてみよう。複製したものを使っても良い。このサンプルはビルドすればそのまま GearVR コントローラが使えるが、このコントローラからレーザーが出るようにする。設定は前述したインスペクタのようにすれば良い。とりあえず右手のコントローラを「Laser Pointer」の「Anchor」にセットしよう。ヒエラルキーとインスペクタでは以下のような感じになる。


 セットアップが済んだら、あとは「GearVR ビルドをする」と同じように「Build Settings」でこのシーンを追加して apk を出力するだけだ。実機で動作を確認してみよう。
●サンプルにレーザーが追加される
(※) Galaxy S7 Edge で確認



■VRStandardAssets(VRSamples)を使ってレーザーポインタでアイテムをクリックする

 次に何らかの SDK を使ってレーザーポインタでインタラクティブな動作をやってみよう。今回は公式の VRSamples を使うことにする(※VRSamples は完成プロジェクトなので、インポートすると現在のプロジェクトを書き換えてしまうので注意。別の新規プロジェクトにインポートする方が良い)。と言ってもレティクル(視点ポインタ)などのUIや Raycast でのオブジェクトの判定は他の SDK でも同じようにあるハズなので、一部置き換えれば移植は簡単だと思う。

●VRStandardAssets(VRSamples)でレーザーポインタを使う
using UnityEngine;

namespace VRStandardAssets.Utils
{
/// <summary>
/// VRSample の VREyeRaycaster や Reticle(使用するのはUIの位置)を LaserPointer.target に入力する。
/// </summary>
public class VRLaserPointer : MonoBehaviour
{
public LaserPointer laserPointer; //レーザーの描画
public VREyeRaycaster eyeRaycaster; //Rayを発射してオブジェクトを判定する
public Transform guiReticle; //レティクルなどのUI

// Update is called once per frame
void Update()
{
if (eyeRaycaster.CurrentInteractible != null)
laserPointer.target = guiReticle;
else
laserPointer.target = null;
}
}
}

 これは VRStandardAssets(VRSamples) の機能を前述の「LaserPointer」に橋渡しするだけのスクリプトである。VRStandardAssets では「VREyeRaycaster」というスクリプトが Ray を発射し、「VRInteractiveItem」コンポーネントとコライダを持つオブジェクトを取得するという仕組みでインタラクティブ機能を実現している。VRStandardAssets に入っているプレファブの一部を利用して、ここまでのサンプルシーン「GearVrControllerTest」で、レティクル付きのレーザーポインタでオブジェクトをクリックできるように改造してみよう。少し面倒だが、以下の手順でセットアップしたものをプレファブ化しておけば使い回しも簡単にできる。


1.「Assets/VRSampleScenes/Prefabs/Utils/」にある「MainCamera」をヒエラルキーにドラッグ&ドロップしよう。ドロップしたらカメラ自体は元からある「OVRCameraRig」を使うので、カメラとその他必要ないコンポーネントを削除(Remove Component)する。具体的には「Camera」「GUI Layer」「Flare Layer」「Audio Listener」「VR Camera Fade」「Return To Main Menu」「VR Tracking Reset」を削除する。残ったのは「VR Eye Raycaster」「VR Input」「VR Camera UI」「Selection Radial」「Reticle」となるようにする。また「MainCamera」のタグを無くし、ついでに名前を変えておこう。ここでは「VR Eye」としている。あとヒエラルキーで「Fade Panel」も削除しておこう。


2.次に先に作った「LaserPointer」オブジェクトに「VRLaserPointer」をアタッチしよう。そしたらヒエラルキーからドラッグ&ドロップして図のようにセットアップする。「Shot Ray」をオフにするのを忘れずに。これで「VREyeRaycaster」に判定を任せ、ヒットしているときレティクルのUI までレーザーが引かれるようになる(ヒットしてないときは「Default Length」の長さになる)。


3.それとレティクル(視点ポインタUI)もセットアップしておく。ヒエラルキーで「VR Eye>VRCameraUI>GUIReticle」を展開して、「Image」をオンにし、「Color」をレーザーに合うように色を変更しておこう。また今回は使用しないが、必要なら更に展開して UISelectionBar(リングUI)の色も変えておいても良い。ここではリングUI は起動時には非表示にしたいので「VR Eye」に戻り「Selection Radial」の「Hide On Start」をオンにしておく。



※リングUI は起動時に非表示にする


4.「VR Eye」のセットアップができたら、このオブジェクトに先に作った「LaserPointer」オブジェクトをドラッグ&ドロップし、子要素にしよう。そしてこれをプロジェクトエクスプローラでプレファブ化しておく。


5.「VR Eye」のプレファブ化ができたら、ヒエラルキーにあるものとプレファブのものをそれぞれ「RightHandAnchor」「LeftHandAnchor」を展開した先にある「Model」(コントローラの3Dモデル)の子要素に配置しよう。ここに置いておくと、右手用・左手用コントローラを切り替えたとき、自動的にオン・オフされるので便利だ。またプレファブ化した方はヒエラルキー上の参照は外れてしまうので、「Laser Pointer」の「Anchor」だけ設定しておこう。ここでは左手用「LeftHandAnchor」以下にプレファブ化した「VREye」を子要素に置いたので、「Anchor」に「LeftHandAnchor」を設定している。また子要素にすると Transform の Position が再計算されるので、(0, 0, 0) になっていることを確かめよう。ずれてしまった場合は必ず修正する(※動作は正しくても見た目がおかしくなるため)。あと情報表示が邪魔なら「CenterEyeAnchor」以下の「Canvas」を消しても良い。



※Transform は必ず (0, 0, 0) にする


6.これで必要なセットアップは一通り完了したので一旦シーンを保存しよう。またヒエラルキーの「OVRCameraRig」を「OVRCameraRig Gear」とでもしてプレファブ化しておくと再利用が簡単になる。そしてインタラクティブなテストをするために「VRSamples」に入っているサンプルシーン「Assets/VRSampleScenes/Scenes/Examples/InteractiveItem」を開き、ヒエラルキーにある「Cube1」をプレファブ化しよう(3つあるがどれでも同じ)。プレファブ化したらシーンは閉じてしまって良いので(保存はしなくても良い:任意)、再びテストするシーン「Assets/OVR/Scenes/GearVrControllerTest」に戻って、ここに先ほどプレファブ化したインタラクティブなアイテム「Cube1」をいくつか置いておく。


7.最後にシーンを GearVR 用にビルドしてみよう(VRSamples をあとから追加インポートした場合はシーンがいくつか追加されているので全て削除し「GearVrControllerTest」だけビルドする)。レーザーポインタでオブジェクトを指したり、クリックしたりすると色が変化するハズだ(起動初期にコントローラが認識されないときは、Backボタンを短押しすると認識される)。これで簡単な機能だけだがとりあえず完成だ。インタラクティブなアイテムにはスクリプト「VRInteractiveItem」の他に「ExampleInteractiveItem」もアタッチされている。この辺りのソースを覗けば(イベントで色を変えてるだけ)、他のアイテムなどにも応用は可能だろう。

●アイテムにヒットしてないときはデフォルトの長さで、ヒットしてるときはレーザーが伸びていく

(※) Galaxy S7 Edge (Android 7.0) で確認





(関連記事)
【Unity】GearVR アプリをビルドする
【Unity】GearVR アプリを Oculus Store に申請する



この作品・コンテンツは『サファイアートちゃん ライセンス条項』の元に提供されています。
This work is provided under "SapphiArt-Chan License" agreement.
© SapphiArt inc /SCL

関連記事
スポンサーサイト



category: Unity

thread: ゲーム開発

janre: コンピュータ

tag: GearVR  VR 
tb: 0   cm: --


トラックバック

トラックバックURL
→http://fantom1x.blog130.fc2.com/tb.php/259-07646611
この記事にトラックバックする(FC2ブログユーザー)

プロフィール

Social

検索フォーム

全記事一覧

カテゴリ

ユーザータグ

最新記事

リンク

PR

▲ Pagetop