193 lines
8.4 KiB
C#
193 lines
8.4 KiB
C#
/// ---------------------------------------------
|
||
/// 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<GameObject> 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<float> m_DistancePrediction = 20;
|
||
[Tooltip("Specifies the multiplier for predicting the look ahead distance.")]
|
||
[SerializeField] protected SharedVariable<float> m_DistancePredictionMultiplier = 20;
|
||
|
||
// --- 新增的“方案一”变量 ---
|
||
[Header("Precise Stopping")]
|
||
[Tooltip("【新】开始减速的距离 (例如 6m)")]
|
||
[SerializeField] protected SharedVariable<float> m_SlowDownDistance = 6f;
|
||
[Tooltip("【新】减速到最慢时的速度 (防止完全停止)")]
|
||
[SerializeField] protected SharedVariable<float> m_MinSpeed = 0.5f;
|
||
|
||
// --- 私有变量 ---
|
||
private Vector3 m_TargetPosition;
|
||
private float m_OriginalMaxSpeed; // 用于存储 Agent 的原始最大速度
|
||
private NavMeshAgentPathfinder m_Pathfinder => base.m_Pathfinder as NavMeshAgentPathfinder;
|
||
|
||
/// <summary>
|
||
/// The task has started.
|
||
/// </summary>
|
||
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());
|
||
}
|
||
|
||
/// <summary>
|
||
/// (原 Pursue 脚本逻辑,保持不变)
|
||
/// Returns the target destination.
|
||
/// </summary>
|
||
/// <returns>The target destination.</returns>
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Updates the destination.
|
||
/// </summary>
|
||
/// <returns>Success when the agent arrives.</returns>
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Resets the task values back to their default.
|
||
/// </summary>
|
||
public override void Reset()
|
||
{
|
||
base.Reset();
|
||
|
||
// 原有变量
|
||
m_Target = null;
|
||
m_DistancePrediction = 20;
|
||
m_DistancePredictionMultiplier = 20;
|
||
|
||
// 新增变量
|
||
m_SlowDownDistance = 6f;
|
||
m_MinSpeed = 0.5f;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 任务结束时(无论是成功到达,还是被Parallel Selector打断),彻底重置状态
|
||
/// </summary>
|
||
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 是最安全的“待机”状态。
|
||
}
|
||
}
|
||
}
|
||
} |