Files
Cielonos/Assets/Scripts/MainGame/Characters/Automata/AI/Actions/Movement/PrecisePursue.cs
SoulliesOfficial f26f9fd374 爆更
2026-03-20 12:07:44 -04:00

174 lines
6.3 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.
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("指定提前预测目标的距离的程度。")]
[SerializeField] protected SharedVariable<float> m_DistancePrediction = 20;
[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;
m_DistancePrediction = 20;
m_DistancePredictionMultiplier = 2; // 与声明一致
m_SlowDownDistance = 6f;
m_MinSpeed = 0.5f;
m_SpeedCurve = AnimationCurve.EaseInOut(0f, 0f, 1f, 1f);
}
}
}