using Opsive.BehaviorDesigner.AddOns.MovementPack.Runtime.Tasks; using Opsive.BehaviorDesigner.AddOns.Shared.Runtime.Pathfinding; using Opsive.BehaviorDesigner.Runtime.Tasks; using Opsive.GraphDesigner.Runtime; using Opsive.GraphDesigner.Runtime.Variables; using Opsive.Shared.Utility; using UnityEngine; using UnityEngine.AI; namespace Cielonos.MainGame.Characters.AI { [Description("以精准停止方式追踪目标。在减速缓冲区内平滑减速,恰好停在目标前方指定距离处。")] [NodeIcon("079b135d5d495e14abd9ad2cb0dfaf9e", "c0515b66e6314734ba5a8f2ec7d6f451")] [Category("Cielonos/Movement")] public class PrecisePursue : MovementBase { #region Pursue Parameters [Tooltip("追踪开始时的初始移动速度,超出减速距离外会保持此速度。")] [SerializeField] protected SharedVariable m_StartSpeed = 10f; [Tooltip("Agent 正在追踪的目标对象。")] [SerializeField] protected SharedVariable m_Target; [Tooltip("指定提前预测目标的距离的程度。")] [SerializeField] protected SharedVariable m_DistancePrediction = 20; [Tooltip("预测提前距离的乘数。")] [SerializeField] protected SharedVariable m_DistancePredictionMultiplier = 1; #endregion #region Precise Stopping Parameters [Header("Precise Stopping")] [Tooltip("开始减速的距离阈值 (单位: 米)")] [SerializeField] protected SharedVariable m_SlowDownDistance = 6f; [Tooltip("减速后的最低速度 (防止完全停滞)")] [SerializeField] protected SharedVariable m_MinSpeed = 0.5f; [Tooltip("减速缓动曲线 (X: 0=停止距离 1=减速距离, Y: 0=最低速度 1=最大速度)")] [SerializeField] protected AnimationCurve m_SpeedCurve = AnimationCurve.EaseInOut(0f, 0f, 1f, 1f); #endregion #region Private State private Vector3 _targetPosition; private float _originalMaxSpeed; private NavMeshAgentPathfinder _navPathfinder; private NavMeshAgent _agent; #endregion public override void OnAwake() { base.OnAwake(); _navPathfinder = m_Pathfinder as NavMeshAgentPathfinder; if (_navPathfinder == null) { Debug.LogError("[PrecisePursue] Requires NavMeshAgentPathfinder."); return; } _agent = _navPathfinder.m_NavMeshAgent; } public override void OnStart() { base.OnStart(); if (m_Target == null || m_Target.Value == null) { Debug.LogError("[PrecisePursue] A target must be set."); return; } if (_agent == null) return; _originalMaxSpeed = _navPathfinder.Speed; _agent.autoBraking = false; _targetPosition = m_Target.Value.transform.position; SetDestination(GetTargetDestination()); } public override TaskStatus OnUpdate() { if (m_Target == null || m_Target.Value == null) return TaskStatus.Failure; if (_agent == null) return TaskStatus.Failure; // 1. 用直线距离(非 NavMesh remainingDistance)做判定 float distanceToTarget = Vector3.Distance( transform.position, m_Target.Value.transform.position ); // 2. 到达判定 if (distanceToTarget <= _navPathfinder.StoppingDistance) { _agent.velocity = Vector3.zero; _agent.isStopped = true; _agent.speed = 0f; return TaskStatus.Success; } // 3. 减速缓冲区控制 float denominator = m_SlowDownDistance.Value - _navPathfinder.StoppingDistance; if (denominator > 0f && distanceToTarget < m_SlowDownDistance.Value) { float t = Mathf.Clamp01( (distanceToTarget - _navPathfinder.StoppingDistance) / denominator ); float easedFactor = m_SpeedCurve.Evaluate(t); _agent.speed = Mathf.Lerp(m_MinSpeed.Value, m_StartSpeed.Value, easedFactor); } else { _agent.speed = m_StartSpeed.Value; } _agent.isStopped = false; // 4. 持续更新预测目标点 SetDestination(GetTargetDestination()); return TaskStatus.Running; } public override void OnEnd() { base.OnEnd(); if (_agent != null && _agent.isOnNavMesh && _agent.isActiveAndEnabled) { _agent.isStopped = true; _agent.velocity = Vector3.zero; _agent.ResetPath(); } if (_navPathfinder != null && _agent != null) { _agent.autoBraking = true; _agent.speed = _originalMaxSpeed; } } private Vector3 GetTargetDestination() { var distance = (m_Target.Value.transform.position - transform.position).magnitude; var velocityMagnitude = m_Pathfinder.Velocity.magnitude; float futurePrediction; if (velocityMagnitude <= distance / m_DistancePrediction.Value) { futurePrediction = m_DistancePrediction.Value; } else { futurePrediction = (distance / velocityMagnitude) * m_DistancePredictionMultiplier.Value; } var lastTargetPosition = _targetPosition; _targetPosition = m_Target.Value.transform.position; return _targetPosition + (_targetPosition - lastTargetPosition) * futurePrediction; } public override void Reset() { base.Reset(); m_StartSpeed = 10f; m_Target = null; m_DistancePrediction = 20; m_DistancePredictionMultiplier = 2; // 与声明一致 m_SlowDownDistance = 6f; m_MinSpeed = 0.5f; m_SpeedCurve = AnimationCurve.EaseInOut(0f, 0f, 1f, 1f); } } }