Unity + SteamVR SDKチュートリアル Part6

日が開いてしまいましたが、Unity + SteamVRのチュートリアルを進めていきます。
今回は移動できる場所にマーカーを表示し、実際に移動を行うところまで作成します。
今回でこのチュートリアルは終了します。最後まで、どうぞお付き合いをよろしくお願いいたします。

以下のチュートリアルの「Moving Around」のパートになります
https://www.raywenderlich.com/9189-htc-vive-tutorial-for-unity

テレポート処理の作成

移動可能な場所にマーカーを表示できるように改修します。
マーカーはすでに準備されているため、実際に設定を行うときに改めて紹介します。

今回はLaserPointer.csに改修を加えていきます。
全体像は後ほど紹介しますが、大まかに以下の改修を加えていきます。

  • テレポートに使用する各種変数の準備
  • レイヤーマスクの追加
  • マーカーの表示ON/OFF作成
  • 移動処理
  • オフセット設定

やることは多くありますが、確実に設定していきましょう

改修済みソース内容

今回のチュートリアルで作成するLaserPointer.csの完成内容です。
処理内容はそれぞれのコメントを参照してください。

// LaserPointer.cs
using UnityEngine;
using Valve.VR;

public class LaserPointer : MonoBehaviour
{
    public SteamVR_Input_Sources handType;
    public SteamVR_Behaviour_Pose controllerPose;
    public SteamVR_Action_Boolean teleportAction;

    public GameObject laserPrefab;      // レーザーオブジェクトのプレハブを保存

    public Transform cameraRigTransform;        // カメラリグのトランスフォーム
    public GameObject teleportReticlePrefab;    // レーザーポインタのマーカー保存用
    public Transform headTransform;             // プレイヤーカメラへの参照
    public Vector3 teleportReticleOffset;       // マーカー表示位置調整用
    public LayerMask teleportMask;              // テレポート可能範囲のレイヤーマスク
    
    private GameObject laser;           // 作成したレーザーインスタンスの保存
    private Transform laserTransform;   // 
    private Vector3 hitPoint;           // レーザーがヒットしたポジションを保存
    
    private GameObject reticle;                 // マーカーへの参照
    private Transform teleportReticleTransform; // マーカーを利用しやすいように加工したもの
    private bool shoudTeleport;                 // テレポート可能な場合true


    // Start is called before the first frame update
    void Start()
    {
        // プレハブからレーザーインスタンスを新規作成
        laser = Instantiate(laserPrefab);
        // トランスフォーム情報を保存
        laserTransform = laser.transform;

        // テレポート用の変数を初期化
        reticle = Instantiate(teleportReticlePrefab);
        teleportReticleTransform = reticle.transform;
    }

    // Update is called once per frame
    void Update()
    {
        // テレポートアクションが行われた場合
        // (動き的にはテレポートボタンが押されている場合)
        if (teleportAction.GetState(handType))
        {
            RaycastHit hit;

            // コントローラから例を飛ばし、ヒットした場合はhitに内容を保存する
            // teleportMaskを指定することで、テレポート可能な場所を指定する
            if (Physics.Raycast(controllerPose.transform.position, transform.forward, out hit, 100, teleportMask))
            {
                hitPoint = hit.point;
                ShowLaser(hit);

                // テレポート可能エリアにヒットした場合、
                // マーカーをON
                // テレポート位置の設定
                // フラグをtrueに変更
                reticle.SetActive(true);
                teleportReticleTransform.position = hitPoint + teleportReticleOffset;
                shoudTeleport = true;
            }
        }
        else // レイがヒットしていない場合、レーザーオブジェクトとマーカーの表示を消す
        {
            laser.SetActive(false);
            reticle.SetActive(false);
        }

        // テレポートボタンが離された かつ テレポート可能な時
        if (teleportAction.GetStateUp(handType) && shoudTeleport)
        {
            Teleport();
        }
    }

    private void ShowLaser(RaycastHit hit)
    {
        // レーザーを表示
        laser.SetActive(true);
        // コントローラとレイがヒットしたポイントの間にレーザーオブジェクトを配置
        // Leapを利用する理由は、0.5を指定することで、2点間の中間にオブジェクトを配置することができるから
        laserTransform.position = Vector3.Lerp(controllerPose.transform.position, hitPoint, 0.5f);
        // レイがヒットした方向にレーザーオブジェクトを向ける
        laserTransform.LookAt(hitPoint);
        // レイがヒットした位置に合わせて、レーザーオブジェクトの大きさを変える
        laserTransform.localScale = new Vector3(laserTransform.localScale.x,
                                                laserTransform.localScale.y,
                                                hit.distance);
    }

    private void Teleport()
    {
        // ここに来た時点でテレポートは進行中なので、フラグはfalseにしておく
        shoudTeleport = false;
        // マーカーの表示も消す
        reticle.SetActive(false);
        // カメラリグとプレイヤーの頭(メインカメラ)の位置を計算する
        // カメラリグとプレイヤーの頭の位置を計算しないと、移動位置がずれたように感じられてしまう
        // ここはチュートリアル元の図解を見ていただいたほうがわかりやすい
        // https://www.raywenderlich.com/9189-htc-vive-tutorial-for-unity#toc-anchor-009
        Vector3 difference = cameraRigTransform.position - headTransform.position;

        // 計算したy座標を0に。ここでは平面上のずれのみ計算したいので、yの値は08ずれ無し)にしておく
        difference.y = 0;

        // 計算した座標をもとにカメラリグを移動する
        cameraRigTransform.position = hitPoint + difference;
    }
}

Teleportの各種設定

まずはテレポート用のレイヤーマスクが設定されているか確認してください。
(なぜか自分の環境では抜けていた・・・)
追加方法はInspectorのLayerからAddLayer… を選択し、追加します

次に修正したスクリプトに対して、各種設定を施します。
具体的には、以下のGifアニメを参照してください

動作確認

以上でテレポート処理が実装されれました。これでVR空間を自由に動き回ることができるはずです。
動作確認を行いましょう

問題なければVR空間内を自由に移動し、オブジェクトもつかむことができるはずです。
これで、このチュートリアルで取り扱う内容は終了です。お疲れさまでした!

以上

以上でSteamVRのチュートリアルは終了です。SteamVRは2系で入力システムが大幅に変わりましたが、今回のチュートリアルを通して大まかな概要が理解できたのではないでしょうか?
また、「物をつかむ」「移動する」ということができるようになっただけで、VR空間内でいろいろできそうな気がしませんか?

今回のチュートリアルがお役に立てば幸いです。また、オリジナルのチュートリアル作者の方に感謝です!