/// --------------------------------------------- /// Movement Pack for Behavior Designer Pro /// Copyright (c) Opsive. All Rights Reserved. /// https://www.opsive.com /// --------------------------------------------- using Opsive.BehaviorDesigner.AddOns.Shared.Runtime.Pathfinding; namespace Opsive.BehaviorDesigner.AddOns.MovementPack.Runtime.Tasks { using Opsive.BehaviorDesigner.Runtime.Tasks; using Opsive.GraphDesigner.Runtime; using Opsive.GraphDesigner.Runtime.Variables; using UnityEngine; [Opsive.Shared.Utility.Description("Pursues the specified target with precise stopping. Manually controls agent speed based on distance to stop smoothly without overshooting.")] [NodeIcon("079b135d5d495e14abd9ad2cb0dfaf9e", "c0515b66e6314734ba5a8f2ec7d6f451")] public class PrecisePursue : MovementBase // 依然继承自 MovementBase { // --- 原有的 Pursue 变量 --- [Tooltip("The GameObject that the agent is seeking.")] [SerializeField] protected SharedVariable m_Target; [Tooltip("Specifies how far to predict the distance ahead of the target. Lower values indicate that less distance should be predicated.")] [SerializeField] protected SharedVariable m_DistancePrediction = 20; [Tooltip("Specifies the multiplier for predicting the look ahead distance.")] [SerializeField] protected SharedVariable m_DistancePredictionMultiplier = 20; // --- 新增的“方案一”变量 --- [Header("Precise Stopping")] [Tooltip("【新】开始减速的距离 (例如 6m)")] [SerializeField] protected SharedVariable m_SlowDownDistance = 6f; [Tooltip("【新】减速到最慢时的速度 (防止完全停止)")] [SerializeField] protected SharedVariable m_MinSpeed = 0.5f; // --- 私有变量 --- private Vector3 m_TargetPosition; private float m_OriginalMaxSpeed; // 用于存储 Agent 的原始最大速度 private NavMeshAgentPathfinder m_Pathfinder => base.m_Pathfinder as NavMeshAgentPathfinder; /// /// The task has started. /// public override void OnStart() { base.OnStart(); if (m_Target == null) { Debug.LogError("Error: A target must be set on the PrecisePursue ability."); return; } // --- 关键设置 --- // 1. 存储原始最大速度 m_OriginalMaxSpeed = m_Pathfinder.Speed; // 2. 关闭 NavMeshAgent 的自动刹车,因为我们要手动控制 m_Pathfinder.m_NavMeshAgent.autoBraking = false; // --------------- m_TargetPosition = m_Target.Value.transform.position; SetDestination(GetTargetDestination()); } /// /// (原 Pursue 脚本逻辑,保持不变) /// Returns the target destination. /// /// The target destination. private Vector3 GetTargetDestination() { // Calculate the current distance to the target and the current speed. var distance = (m_Target.Value.transform.position - transform.position).magnitude; var velocityMagnitude = m_Pathfinder.Velocity.magnitude; var futurePrediction = 0f; // Set the future prediction to the max prediction if the speed is too small to give an accurate prediction. if (velocityMagnitude <= distance / m_DistancePrediction.Value) { futurePrediction = m_DistancePrediction.Value; } else { futurePrediction = (distance / velocityMagnitude) * m_DistancePredictionMultiplier.Value; } // Predict the future by taking the velocity of the target and multiply it by the future prediction. var lastTargetPosition = m_TargetPosition; m_TargetPosition = m_Target.Value.transform.position; return m_TargetPosition + (m_TargetPosition - lastTargetPosition) * futurePrediction; } /// /// Updates the destination. /// /// Success when the agent arrives. public override TaskStatus OnUpdate() { if (m_Target == null || m_Target.Value == null) { return TaskStatus.Failure; } // --- 核心修改:基于距离的速度控制 --- // 1. 计算【直线距离】(非常重要,不用 NavMesh 的 remainingDistance) float distanceToTarget = Vector3.Distance(transform.position, m_Target.Value.transform.position); // 2. 检查是否已“到达” (即进入了停止范围) if (distanceToTarget <= m_Pathfinder.StoppingDistance) { // 已经到达,强制停止并返回成功 m_Pathfinder.m_NavMeshAgent.velocity = Vector3.zero; // 清除惯性 m_Pathfinder.m_NavMeshAgent.isStopped = true; m_Pathfinder.m_NavMeshAgent.speed = 0f; // 速度设为零 return TaskStatus.Success; } // 3. 检查是否在“减速缓冲区” if (distanceToTarget < m_SlowDownDistance.Value) { // 在缓冲区 (例如 2m 到 6m 之间) // 计算减速因子 (0.0 -> 1.0) // 距离 = 2m 时, factor = 0.0 // 距离 = 6m 时, factor = 1.0 float factor = (distanceToTarget - m_Pathfinder.StoppingDistance) / (m_SlowDownDistance.Value - m_Pathfinder.StoppingDistance); // 使用 Lerp 平滑地插值速度 m_Pathfinder.m_NavMeshAgent.speed = Mathf.Lerp(m_MinSpeed.Value, m_OriginalMaxSpeed, factor); m_Pathfinder.m_NavMeshAgent.isStopped = false; } else { // 在缓冲区外,全速前进 m_Pathfinder.m_NavMeshAgent.speed = m_OriginalMaxSpeed; m_Pathfinder.m_NavMeshAgent.isStopped = false; } // --- 核心修改结束 --- // 4. (原 Pursue 逻辑) 持续更新目标点(因为目标在移动) SetDestination(GetTargetDestination()); return TaskStatus.Running; } /// /// Resets the task values back to their default. /// public override void Reset() { base.Reset(); // 原有变量 m_Target = null; m_DistancePrediction = 20; m_DistancePredictionMultiplier = 20; // 新增变量 m_SlowDownDistance = 6f; m_MinSpeed = 0.5f; } /// /// 任务结束时(无论是成功到达,还是被Parallel Selector打断),彻底重置状态 /// public override void OnEnd() { base.OnEnd(); if (m_Pathfinder != null && m_Pathfinder.m_NavMeshAgent != null) { // 1. 【核心修复】立即物理刹车 // 只有在 Agent 处于激活且在 NavMesh 上时才操作,防止报错 if (m_Pathfinder.m_NavMeshAgent.isOnNavMesh && m_Pathfinder.m_NavMeshAgent.isActiveAndEnabled) { // 强制停止寻路逻辑 m_Pathfinder.m_NavMeshAgent.isStopped = true; // 抹除物理惯性 (这是导致滑步的罪魁祸首) m_Pathfinder.m_NavMeshAgent.velocity = Vector3.zero; // 清空当前的路径数据 (防止下一个任务开始前,Agent试图跑完旧路径) m_Pathfinder.m_NavMeshAgent.ResetPath(); } // 2. 恢复参数设置 (把 Agent 还原成标准状态,供下一个 Task 使用) m_Pathfinder.m_NavMeshAgent.autoBraking = true; m_Pathfinder.m_NavMeshAgent.speed = m_OriginalMaxSpeed; // 注意:不要在这里设为 isStopped = false。 // 下一个 Task (比如 Attack 或 Move) 开始时, // 它们会自动调用 SetDestination 或相关逻辑来启动 Agent。 // 在这里保持 Stopped 是最安全的“待机”状态。 } } } }