MRが楽しい

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

HoloLens2でホロモンアプリを作る その24(ホロモンに任意の方向を注視させる)

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

今回はホロモンに任意の方向を注視させるメモです。

ホロモンに任意の方向を注視させる

ホロモンに任意の方向を中止させるには、以下の記事で紹介した HeadLookController を利用します。
bluebirdofoz.hatenablog.com

注視対象を任意の位置にするため、以下の注視用オブジェクトを作成しました。
f:id:bluebirdofoz:20210402231944j:plain

このオブジェクトは表示と非表示を切り替えられるように、Mesh と Renderer コンポーネントを持つオブジェクトを別にしています。
f:id:bluebirdofoz:20210402231958j:plain

プレイヤーの注視を行うスクリプトを改修し、指定された任意のオブジェクトを注視させるスクリプトを作成しました。
・HoloMonLookLogicTargetTracking.cs

using UnityEngine;
using UniRx;

using HMProject.HoloMon;

namespace HMProject.HoloMonLogic
{
    public class HoloMonLookLogicTargetTracking : MonoBehaviour, HoloMonLogicInterface
    {
        /// <summary>
        /// 担当ホロモン視線ロジック
        /// </summary>
        const HoloMonLook MyHoloMonModeLogic = HoloMonLook.PlayerTracking;

        /// <summary>
        /// 視線ロジックの起動状態
        /// </summary>
        [SerializeField, Tooltip("視線ロジックの起動状態")]
        private BoolReactiveProperty p_LookLogicFlg = new BoolReactiveProperty(false);

        /// <summary>
        /// 視線ロジックの起動状態のReadOnlyReactivePropertyの保持変数
        /// </summary>
        private IReadOnlyReactiveProperty<bool> p_IReadOnlyReactivePropertyLookLogicFlg;

        /// <summary>
        /// 視線ロジックの起動状態のReadOnlyReactivePropertyの参照変数
        /// </summary>
        public IReadOnlyReactiveProperty<bool> IReadOnlyReactivePropertyLookLogicFlg
            => p_IReadOnlyReactivePropertyLookLogicFlg
            ?? (p_IReadOnlyReactivePropertyLookLogicFlg = p_LookLogicFlg.ToSequentialReadOnlyReactiveProperty());

        
        /// <summary>
        /// ホロモン頭部視点の制御コンポーネント
        /// </summary>
        [SerializeField, Tooltip("ホロモン頭部視点の制御コンポーネント")]
        private HeadLookControllerSmooth p_HeadLookController;


        /// <summary>
        /// 追跡オブジェクト位置
        /// </summary>
        [SerializeField, Tooltip("追跡オブジェクト位置")]
        Transform p_TargetPoint;

        
        /// <summary>
        /// HeadLookControllerの各ボーンの最大角デフォルト設定
        /// </summary>
        [SerializeField, Tooltip("HeadLookControllerの各ボーンの最大角デフォルト設定")]
        private float p_DefaultMaxBendingAngles;

        /// <summary>
        /// HeadLookControllerのアニメーションオーバーライドのデフォルト値
        /// </summary>
        [SerializeField, Tooltip("HeadLookControllerのアニメーションオーバーライドのデフォルト値")]
        private bool p_DefaultOverrideAnimation;


        /// <summary>
        /// モードの有効無効を取得する
        /// </summary>
        /// <returns></returns>
        public bool GetModeStatus()
        {
            return p_LookLogicFlg.Value;
        }

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

            result = EnableTrackingStatus();
            return result;
        }

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

            result = DisableTrackingStatus();
            return result;
        }

        /// <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 Start()
        {
        }

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

        }

        /// <summary>
        /// 定期処理(Late)
        /// </summary>
        void LateUpdate()
        {
            if (p_LookLogicFlg.Value)
            {
                // プレイヤーの頭部位置を毎フレーム更新する
                p_HeadLookController.target = p_TargetPoint.position;
            }
        }


        /// <summary>
        /// 視線モードの設定を有効化する
        /// </summary>
        private bool EnableTrackingStatus()
        {
            // 視線の追従を有効化する
            ChangeMaxBendingAngle(true);

            // モードフラグを設定する
            p_LookLogicFlg.SetValueAndForceNotify(true);

            return true;
        }

        /// <summary>
        /// 視線モードの設定を無効化する
        /// </summary>
        private bool DisableTrackingStatus()
        {
            // 視線の追従を無効化する
            ChangeMaxBendingAngle(false);

            // モードフラグを落とす
            p_LookLogicFlg.SetValueAndForceNotify(false);

            return true;
        }

        /// <summary>
        /// 有効無効に合わせてHeadLookControllerの最大角を変更する
        /// </summary>
        /// <param name="onoff"></param>
        private void ChangeMaxBendingAngle(bool onoff)
        {
            // 有効無効に合わせてHeadLookControllerの最大角を変更する
            int segmentnum = p_HeadLookController.segments.Length;
            if (segmentnum > 0)
            {
                for (int loop = 0; loop < segmentnum; loop++)
                {
                    float setValue = 0;
                    if (onoff)
                    {
                        setValue = p_DefaultMaxBendingAngles;
                    }
                    p_HeadLookController.segments[loop].maxBendingAngle = setValue;
                }
            }
            // 有効無効に合わせてOverrideAnimationの設定を変更する
            if (onoff)
            {
                p_HeadLookController.overrideAnimation = p_DefaultOverrideAnimation;
            }
            else
            {
                p_HeadLookController.overrideAnimation = false;
            }
        }
    }
}

更に、注視オブジェクトには以下のようなオブジェクトの有効化と無効化、表示と非表示を切り替えるスクリプトを設定しました。
・ItemAttentionController

using UnityEngine;
using UnityEngine.Events;
using System;

namespace HMProject.Item
{
    [Serializable]
    public enum AttentionMode
    {
        Disable = 0,
        Show = 1,
        Hide = 2,
    }
    
    public class ItemAttentionController : MonoBehaviour
    {
        /// <summary>
        /// 開始位置
        /// </summary>
        [SerializeField, Tooltip("開始位置")]
        private Transform p_StartPosition;

        /// <summary>
        /// デフォルト座標
        /// </summary>
        [SerializeField, Tooltip("デフォルト座標")]
        private Vector3 p_DefaultWorldPosition;

        /// <summary>
        /// デフォルトスケール
        /// </summary>
        [SerializeField, Tooltip("デフォルトスケール")]
        private Vector3 p_DefaultLocalScale;

        /// <summary>
        /// 注視モード
        /// </summary>
        [SerializeField, Tooltip("注視モード")]
        private AttentionMode p_AttentionMode;

        /// <summary>
        /// 注視オブジェクトの参照
        /// </summary>
        [SerializeField, Tooltip("注視オブジェクトの参照")]
        private GameObject p_AttentionObject;

        /// <summary>
        /// モデルオブジェクトの参照
        /// </summary>
        [SerializeField, Tooltip("モデルオブジェクトの参照")]
        private GameObject p_ModelObject;


        /// <summary>
        /// Disable切り替え時の実行イベント
        /// </summary>
        [SerializeField, Tooltip("Disable切り替え時の実行イベント")]
        private UnityEvent p_DisableEvent;

        /// <summary>
        /// Show切り替え時の実行イベント
        /// </summary>
        [SerializeField, Tooltip("Show切り替え時の実行イベント")]
        private UnityEvent p_ShowEvent;

        /// <summary>
        /// Hide切り替え時の実行イベント
        /// </summary>
        [SerializeField, Tooltip("Hide切り替え時の実行イベント")]
        private UnityEvent p_HideEvent;


        /// <summary>
        /// 開始処理
        /// </summary>
        void Start()
        {
            // デフォルト座標を保存する
            p_DefaultWorldPosition = this.transform.position;

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

            // 注視/モデルオブジェクトを無効化する
            p_AttentionObject.SetActive(false);
            p_ModelObject.SetActive(false);

            // 状態を設定する
            p_AttentionMode = AttentionMode.Disable;
        }

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

        }

        /// <summary>
        /// オブジェクトリセット
        /// </summary>
        public void ResetObject()
        {
            // デフォルト位置に戻す
            this.transform.position = p_DefaultWorldPosition;

            // デフォルトスケールに戻す
            this.transform.localScale = p_DefaultLocalScale;
        }

        /// <summary>
        /// オブジェクト開始
        /// </summary>
        public void StartObject()
        {
            // 開始位置にオブジェクトを移動する
            this.transform.position = p_StartPosition.position + (p_StartPosition.up * 0.1f);

            // デフォルトスケールに戻す
            this.transform.localScale = p_DefaultLocalScale;
        }

        /// <summary>
        /// モードをスイッチする
        /// </summary>
        public void SwitchMode()
        {
            // 現在のモードに合わせて次のモードに切り替える
            switch(p_AttentionMode)
            {
                case AttentionMode.Disable:
                    // Disableの場合は次はShowに切り替える
                    SwitchShowMode();
                    break;
                case AttentionMode.Show:
                    // Showの場合は次はHideに切り替える
                    SwitchHideMode();
                    break;
                case AttentionMode.Hide:
                    // Hideの場合は次はDisableに切り替える
                    SwitchDisableMode();
                    break;
                default:
                    break;
            }
        }

        /// <summary>
        /// アクティブ状態をスイッチする
        /// </summary>
        public void SwitchActive()
        {
            // 現在のモードに合わせて次のモードに切り替える
            switch (p_AttentionMode)
            {
                case AttentionMode.Disable:
                    // Disableの場合は次はShowに切り替える
                    SwitchShowMode();
                    break;
                case AttentionMode.Show:
                case AttentionMode.Hide:
                    // ShowまたはHideの場合は次はDisableに切り替える
                    SwitchDisableMode();
                    break;
                default:
                    break;
            }

            // オブジェクトを開始位置にリセットする
            StartObject();
        }

        /// <summary>
        /// 表示状態をスイッチする
        /// </summary>
        public void SwitchVisible()
        {
            // 現在のモードに合わせて次のモードに切り替える
            switch (p_AttentionMode)
            {
                case AttentionMode.Disable:
                case AttentionMode.Hide:
                    // DisableまたはHideの場合は次はShowに切り替える
                    SwitchShowMode();
                    break;
                case AttentionMode.Show:
                    // Showの場合は次はHideに切り替える
                    SwitchHideMode();
                    break;
                default:
                    break;
            }
        }

        /// <summary>
        /// Disableモードに切り替える
        /// </summary>
        private void SwitchDisableMode()
        {
            // 注視/モデルオブジェクトを無効化する
            p_AttentionObject.SetActive(false);
            p_ModelObject.SetActive(false);

            // イベントを実行する
            p_DisableEvent.Invoke();

            // 状態を設定する
            p_AttentionMode = AttentionMode.Disable;
        }

        /// <summary>
        /// Showモードに切り替える
        /// </summary>
        private void SwitchShowMode()
        {

            // 注視/モデルオブジェクトを有効化する
            p_AttentionObject.SetActive(true);
            p_ModelObject.SetActive(true);

            // イベントを実行する
            p_ShowEvent.Invoke();

            // 状態を設定する
            p_AttentionMode = AttentionMode.Show;
        }

        /// <summary>
        /// Hideモードに切り替える
        /// </summary>
        private void SwitchHideMode()
        {
            // 注視オブジェクトを有効化し、
            // モデルオブジェクトを無効化する
            p_AttentionObject.SetActive(true);
            p_ModelObject.SetActive(false);

            // イベントを実行する
            p_HideEvent.Invoke();

            // 状態を設定する
            p_AttentionMode = AttentionMode.Hide;
        }
    }
}

シーンを再生して動作を確認します。
[Attention]ボタンをクリックすると注視オブジェクトが有効化されます。
f:id:bluebirdofoz:20210402232017j:plain

注視オブジェクトが有効化されると、ホロモンがそちらを向きました。
f:id:bluebirdofoz:20210402232031j:plain

また、非表示にすることでホロモンが何もない虚空を見つめるような形になります。
ホロモンが起こす色々なアクションに活用できそうです。
f:id:bluebirdofoz:20210402232044j:plain