MRが楽しい

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

HoloLens2でホロモンアプリを作る その40(モードの基本動作を継承クラスで共通化する)

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

今回はモードの基本動作を継承クラスで共通化します。

モードの共通設定

待機、食事、睡眠、対象追跡など様々なホロモンの動作モードを作成してきました。
これらのモードで共通する以下の要素を基底クラスとして定義しました。

  • モードの開始/停止
  • モードの状態と参照

・HoloMonModeLogicBase.cs

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

using HMProject.HoloMon;

namespace HMProject.HoloMonLogic
{
    public class HoloMonModeLogicBase : MonoBehaviour
    {
        /// <summary>
        /// モードロジック状態
        /// </summary>
        [SerializeField, Tooltip("モードロジック状態")]
        protected HoloMonModeStatusReactiveProperty p_ModeLogicStatus
            = new HoloMonModeStatusReactiveProperty(HoloMonModeStatus.Nothing);

        // EveryValueChanged を使ってフレーム間で変化があった時のみ通知する
        /// <summary>
        /// モードロジック状態の EveryValueChanged オブザーバ保持変数
        /// </summary>
        protected IObservable<HoloMonModeStatus> p_IObservableModeLogicStatusEveryValueChanged;

        /// <summary>
        /// モードロジック状態の EveryValueChanged オブザーバ参照変数
        /// </summary>
        public IObservable<HoloMonModeStatus> IObservableModeLogicStatusEveryValueChanged
            => p_IObservableModeLogicStatusEveryValueChanged
            ?? (p_IObservableModeLogicStatusEveryValueChanged =
            p_ModeLogicStatus.ObserveEveryValueChanged(x => x.Value));


        /// <summary>
        /// モードロジックの状態を取得する
        /// </summary>
        /// <returns></returns>
        public HoloMonModeStatus ModeStatus => p_ModeLogicStatus.Value;


        /// <summary>
        /// アニメーション操作の参照
        /// </summary>
        protected HoloMonAnimationSingleton p_Animation
            => HoloMonAnimationSingleton.Instance;

        /// <summary>
        /// 共通コンポーネントの参照
        /// </summary>
        protected HoloMonComponentsSingleton p_Component
            => HoloMonComponentsSingleton.Instance;

        /// <summary>
        /// モードを有効化する
        /// </summary>
        /// <returns></returns>
        public bool EnableMode()
        {
            bool result = false;

            result = EnableSetting();
            return result;
        }

        /// <summary>
        /// モードを無効化する
        /// </summary>
        /// <returns></returns>
        public bool DisableMode()
        {
            bool result = false;

            result = DisableSetting(HoloMonModeStatus.Stopping);
            return result;
        }


        /// <summary>
        /// モードの各種設定を有効化する
        /// </summary>
        protected virtual bool EnableSetting()
        {
            // 開始モード状態を設定する
            p_ModeLogicStatus.SetValueAndForceNotify(HoloMonModeStatus.Runtime);

            return true;
        }

        /// <summary>
        /// モードの各種設定を無効化する
        /// </summary>
        protected virtual bool DisableSetting(HoloMonModeStatus a_DisableStatus)
        {
            // 終了モード状態を設定する
            p_ModeLogicStatus.SetValueAndForceNotify(a_DisableStatus);

            return true;
        }
    }
}

f:id:bluebirdofoz:20210516221125j:plain

モードの細かな設定を行う EnableSetting, DisableSetting 関数は各モード毎に上書きしたいので virtual 修飾子を追加しています。
docs.microsoft.com

本クラスを継承する睡眠モードのクラスは以下のような記述になります。
・HoloMonModeLogicSleep.cs

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

using HMProject.HoloMon;

namespace HMProject.HoloMonLogic
{
    public class HoloMonModeLogicSleep : HoloMonModeLogicBase
    {
        /// <summary>
        /// モードの設定を有効化する
        /// </summary>
        protected override bool EnableSetting()
        {
            // アニメーションを睡眠モードにする
            p_Animation.StartSleepMode();

            // 開始モード状態を設定する
            base.EnableSetting();

            return true;
        }

        /// <summary>
        /// モードの設定を無効化する
        /// </summary>
        protected override bool DisableSetting(HoloMonModeStatus a_DisableStatus)
        {
            // アニメーションを待機モードにする
            p_Animation.ReturnStandbyMode();

            // 終了モード状態を設定する
            base.DisableSetting(a_DisableStatus);

            return true;
        }
    }
}

f:id:bluebirdofoz:20210516221146j:plain

睡眠モードではアニメーションの変更のみ行うため、EnableSetting, DisableSetting 関数を override 修飾子で作成しています。
また共通の処理を通すため、base 変数を使って基底クラスの関数の呼び出しも行っています。

基底クラスが MonoBehaviour を継承しているので、毎フレーム監視が必要なモードでは Update 関数を利用します。
以下はターゲットの方を向くモードのクラスです。
・HoloMonModeLogicTargetTurn

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

using HMProject.HoloMon;

namespace HMProject.HoloMonLogic
{
    public class HoloMonModeLogicTargetTurn : HoloMonModeLogicBase
    {
        /// <summary>
        /// 追跡オブジェクトのマップ位置
        /// </summary>
        [SerializeField, Tooltip("追跡オブジェクトのマップ位置")]
        Transform p_TargetPoint;

        /// <summary>
        /// 前回記録回転角
        /// </summary>
        Quaternion p_BeforeRotation;


        /// <summary>
        /// 停止確認カウンタ
        /// </summary>
        int StopCheckCount;
        int StopCheckThreshold = 5;


        /// <summary>
        /// 追跡対象を設定する
        /// </summary>
        /// <param name="a_Target"></param>
        /// <returns></returns>
        public bool SetTargetTransform(Transform a_Target)
        {
            bool result = false;

            p_TargetPoint = a_Target;
            result = true;

            return result;
        }


        /// <summary>
        /// 定期処理
        /// </summary>
        void Update()
        {
            // モードが実行中かチェックする
            if (p_ModeLogicStatus.Value == HoloMonModeStatus.Runtime)
            {
                // 現在の回転角を取得する
                Quaternion currentRotation = p_Component.GetRigidbody().transform.rotation;

                // 前回との差分を取得する
                float diffAngle = Quaternion.Angle(p_BeforeRotation, currentRotation);

                // Update間隔の係数を取得する
                float secCoefficient = 1.0f / Time.deltaTime;

                // 1秒あたりの回転速度を取得する
                float secAngle = diffAngle * secCoefficient;

                // 回転速度をアニメーションに通知する
                p_Animation.SetModelRotate(secAngle);

                // 正面を向いているか否か
                bool isFront = p_Component.GetBillboard().IsFront();

                // 回転中か否か
                if (isFront)
                {
                    // 回転していないならカウントアップ
                    StopCheckCount++;

                    // 敷居値回数以上停止していれば停止と判定する
                    if (StopCheckCount > StopCheckThreshold)
                    {
                        // 停止すれば目標を達成したと判定する
                        Debug.Log("Turn Achievement");

                        // 移動していなければ追跡を停止する
                        DisableSetting(HoloMonModeStatus.Achievement);
                    }
                }
                else
                {
                    // 移動していればカウント初期化
                    StopCheckCount = 0;
                }
                
                // 最終的な回転角を記録する
                p_BeforeRotation = p_Component.GetRigidbody().transform.rotation;
            }
        }

        /// <summary>
        /// 回転モードの設定を有効化する
        /// </summary>
        protected override bool EnableSetting()
        {
            // アニメーションを回転モードにする
            p_Animation.StartTurnMode();

            // ターゲットを指定する
            p_Component.GetBillboard().TargetTransform = p_TargetPoint;

            // 回転開始時は方向回転を有効化する
            p_Component.GetBillboard().isActive = true;

            // 現在の回転角を記録する
            p_BeforeRotation = p_Component.GetRigidbody().transform.rotation;

            // 停止チェックカウントを初期化する
            StopCheckCount = 0;

            // 開始モード状態を設定する
            base.EnableSetting();

            return true;
        }

        /// <summary>
        /// 回転モードの設定を無効化する
        /// </summary>
        protected override bool DisableSetting(HoloMonModeStatus a_DisableStatus)
        {
            // アニメーションを待機モードにする
            p_Animation.ReturnStandbyMode();
            p_Animation.SetModelRotate(0.0f);

            // 回転終了時は方向回転を無効化する
            p_Component.GetBillboard().isActive = false;

            // 回転角を初期化する
            p_BeforeRotation = new Quaternion();

            // 停止チェックカウントを初期化する
            StopCheckCount = 0;

            // 終了モード状態を設定する
            base.DisableSetting(a_DisableStatus);

            return true;
        }
    }
}