Files
Cielonos/Assets/Opsive/BehaviorDesigner/Add-Ons/CielonosPack/Actions/Movement/PrecisePursue.cs

174 lines
6.2 KiB
C#
Raw Normal View History

2026-03-20 12:07:44 -04:00
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<float> m_StartSpeed = 10f;
[Tooltip("Agent 正在追踪的目标对象。")]
[SerializeField] protected SharedVariable<GameObject> m_Target;
[Tooltip("指定提前预测目标的距离的程度。")]
2026-05-11 15:22:30 -04:00
[SerializeField] protected SharedVariable<float> m_DistancePrediction = 10;
2026-03-20 12:07:44 -04:00
[Tooltip("预测提前距离的乘数。")]
[SerializeField] protected SharedVariable<float> m_DistancePredictionMultiplier = 1;
#endregion
#region Precise Stopping Parameters
[Header("Precise Stopping")]
[Tooltip("开始减速的距离阈值 (单位: 米)")]
[SerializeField] protected SharedVariable<float> m_SlowDownDistance = 6f;
[Tooltip("减速后的最低速度 (防止完全停滞)")]
[SerializeField] protected SharedVariable<float> 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;
2026-05-11 15:22:30 -04:00
m_DistancePrediction = 10;
m_DistancePredictionMultiplier = 1;
2026-03-20 12:07:44 -04:00
m_SlowDownDistance = 6f;
m_MinSpeed = 0.5f;
m_SpeedCurve = AnimationCurve.EaseInOut(0f, 0f, 1f, 1f);
}
}
}