MRが楽しい

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

HoloLens2でホロモンアプリを作る その58(台座の上から物が無くなると台座が自動的に消失する)

本日はアプリ作成枠です。
HoloLens2でホロモンアプリを作る進捗を書き留めていきます。
f:id:bluebirdofoz:20210911061458j:plain

今回は台座の上から物が無くなると台座が自動的に消失する実装を行いました。

Physics.RaycastNonAlloc

指定の方向にレイキャストを飛ばして衝突したオブジェクトの情報を取得します。
今回はこのレイキャストを台座の中心から上方向に一定距離飛ばすことで、台座の上に物があるか判定を行います。
docs.unity3d.com

実装コード

台座用のスクリプトを作成しました。
台座を表示中は常に上に物が置いてあるかチェックを行い、物が無くなると消失モーションを再生してオブジェクトを無効化します。
・StandSpawnController.cs

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

using UniRx;

using HoloMonApp.ConditionSpace;

namespace HoloMonApp.Item
{
    public class StandSpawnController : MonoBehaviour
    {
        /// <summary>
        /// デフォルト座標
        /// </summary>
        [SerializeField, Tooltip("デフォルト座標")]
        private Vector3 p_DefaultLocalPosition;

        /// <summary>
        /// デフォルト回転
        /// </summary>
        [SerializeField, Tooltip("デフォルト回転")]
        private Quaternion p_DefaultLocalRotation;

        /// <summary>
        /// デフォルトサイズ
        /// </summary>
        [SerializeField, Tooltip("デフォルトサイズ")]
        private Vector3 p_DefaultLocalScale;

        [SerializeField, Tooltip("スポーン状態")]
        private bool p_IsSpawn;

        /// <summary>
        /// 消失トリガー
        /// </summary>
        private IDisposable p_DisappearLerpTrigger;

        // Start is called before the first frame update
        void Start()
        {
            // デフォルト座標を保存
            p_DefaultLocalPosition = this.transform.localPosition;

            // デフォルト回転を保存
            p_DefaultLocalRotation = this.transform.localRotation;

            // デフォルトスケールを保存
            p_DefaultLocalScale = this.transform.localScale;

            // 初期状態は表示状態とする
            p_IsSpawn = true;
        }

        // Update is called once per frame
        void Update()
        {
            if(p_IsSpawn)
            {
                // 表示状態のときは上に何か乗っているか常にチェックする
                if (!CheckStandOn())
                {
                    // 上に物がないときはスタンドを消失させる
                    DisappearStand();
                }
            }
        }

        /// <summary>
        /// スタンドをリセットする
        /// </summary>
        public void ResetObject()
        {
            // デフォルトトランスフォームに戻す
            this.transform.localPosition = p_DefaultLocalPosition;
            this.transform.localRotation = p_DefaultLocalRotation;
            this.transform.localScale = p_DefaultLocalScale;

            // オブジェクトを有効化する
            this.gameObject.SetActive(true);

            // 表示状態とする
            p_IsSpawn = true;
        }

        /// <summary>
        /// スタンドを消失させる
        /// </summary>
        public void DisappearStand()
        {
            // スムーズにトランスフォームを移動させる
            // 分割フレーム数
            int lerpTime = 5;

            // 反映前トランスフォーム
            Vector3 beforeScale = this.transform.localScale;

            // 反映後トランスフォーム
            Vector3 afterScale = Vector3.zero;

            // 非表示状態とする
            p_IsSpawn = false;

            // トリガーを設定済みの場合は一旦破棄する
            p_DisappearLerpTrigger?.Dispose();

            // 1フレームごとにトランスフォームを徐々に変化させる
            p_DisappearLerpTrigger = Observable
                .IntervalFrame(1)
                .Take(lerpTime)
                .SubscribeOnMainThread()
                .Subscribe(
                x =>
                {
                    // Lerpを使って徐々にトランスフォームを変化させる
                    float current = (float)(1.0 / lerpTime) * x;
                    Vector3 settingScale = Vector3.Lerp(beforeScale, afterScale, current);

                    this.transform.localScale = settingScale;
                },
                () =>
                {
                    // 完了時はオブジェクトを非表示にしてトランスフォームを戻す
                    this.transform.localScale = beforeScale;

                    // オブジェクトを無効化する
                    this.gameObject.SetActive(false);
                })
                .AddTo(this);
        }

        /// <summary>
        /// スタンドの上に何か乗っているかチェックする
        /// </summary>
        /// <returns></returns>
        private bool CheckStandOn()
        {
            bool onStand = false;

            // レイキャストの結果
            RaycastHit[] raycastHits = new RaycastHit[2];

            // 現在のホロモンの大きさに合わせてチェック距離を調整する
            float checkDistance = 1.0f * CurrentHeightScale();

            // レイキャストでスタンド上方の衝突オブジェクトを検知する
            int hitCount = Physics.RaycastNonAlloc(this.transform.position, Vector3.up, raycastHits, checkDistance);

            if (hitCount > 0)
            {
                // ヒット数が 0 より多いなら障害物があるので何か乗っている
                return true;
            }

            return onStand;
        }

        /// <summary>
        /// 現在のホロモンの身体スケールを取得する
        /// </summary>
        /// <returns></returns>
        private float CurrentHeightScale()
        {
            // 現在の体の大きさを取得する
            float currentHeight = HoloMonConditionBodySingleton.Instance.
                IReadOnlyReactivePropertyHoloMonBodyStatus.Value.BodyHeight;

            return currentHeight;
        }
    }
}

f:id:bluebirdofoz:20210911061521j:plain

作成したスクリプトを台座オブジェクトに設定します。
f:id:bluebirdofoz:20210911061531j:plain

動作確認

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

ホロモンが歩いて台座を降りると、衝突物が無くなったことを検知して自動で台座が消失します。
f:id:bluebirdofoz:20210911061550j:plain

他のアイテムの台座にも同じスクリプトを設定しました。
f:id:bluebirdofoz:20210911061559j:plain

アイテムを掴んで台座からおろすと、こちらも自動的に台座が消失しました。
f:id:bluebirdofoz:20210911061607j:plain