MRが楽しい

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

MRTKのObjectManipulatorで箱を開く動作を行う

本日は MRTK の小ネタ枠です。
MRTKのObjectManipulatorで箱を開く動作を試してみたので記事にします。
f:id:bluebirdofoz:20211117235903j:plain

前提条件

以下の記事で作成した蓋オブジェクトを設定した段ボールモデルを利用します。
bluebirdofoz.hatenablog.com

サンプルプロジェクトの作成

MRTK をインポートしたサンプルプロジェクトを作成します。

MRTK のインポートと基本設定

MRTK のインポートと HoloLens 向けプロジェクトの基本設定を行い、サンプルプロジェクトを作成します。
手順の詳細は以下の記事を参照してください。
bluebirdofoz.hatenablog.com

サンプルシーンの作成

MRTKの基本設定を行ったうえで以下の通り段ボールオブジェクトを配置したシーンを準備しました。
f:id:bluebirdofoz:20211117235616j:plain

Collider でオブジェクトのアタリ判定を設定します。
このとき、段ボールの本体のアタリ判定と箱を開けるためのハンドルのアタリ判定を別々に設定しておきます。
f:id:bluebirdofoz:20211117235626j:plain
f:id:bluebirdofoz:20211117235636j:plain

この状態で本体とハンドルそれぞれに ObjectManipulator を設定します。
f:id:bluebirdofoz:20211117235645j:plain
f:id:bluebirdofoz:20211117235654j:plain

これで本体とハンドルのオブジェクトを別々に掴んで動かすことができます。
f:id:bluebirdofoz:20211117235704j:plain

サンプルスクリプト

後は本体の蓋オブジェクトのみ、ハンドルオブジェクトの移動に合わせて回転動作を行います。
以下の LookAt 関数を用いて特定方向の軸回転を行うスクリプトを作成しました。
・HandleLookAtController.cs

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

public class HandleLookAtController : MonoBehaviour
{
    private enum TrackingAxis
    {
        All,
        XAxis,
        YAxis,
        ZAxis,
    }

    [SerializeField, Tooltip("追跡の有効無効")]
    private bool p_isLookAtTracking;

    [SerializeField, Tooltip("軸の反転フラグ")]
    private bool p_isReverse;

    [SerializeField, Tooltip("LookAt対象のハンドルトランスフォーム")]
    private Transform p_LookAtHandleTransform;

    [SerializeField, Tooltip("ローカル回転軸の指定")]
    private TrackingAxis p_LocalTrackingAxis;

    [SerializeField, Tooltip("ハンドルリセット位置のトランスフォーム")]
    private Transform p_HandleResetTransform;

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        if (p_isLookAtTracking)
        {
            Tracking();
        }
    }

    /// <summary>
    /// 追跡フラグの切り替え
    /// </summary>
    /// <param name="onoff"></param>
    public void ChangeLookAtTracking(bool onoff)
    {
        p_isLookAtTracking = onoff;
    }

    /// <summary>
    /// ハンドルの位置を動きに合わせてリセットする
    /// </summary>
    public void ResetHandlePosition()
    {
        p_LookAtHandleTransform.transform.position = p_HandleResetTransform.position;
        p_LookAtHandleTransform.transform.rotation = p_HandleResetTransform.rotation;
    }

    /// <summary>
    /// 角度の手動変更
    /// </summary>
    /// <param name="a_LocalEulerAngle"></param>
    public void ChangeAngle(Vector3 a_LocalEulerAngle)
    {
        this.transform.localEulerAngles = a_LocalEulerAngle;

        // ハンドル位置をリセット
        ResetHandlePosition();
    }

    /// <summary>
    /// 軸設定に合わせてLookAt方向を追跡する
    /// </summary>
    private void Tracking()
    {
        if (p_LookAtHandleTransform != null)
        {
            this.transform.LookAt(p_LookAtHandleTransform);
            float reverseFact = 1.0f;
            if (p_isReverse) reverseFact = -1.0f;
            switch (p_LocalTrackingAxis)
            {
                case TrackingAxis.All:
                    this.transform.localEulerAngles = new Vector3(
                        reverseFact * this.transform.localEulerAngles.x,
                        reverseFact * this.transform.localEulerAngles.y,
                        reverseFact * this.transform.localEulerAngles.z
                        );
                    break;
                case TrackingAxis.XAxis:
                    this.transform.localEulerAngles = new Vector3(
                        reverseFact * this.transform.localEulerAngles.x,
                        0.0f,
                        0.0f
                        );
                    break;
                case TrackingAxis.YAxis:
                    this.transform.localEulerAngles = new Vector3(
                        0.0f,
                        reverseFact * this.transform.localEulerAngles.y,
                        0.0f
                        );
                    break;
                case TrackingAxis.ZAxis:
                    this.transform.localEulerAngles = new Vector3(
                        0.0f,
                        0.0f,
                        reverseFact * this.transform.localEulerAngles.z
                        );
                    break;
                default:
                    break;
            }
        }
    }
}

蓋オブジェクトに本スクリプトを設定します。
f:id:bluebirdofoz:20211117235720j:plain

p_HandleResetTransform 変数にはハンドルを離した際、ハンドルのアタリ判定を蓋オブジェクトの見た目に合わせるためのリセット位置を指定します。
f:id:bluebirdofoz:20211117235729j:plain

ObjectManipulator の OnManipulationEnded 関数にスクリプトの ResetHandlePosition 関数を指定しておきます。
これにより、ハンドルを手放すと、ハンドルのアタリ判定の位置がリセット位置に移動します。
f:id:bluebirdofoz:20211117235737j:plain

動作確認

シーンを再生して動作を確認します。
f:id:bluebirdofoz:20211117235747j:plain

ハンドル位置を掴んで動かすと、蓋オブジェクトが回転して蓋が開きます。
手を離すと、アタリ判定は蓋オブジェクトの指定位置に戻ります。
f:id:bluebirdofoz:20211117235755j:plain

2021/12/13追記

本記事のスクリプトでは反対方向の回転が考慮されていなかったので反対方向の回転も考慮した改修スクリプトを以下の記事で作成しています。
bluebirdofoz.hatenablog.com