MRが楽しい

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

HoloLens2でホロモンアプリを作る その51(現在のアイテムが地面の上にあるかの状態を検出する)

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

今回は現在のアイテムが地面の上にあるかの状態を検出するメモです。

サンプルコード

以下のようなアタッチしたオブジェクトの状態を判定するスクリプトを作成しました。
次の処理を行ってオブジェクトが床の上にあるか判定を行います。
1. 起動時にオブジェクトのコライダーを取得し、オブジェクトの大きさを記録する
2. 常に真下にレイキャストを飛ばし、オブジェクトの大きさより近い距離にアタリ判定があった場合、床の上にあると判定する
3. ただしホールド中(掴み中)のフラグが立っている場合は判定を行わない
・ItemStatusController.cs

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

using HoloMonApp.DataFormatSpace;

namespace HoloMonApp.Item
{
    public class ItemStatusController : MonoBehaviour
    {
        /// <summary>
        /// オブジェクトの特徴参照
        /// </summary>
        [SerializeField, Tooltip("オブジェクトの特徴参照")]
        private ObjectFeatures p_ObjectFeatures;


        /// <summary>
        /// アイテム状態
        /// </summary>
        [SerializeField, Tooltip("アイテム状態")]
        private ObjectStatusItem p_StatusItem;

        /// <summary>
        /// 接地判定距離
        /// </summary>
        [SerializeField, Tooltip("接地判定距離")]
        private float p_FloorDistance;

        /// <summary>
        /// ホールド有無
        /// </summary>
        [SerializeField, Tooltip("ホールド有無")]
        private bool p_IsHold;


        // Start is called before the first frame update
        void Start()
        {
            // 接地判定距離を保存
            CalcFoorDistance();
        }

        // Update is called once per frame
        void Update()
        {
            if (!p_IsHold)
            {
                // ホールド状態でなければ接地状態チェック
                bool onFloor = CheckOnFloor();
                if (onFloor)
                {
                    ChangeStatusItem(ObjectStatusItem.Item_Floor);
                }
                else
                {
                    ChangeStatusItem(ObjectStatusItem.Item_Air);
                }
            }
        }

        /// <summary>
        /// ホロモンによるアイテム保持
        /// </summary>
        public void NoticeHoldByHoloMon(bool onoff)
        {
            if(onoff)
            {
                p_IsHold = true;
                ChangeStatusItem(ObjectStatusItem.Item_HoloMonHold);
            }
            else
            {
                p_IsHold = false;
            }
        }


        /// <summary>
        /// プレイヤーによるアイテム保持
        /// </summary>
        public void NoticeHoldByPlayer(bool onoff)
        {
            if (onoff)
            {
                p_IsHold = true;
                ChangeStatusItem(ObjectStatusItem.Item_PlayerHold);
            }
            else
            {
                p_IsHold = false;
            }
        }

        /// <summary>
        /// 接地距離の取得
        /// </summary>
        private void CalcFoorDistance()
        {
            BoxCollider boxCollider = this.GetComponent<BoxCollider>();

            if (boxCollider != null)
            {
                p_FloorDistance = Mathf.Max(boxCollider.size.x, boxCollider.size.y, boxCollider.size.z) * 1.1f;
            }
            else
            {
                SphereCollider sphereCollider = this.GetComponent<SphereCollider>();

                if (sphereCollider != null)
                {
                    p_FloorDistance = sphereCollider.radius * 1.1f;
                }
                else
                {
                    p_FloorDistance = 0;
                }
            }
        }

        /// <summary>
        /// 接地判定チェック
        /// </summary>
        private bool CheckOnFloor()
        {
            bool onFloor = false;

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

            Vector3 rootPosition = this.gameObject.transform.position;
            float currentCheckDistance = p_FloorDistance * this.transform.lossyScale.y;
            Vector3 checkDirection = Vector3.down;

            // レイキャストでその方向の衝突オブジェクトを検知する
            int hitCount = Physics.RaycastNonAlloc(rootPosition, checkDirection, raycastHits, currentCheckDistance);

            if (hitCount > 0)
            {
                // ヒット数が 0 より多いなら間に障害物(床)があるので接地している
                onFloor = true;
            }

            return onFloor;
        }

        /// <summary>
        /// オブジェクト特徴の変更
        /// </summary>
        private bool ChangeStatusItem(ObjectStatusItem a_StatusItem)
        {
            bool isChanged = false;

            isChanged = p_ObjectFeatures.ChangeStatusItem(a_StatusItem);
            p_StatusItem = a_StatusItem;

            return isChanged;
        }
    }
}

f:id:bluebirdofoz:20210810235519j:plain

スクリプトをアイテムオブジェクトに設定します。
f:id:bluebirdofoz:20210810235531j:plain

アイテムに ObjectManipulator コンポーネントを設定している場合は、掴みイベントにホールド開始/終了の通知イベントを設定します。
f:id:bluebirdofoz:20210810235542j:plain

動作確認

シーンを再生して動作を確認します。
アイテムが地面の上にある場合、床の上のある状態とステータスが判定されています。
f:id:bluebirdofoz:20210810235554j:plain

アイテムを手で掴むとホールド中フラグが立ち、掴み状態と判定されます。
f:id:bluebirdofoz:20210810235605j:plain