MRが楽しい

MRやVRについて学習したことを書き残す

Unityでオブジェクトをシーン開始時の相対位置に戻す

本日は Unity の技術調査枠です。
HoloLens でも使える小技として、オブジェクトをシーン開始時の相対位置に戻す方法をまとめます。
f:id:bluebirdofoz:20190227081750j:plain

例えば、シーン開始時に目の前に表示されていた3Dオブジェクトを、任意のタイミングで再び目の前に再表示します。
シーンを見渡すうちに3Dオブジェクトを見失ってしまった場合などに便利です。

プロジェクトとシーンの準備

今回の例では位置を戻すトリガーにダブルタップを用います。
以下の記事を参考に、プロジェクトのシーンを構築します。
bluebirdofoz.hatenablog.com

また今回のシーン構築では UnityEvent による呼び出し関数の指定を利用します。
以下の記事で作成した MultiTapEvent.cs を流用するので参考にしてください。
bluebirdofoz.hatenablog.com

今回はオブジェクトの正面方向のリセットも実装したいと思います。
オブジェクトの方向が分かるように、Sphere ではなく Cube オブジェクトを配置しました。
f:id:bluebirdofoz:20190227081817j:plain

相対位置の取得スクリプト

カメラと Cube オブジェクトの相対位置を取得し、任意のタイミングで位置関係をリセットするスクリプトを作成します。
InverseTransformPoint 関数と InverseTransformDirection 関数を利用します。
特定オブジェクトに対するオブジェクトのローカル座標またはベクトルを取得する関数になります。

以下の機能を持つスクリプトを作成しました。
1.起動時に、基準オブジェクトに対する自身の相対位置・相対ベクトルを取得する
2.処理を呼び出すと、その時点の基準オブジェクトの位置を元に、シーン開始時の相対位置・相対ベクトルを再現する

ResetPosition.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ResetPosition : MonoBehaviour {

    /// <summary>
    /// 基準点となるトランスフォーム
    /// </summary>
    [SerializeField, Tooltip("基準点となるトランスフォーム")]
    private Transform TargetTransform;

    /// <summary>
    /// シーン開始時の相対位置
    /// </summary>
    private Vector3 StartRelativePosition;
    
    /// <summary>
    /// シーン開始時の相対的な正面方向
    /// </summary>
    private Vector3 StartRelativeForward;

    /// <summary>
    /// 起動時処理
    /// </summary>
	void Start () {
        // InverseTransformPoint はワールド空間の座標をローカル空間の座標に変換する
        // 基準点となるトランスフォームからの相対位置を取得しておく
        StartRelativePosition = TargetTransform.InverseTransformPoint(this.transform.position);

        // InverseTransformDirection はワールド空間のベクトルをローカル空間のベクトルに変換する
        // 基準点となるトランスフォームからの相対的な正面方向を取得しておく
        StartRelativeForward = TargetTransform.InverseTransformDirection(this.transform.forward);
    }

    /// <summary>
    /// シーン開始時の相対位置・回転方向に再設定する
    /// </summary>
    public void ResetRelativePosition() {
        // TransformPoint はローカル空間の座標をワールド空間の座標に変換する
        // 基準点となるトランスフォームの現在位置からの相対位置を指定し、移動先のワールド座標を取得する
        Vector3 ResetWorldPosition = TargetTransform.TransformPoint(StartRelativePosition);

        // オブジェクトをシーン起動時の相対位置に座標を移動する
        this.transform.position = ResetWorldPosition;

        // TransformDirection はローカル空間のベクトルをワールド空間のベクトルに変換する
        // 基準点となるトランスフォームの現在方向からの相対的な正面方向を指定し、ワールド座標での正面方向を取得する
        Vector3 ResetWorldForward = TargetTransform.TransformDirection(StartRelativeForward);

        // オブジェクトをシーン起動時の相対的な正面方向になるよう回転する
        this.transform.forward = ResetWorldForward;
    }
}

シーンの構築

作成した ResetPosition.cs とダブルタップの記事で作成した MultiTapEvent.cs を Cube オブジェクトにアタッチします。
f:id:bluebirdofoz:20190227081846j:plain

どこでタップしても初期位置に戻るように、ダブルタップのスクリプトをグローバルリスナーに設定します。
UnityEvent の呼び出し関数には Cube オブジェクト自身を指定し、ResetRelativePosition 関数を呼び出すよう指定します。
f:id:bluebirdofoz:20190227081901j:plain

最後に基準点となるオブジェクトを、ResetPosition コンポーネントの TargetTransform に指定します。
メインカメラの MixedRealityCameraParent/MixedRealityCamera をドラッグして設定します。
f:id:bluebirdofoz:20190227081913j:plain

シーンテスト

これで構築完了です。
[再生]ボタンを押して、試してみます。
f:id:bluebirdofoz:20190227081925j:plain

ゲーム画面を選択して、WSADキーまたはドラッグによる視点変更でカメラを大きく移動しました。
f:id:bluebirdofoz:20190227081940j:plain

この状態で Shift + 左クリック を連続して行い、ダブルタップイベントを発生させると……。
f:id:bluebirdofoz:20190227081951j:plain
Cube オブジェクトがカメラの正面に移動しました。回転方向もきちんとカメラの方へ正面を向いています。