MRが楽しい

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

HoloLens2でホロモンアプリを作る その72(ホロモンのうんちを水で流す)

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

今回はホロモンのうんちを水で流すメモです。

前提条件

以下の記事でシャワーの水流を利用します。
bluebirdofoz.hatenablog.com

シャワーの水流にアタリ判定を設定する手順については以下の記事で解説しています。
bluebirdofoz.hatenablog.com

現在衝突中のオブジェクトを検知して通知する

以下のようなうんちオブジェクト側で衝突を検知する共通スクリプトを作成しました。
・HitObjectFeatureNotice.cs

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

// イベント通知のため
using UnityEngine.Events;

// コライダー処理のため
using UniRx;
using UniRx.Triggers;

// ToList使用のため
using System.Linq;

// CoreSystemへのアクセスのため
using Microsoft.MixedReality.Toolkit;
// 空間認識情報の取得のため
using Microsoft.MixedReality.Toolkit.SpatialAwareness;

using HoloMonApp.DataFormatSpace;

namespace HoloMonApp.ItemSpace
{
    public class HitObjectFeatureNotice : MonoBehaviour
    {
        [Serializable]
        private class HitObjectOfFeaturesNoticeEvent : UnityEvent<GameObject>{ }


        [SerializeField, Tooltip("空間認識レイヤー")]
        private int p_SpatialAwarenessLayer;

        /// <summary>
        /// 識別情報あり接触オブジェクトリスト
        /// </summary>
        private Dictionary<int, GameObject> p_HitObjectOfFeaturesDictionary
            = new Dictionary<int, GameObject>();

        [SerializeField, Tooltip("識別情報あり接触オブジェクトリスト(Editor確認用)")]
        private List<GameObject> p_HitObjectOfFeaturesList;

        [SerializeField, Tooltip("ヒットした識別情報ありオブジェクトの通知")]
        private HitObjectOfFeaturesNoticeEvent p_HitAddObjectOfFeaturesNoticeEvent;


        // Start is called before the first frame update
        void Start()
        {
            // 空間認識のオブザーバを取得する
            IMixedRealitySpatialAwarenessMeshObserver SpatialAwarenessMeshObserver =
                CoreServices.GetSpatialAwarenessSystemDataProvider<IMixedRealitySpatialAwarenessMeshObserver>();

            // オブザーバからレイヤー番号を取得する
            p_SpatialAwarenessLayer = SpatialAwarenessMeshObserver.MeshPhysicsLayer;

            // コライダーの OnTriggerStay イベントに対する処理を定義する
            this.OnTriggerStayAsObservable()                    // OnTriggerStayイベント
                .BatchFrame()                                   // フレーム毎に値をまとめる
                .ObserveOnMainThread()                          // メインスレッドで行う
                .Subscribe(colliderlist =>
                {
                    // 現在衝突中のコライダーをチェックする
                    bool isUpdated = CheckColliderObjects(colliderlist);
                    // エディター時はリストを更新する
                    if (isUpdated) UpdateList();
                })
                .AddTo(this);
        }

        // Update is called once per frame
        void Update()
        {

        }

        private void UpdateList()
        {
# if UNITY_EDITOR
            // Editor確認用
            if (EditorApplication.isPlaying)
            {
                p_HitObjectOfFeaturesList = p_HitObjectOfFeaturesDictionary.Values.ToList();
            }
# endif
        }

        /// <summary>
        /// 衝突中のコライダーをチェックする
        /// </summary>
        private bool CheckColliderObjects(IList<Collider> a_ColliderList)
        {
            // 検出したオブジェクトのリスト
            List<GameObject> objectList = new List<GameObject>();

            // 衝突オブジェクトの登録を行う
            bool isRegisted = false;
            foreach (Collider collider in a_ColliderList)
            {
                GameObject checkObject = collider?.gameObject;
                if (checkObject == null) continue;

                // チェック対象か否か
                if (CheckHitObject(checkObject))
                {
                    // 検出オブジェクトをコレクションに登録する
                    if (RegistHitObject(checkObject))
                    {
                        // 登録したオブジェクトを記録する
                        objectList.Add(checkObject);
                        // ヒットを通知する
                        p_HitAddObjectOfFeaturesNoticeEvent.Invoke(checkObject);
                        isRegisted = true;
                    }
                }
            }

            // 未検出オブジェクトをコレクションから除去する
            bool isRemove = RemoveHitObject(objectList);

            return isRegisted || isRemove;
        }

        /// <summary>
        /// ゲームオブジェクトが触れているか判定する
        /// </summary>
        private bool CheckHitObject(GameObject a_TouchObject)
        {
            // オブジェクトのレイヤー番号を取得する
            int layernumber = a_TouchObject.layer;

            if (layernumber == p_SpatialAwarenessLayer)
            {
                // 空間認識レイヤーの場合は無視する
                return false;
            }

            // 触れていると判定する
            return true;
        }


        /// <summary>
        /// 未登録のゲームオブジェクトをコレクションに登録する
        /// </summary>
        private bool RegistHitObject(GameObject a_RegistObject)
        {
            // オブジェクトの種別理解情報を取得する
            ObjectFeatures registObjectFeatures = a_RegistObject.GetComponent<ObjectFeatures>();

            // 種別理解情報が付与されているか否か
            if (registObjectFeatures == null) return false;

            // 登録済みか否か
            if (!p_HitObjectOfFeaturesDictionary.ContainsKey(a_RegistObject.GetInstanceID()))
            {
                // コレクションに追加する
                p_HitObjectOfFeaturesDictionary.Add(a_RegistObject.GetInstanceID(), a_RegistObject);
            }

            // 登録対象だった場合は登録済みの場合でも true を返す
            return true;
        }

        /// <summary>
        /// 未検出のゲームオブジェクトをコレクションから除去する
        /// </summary>
        private bool RemoveHitObject(List<GameObject> a_CheckObjectList)
        {
            // 削除を行ったか否か
            bool isRemoved = false;

            // オブジェクトInstanceIDのリストに全て変換する
            List<int> checkIdList = a_CheckObjectList.ConvertAll(obj => obj.GetInstanceID());

            foreach (int key in p_HitObjectOfFeaturesDictionary.Keys.ToList())
            {
                if (!checkIdList.Contains(key))
                {
                    // 検出されていなければコレクションから削除する
                    p_HitObjectOfFeaturesDictionary.Remove(key);
                    isRemoved = true;
                }
            }

            return isRemoved;
        }
    }
}

f:id:bluebirdofoz:20211115232016j:plain

UnityEvent から衝突したイベントを以下のうんち管理スクリプトに通知し、衝突したオブジェクトが水流だった場合、オブジェクトを消去します。
・ItemShitController.cs

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

using UniRx;

using HoloMonApp.ConditionSpace;
using HoloMonApp.DataFormatSpace;

namespace HoloMonApp.ItemSpace
{
    public class ItemShitController : MonoBehaviour
    {
        /// <summary>
        /// モデルコントローラ
        /// </summary>
        [SerializeField, Tooltip("モデルコントローラ")]
        private ItemCommonController p_ModelController;

        /// <summary>
        /// 現在のスケール変化比率
        /// </summary>
        [SerializeField, Tooltip("現在のスケール変化比率")]
        private float p_ScaleRatio;


        /// <summary>
        /// 開始処理
        /// </summary>
        void Start()
        {
            // モデルを初期化する
            p_ModelController.Initialize();

            // モデルを非表示にする
            p_ModelController.HideItem();

            // デフォルトの変化比率は 1 とする
            p_ScaleRatio = 1.0f;
        }

        /// <summary>
        /// 定期処理
        /// </summary>
        void Update()
        {

        }

        /// <summary>
        /// オブジェクトリセット
        /// </summary>
        public void ResetObject()
        {
            // モデルをリセットする
            p_ModelController.ResetObject();

            // モデルを非表示にする
            p_ModelController.HideItem();
        }

        /// <summary>
        /// オブジェクトをスポーンする
        /// </summary>
        /// <param name="a_Position"></param>
        public void SpawnObject(Vector3 a_Position)
        {
            // 現在のホロモンサイズ変化比率を取得する
            float holomonRatio = HoloMonConditionBodySingleton.Instance.GetHeightRatio();

            // 現在のホロモンサイズ変化比率に合わせて大きさを調節する
            ApplySizeCondition(holomonRatio);

            // 現在のホロモンサイズに合わせて位置を指定する
            p_ModelController.SetPosition(a_Position);

            // モデルを表示する
            p_ModelController.ShowItem();
        }

        /// <summary>
        /// 他オブジェクトが衝突したときの処理
        /// </summary>
        /// <param name="a_GameObject"></param>
        public void HitObjectAction(GameObject a_GameObject)
        {
            // オブジェクトの種別理解情報を取得する
            ObjectFeatures registObjectFeatures = a_GameObject.GetComponent<ObjectFeatures>();

            // 種別理解情報が付与されているか否か
            if (registObjectFeatures == null) return;

            switch(registObjectFeatures.Features.ObjectUnderstandType)
            {
                case ObjectUnderstandType.ShowerWater:
                    // シャワー水流が当たった場合はうんちを消失させる
                    p_ModelController.DisappearItem(1.0f);
                    break;
                default:
                    break;
            }
        }

        /// <summary>
        /// 現在のホロモンの身長に合わせて大きさを反映する
        /// </summary>
        /// <param name="a_ScaleRatio"></param>
        private void ApplySizeCondition(float a_ScaleRatio)
        {
            // 現在のホロモンサイズに合わせた大きさを算出して設定する
            p_ModelController.SetScaleRatio(a_ScaleRatio);

            // 設定中のサイズ比率を記録する
            p_ScaleRatio = a_ScaleRatio;
        }
    }
}

f:id:bluebirdofoz:20211115232032j:plain

動作確認

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

ホロモンがうんちをした後、シャワーヘッドを取り出します。
f:id:bluebirdofoz:20211115232055j:plain

水流を出して、手の位置や角度を調整してうんちに当てると……。
f:id:bluebirdofoz:20211115232107j:plain

うんちがどんどん小さくなり消えてなくなりました。
f:id:bluebirdofoz:20211115232117j:plain