Files
Cielonos/Assets/Opsive/BehaviorDesigner/Add-Ons/MovementPack/Scripts/Tasks/PrecisePursue.cs
SoulliesOfficial f7af60351b 阶段性完成
2025-12-08 05:27:53 -05:00

193 lines
8.4 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/// ---------------------------------------------
/// 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 是最安全的“待机”状态。
}
}
}
}