本日はアプリ作成枠です。
HoloLens2でホロモンアプリを作る進捗を書き留めていきます。
今回はホロモンがシャワーの水から逃げるメモです。
ホロモンがシャワーの水から逃げる
ホロモンがシャワーの水から逃げるロジックは以下の記事で紹介したうんちオブジェクトから逃げるロジックを流用します。
bluebirdofoz.hatenablog.com
またシャワーの水は以下の記事でアタリ判定を検知するロジックを実装しています。
この2つを組み合わせてホロモンがシャワーの水から逃げる動作を実装しました。
bluebirdofoz.hatenablog.com
実装コード
以下の通り、逃走用ロジックのクラスを整備しました。
「現在逃走中のオブジェクト」にシャワーの水オブジェクトを指定すると、ホロモンがシャワーの水から135度反対側の方向へ逃げていきます。
・HoloMonModeLogicRunFromTarget.cs
using System.Collections; using System.Collections.Generic; using UnityEngine; using System; using Cysharp.Threading.Tasks; using UniRx; using HoloMonApp.CommonSpace; using HoloMonApp.DataFormatSpace; using HoloMonApp.HeadLogicSpace; using HoloMonApp.ItemHoldSpace; using HoloMonApp.ConditionSpace; using HoloMonApp.FieldOfVisionSpace; namespace HoloMonApp.ModeLogicSpace { public class HoloMonModeLogicRunFromTarget : MonoBehaviour, HoloMonModeLogicInterface { /// <summary> /// モードロジック共通情報 /// </summary> [SerializeField, Tooltip("モードロジック共通情報")] private HoloMonModeLogicCommon p_ModeLogicCommon = new HoloMonModeLogicCommon(); /// <summary> /// 現在の実行待機中フラグ /// </summary> /// <returns></returns> public bool CurrentRunAwaitFlg() { return p_ModeLogicCommon.RunAwaitFlg; } /// <summary> /// モード実行(async/await制御) /// </summary> public async UniTask<ModeLogicResult> RunModeAsync(ModeLogicSetting a_ModeLogicSetting) { // 設定アクションデータを保持する p_ModeLogicCommon.SaveCommonSetting(a_ModeLogicSetting); // 開始処理を行う EnableSetting(); // モードを開始して完了を待機する ModeLogicResult result = await p_ModeLogicCommon.RunModeAsync(); // 終了状態を返却する return result; } /// <summary> /// モードキャンセル /// </summary> public void CancelMode() { // 停止処理を行う DisableSetting(); // キャンセル処理を行う p_ModeLogicCommon.CancelMode(); } /// <summary> /// モード内部停止 /// </summary> private void StopMode(ModeLogicResult a_StopModeLogicResult) { // 停止処理を行う DisableSetting(); // 停止状態を設定する p_ModeLogicCommon.StopMode(a_StopModeLogicResult); } /// <summary> /// 割込み通知 /// </summary> public bool TransmissionInterrupt(InterruptInformation a_InterruptInfo) { bool isProcessed = false; // 処理対象の割込み処理のみ記述 return isProcessed; } /// <summary> /// 固有アクションデータの参照 /// </summary> private ModeLogicRunFromTargetData p_Data => p_ModeLogicCommon.ModeLogicSetting.ModeLogicRunFromTargetData; /// <summary> /// 現在逃走中のオブジェクト /// </summary> [SerializeField, Tooltip("現在逃走中のオブジェクト")] private GameObject p_RunFromObject; /// <summary> /// アイテムの保持位置 /// </summary> [SerializeField, Tooltip("アイテムの保持位置")] private HoloMonItemHolder p_HoloMonItemHolder; [SerializeField, Tooltip("逃走判定の可変ベース距離(m)")] float p_TargetCheckDistanceBase = 0.5f; [SerializeField, Tooltip("逃走対象を確認する時間間隔(秒)")] float p_TargetCheckTimeSpan = 1.0f; float p_TargetCheckTimeElapsed; [SerializeField, Tooltip("逃走対象が見当たらない場合に追跡を解除する上限回数")] int p_TargetNotFoundThreshold = 3; [SerializeField, Tooltip("逃走対象が見当たらなかった回数")] int p_TargetNotFoundCount; /// <summary> /// スタック確認定期処理 /// </summary> private bool p_StackCheckFlg = false; private float p_StackCheckTime = 1.0f; private float p_StackTimeElapsed; /// <summary> /// スタック判定距離 /// </summary> float p_StackDistance = 0.1f; /// <summary> /// スタック判定用ポジション /// </summary> Vector3 p_StackCheckPostion; /// <summary> /// 前回フレームポジション /// </summary> Vector3 p_PrefPosition; /// <summary> /// 開始処理 /// </summary> void Start() { } /// <summary> /// 定期処理 /// </summary> void Update() { // モードが実行中かチェックする if (p_ModeLogicCommon.ModeLogicStatus == HoloMonActionModeStatus.Runtime) { // 一定時間ごとに追跡対象を確認する p_TargetCheckTimeElapsed += Time.deltaTime; if (p_TargetCheckTimeElapsed >= p_TargetCheckTimeSpan) { // 視界内に追跡オブジェクトが見えているかチェックする VisionObjectWrap friendObjectWrap = p_ModeLogicCommon.ReferenceFieldOfVision .CheckCollectionByGameObject(p_RunFromObject); p_TargetCheckTimeElapsed = 0.0f; if (friendObjectWrap == null) { // 追跡対象が見当たらなかった場合はカウントアップ p_TargetNotFoundCount++; if (p_TargetNotFoundCount > p_TargetNotFoundThreshold) { // 指定回数見つからなければ失敗として逃走を解除する Debug.Log("RunFromTarget Missing : Target Not Found"); StopMode(new ModeLogicResult(HoloMonActionModeStatus.Missing, new ModeLogicTrackingTargetReturn())); return; } } else { // 追跡対象が見つかった場合はカウントリセット p_TargetNotFoundCount = 0; } } // 自身の座標 Vector3 selfPostion = p_ModeLogicCommon.ReferenceComponent.GetRigidbody().transform.position; // ターゲットの座標 Vector3 targetPosition = p_RunFromObject.transform.position; // ターゲットまでの距離が停止距離か判定する if (CheckStoppingDistance(targetPosition, selfPostion)) { // ターゲット位置に到達していれば完了とする Debug.Log("RunFrom Achievement"); StopMode(new ModeLogicResult(HoloMonActionModeStatus.Achievement, new ModeLogicTrackingTargetReturn())); return; } // 現在位置と過去位置からスタックを判定する if (CheckImmobility(selfPostion)) { // 到達場所が停止距離以遠の状態でスタックしていれば逃走失敗と判定する Debug.Log("RunFrom Missing"); StopMode(new ModeLogicResult(HoloMonActionModeStatus.Missing, new ModeLogicTrackingTargetReturn())); return; } // 逃走ターゲットの座標を取得する Vector3 runFromTargetPosition = RunTargetPosition(targetPosition, selfPostion); // 逃走ターゲットの足元座標 (Y軸方向の追跡はしないので軸の座標は自身と同じとする) Vector3 runFromTargetFootPosition = new Vector3( runFromTargetPosition.x, selfPostion.y, runFromTargetPosition.z ); // アニメーションパラメータに速度設定を反映する // (実速度では変動が大きいため、設定値をそのまま利用する) ApplyAnimationSpeed(p_Data.RunSetting.MoveSpeed); // 自身の向き Vector3 selfRotateVector = p_ModeLogicCommon.ReferenceComponent.GetRigidbody().transform.forward; // ターゲットへの方向ベクトル Vector3 toTargetVector = (runFromTargetFootPosition - selfPostion).normalized; // フレーム間隔に合わせて回転速度と移動速度を計算する float deltaRotateSpeed = p_Data.RunSetting.RotateSpeed * Time.deltaTime; float deltaMoveSpeed = p_Data.RunSetting.MoveSpeed * Time.deltaTime; // 回転を反映する Vector3 newRotateVector = Vector3.RotateTowards(selfRotateVector, toTargetVector, deltaRotateSpeed, 0f); p_ModeLogicCommon.ReferenceComponent.GetRigidbody().transform.rotation = Quaternion.LookRotation(newRotateVector); // 移動ベクトルを位置に反映する Vector3 nextPosition = Vector3.MoveTowards(selfPostion, runFromTargetFootPosition, deltaMoveSpeed); p_ModeLogicCommon.ReferenceComponent.GetRigidbody().transform.position = nextPosition; // 前回フレーム位置を記録する p_PrefPosition = selfPostion; } } /// <summary> /// 追跡モードの設定を有効化する /// </summary> private bool EnableSetting() { if (p_Data.HoldItemSetting.IsHold) { // アイテム保持設定があればアイテム保持のための設定を行う EnableItemHold(p_Data.HoldItemSetting.HoldItemObject); } // アニメーションを追跡モードにする p_ModeLogicCommon.ReferenceAnimation.StartTrackingMode(); // 頭部追従ロジックを設定する p_ModeLogicCommon.ReferenceLimbControl.Head.ChangeHeadLogic( (p_Data.TargetObject == null) ? new HeadLogicSetting(new HeadLogicNoOverrideData()) : new HeadLogicSetting(new HeadLogicLookAtTargetData(p_Data.TargetObject)) ); // 逃走オブジェクトを設定する p_RunFromObject = p_Data.TargetObject; // スタックチェックを初期化する p_StackCheckFlg = false; p_StackTimeElapsed = 0.0f; p_StackCheckPostion = Vector3.zero; return true; } /// <summary> /// 追跡モードの設定を無効化する /// </summary> private bool DisableSetting() { if (p_Data.HoldItemSetting.IsHold) { // アイテム保持設定があればアイテム解放のための設定を行う DisableItemHold(); } // アニメーションを待機モードにする p_ModeLogicCommon.ReferenceAnimation.ReturnStandbyMode(); // 頭部追従ロジックを解除する p_ModeLogicCommon.ReferenceLimbControl.Head.ChangeHeadLogic( new HeadLogicSetting(new HeadLogicNoOverrideData()) ); // 回転慣性を切る p_ModeLogicCommon.ReferenceComponent.GetRigidbody().angularVelocity = Vector3.zero; // 追跡オブジェクトを解除する p_RunFromObject = null; // スタックチェックを初期化する p_StackCheckFlg = false; p_StackTimeElapsed = 0.0f; p_StackCheckPostion = Vector3.zero; return true; } /// <summary> /// アイテムの保持状態を有効化する /// </summary> /// <param name="a_HoldItemObject"></param> /// <returns></returns> private bool EnableItemHold(GameObject a_HoldItemObject) { // アニメーションのアイテム保持モードを有効化する p_ModeLogicCommon.ReferenceAnimation.ChangeHoldItemOption(true); // アイテムを保持位置に固定する p_HoloMonItemHolder.SetHoldItem(a_HoldItemObject); return true; } /// <summary> /// アイテムの保持状態を無効化する /// </summary> private bool DisableItemHold() { // アニメーションのアイテム保持モードを無効化する p_ModeLogicCommon.ReferenceAnimation.ChangeHoldItemOption(false); // アイテムを保持位置から解放する p_HoloMonItemHolder.ReleaseHoldItem(); return true; } /// <summary> /// 到達距離のチェック /// </summary> private bool CheckStoppingDistance(Vector3 a_TargetPosition, Vector3 a_SelfPostion) { bool isReached = false; // 判定位置の指定に合わせて到達距離のチェックを行う Vector3 arrivalCheckPosition = Vector3.zero; switch (p_Data.RunSetting.RunCheckPosition) { case RunFromTargetCheckPosition.ObjectOrigin: // オリジナルの座標で判定 arrivalCheckPosition = a_TargetPosition; break; case RunFromTargetCheckPosition.FootPosition: // ターゲットの足元座標で判定 (Y軸方向の追跡はしないので軸の座標は自身と同じとする) arrivalCheckPosition = new Vector3( a_TargetPosition.x, a_SelfPostion.y, a_TargetPosition.z); break; default: break; } // ターゲットまでの距離 float toTargetDistance = Vector3.Distance(arrivalCheckPosition, a_SelfPostion); // 現在のホロモンの大きさに合わせて停止距離を調整する float checkDistance = p_Data.RunSetting.StoppingDistance + p_TargetCheckDistanceBase * CurrentHeightScale(); // 停止距離以上に離れていれば逃走を止める if (toTargetDistance > checkDistance) { isReached = true; } return isReached; } /// <summary> /// 移動を継続できているか(スタックしていないか)のチェック /// </summary> private bool CheckImmobility(Vector3 a_CurrentPostion) { bool isImmovility = false; // 一定時間ごとに移動距離を判定する p_StackTimeElapsed += Time.deltaTime; if (p_StackTimeElapsed > p_StackCheckTime) { // チェック座標を保存済みか否か if (p_StackCheckFlg) { // スタック範囲内でしか動けていないのであれば移動していないと判定する float moveDistance = Vector3.Distance(a_CurrentPostion, p_StackCheckPostion); if (moveDistance < p_StackDistance) { isImmovility = true; } } // 前回位置を記録する p_StackCheckFlg = true; p_StackCheckPostion = a_CurrentPostion; p_StackTimeElapsed = 0.0f; } return isImmovility; } /// <summary> /// 逃走先の座標を取得する /// (Y軸を元に135度反対側を逃走先とする) /// </summary> /// <param name="a_TargetPosition"></param> /// <param name="a_SelfPosition"></param> /// <returns></returns> private Vector3 RunTargetPosition(Vector3 a_TargetPosition, Vector3 a_SelfPosition) { // 支点座標と逃走対象座標を設定する Vector3 fulcrumPosition = a_SelfPosition; Vector3 targetPosition = a_TargetPosition; // Y軸に平行な対角ベクトルを求めるため、支点の高さを合わせる Vector3 fulcrumPoint = new Vector3(fulcrumPosition.x, targetPosition.y, fulcrumPosition.z); // 逃げ元へのベクトルを取得する Vector3 targetVector = targetPosition - fulcrumPoint; // 逃げ先への逆ベクトルを取得する Vector3 reverceVector = Quaternion.Euler(0.0f, 135.0f, 0.0f) * targetVector.normalized; // 対角線方向のベクトルを求める Vector3 diagonalVector = (reverceVector + fulcrumPoint); return diagonalVector; } /// <summary> /// 現在の速度をアニメーションパラメータに反映する /// </summary> private void ApplyAnimationSpeed(float a_AgentSpeed) { // 速度をアニメーション調整用にスケール値で補正する // 同じ速度でもスケールが小さければ低い敷居値で走りになる float scaleRatio = CurrentHeightScale(); float scaleAgentSpeed = (a_AgentSpeed / scaleRatio); // 速度をアニメーションに通知する p_ModeLogicCommon.ReferenceAnimation.SetModelSpeed(scaleAgentSpeed); } /// <summary> /// 現在のホロモンの身体スケールを取得する /// </summary> /// <returns></returns> private float CurrentHeightScale() { // 現在の体の大きさを取得する float currentHeight = HoloMonConditionBodySingleton.Instance. IReadOnlyReactivePropertyHoloMonBodyStatus.Value.BodyHeight; return currentHeight; } } }
動作確認
シーンを再生して動作を確認します。
シャワーヘッドを取り出して掴んで水を出してみます。
ホロモンがシャワーの水から逃げるように移動し始めました。