Files
Cielonos/Assets/Opsive/BehaviorDesigner/Add-Ons/Shared/Runtime/NavMeshAgentPathfinder.cs
SoulliesOfficial ef7b479712 initial
2025-11-25 08:19:33 -05:00

165 lines
6.4 KiB
C#

/// ---------------------------------------------
/// Shared Add-On for Behavior Designer Pro
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.AddOns.Shared.Runtime.Pathfinding
{
using Opsive.GraphDesigner.Runtime.Variables;
using UnityEngine;
using UnityEngine.AI;
/// <summary>
/// Implements the Pathfindiner abstract class for the NavMeshAgent pathfinding implementation.
/// </summary>
public class NavMeshAgentPathfinder : Pathfinder
{
[Tooltip("Should the NavMeshAgent rotation be updated?")]
[SerializeField] protected SharedVariable<bool> m_UpdateRotation = true;
[Tooltip("Sets the minimum amount of time in between destination updates. This allows for throttling the number of path searches.")]
[SerializeField] protected SharedVariable<float> m_DestinationUpdateInterval = 0;
[Tooltip("Specifies the stopping distance of the NavMeshAgent.")]
[SerializeField] protected SharedVariable<float> m_StoppingDistance = 0.2f;
public NavMeshAgent m_NavMeshAgent;
private bool m_StartUpdateRotation;
private float m_SetDestinationTime;
public override Vector3 Velocity { get => m_NavMeshAgent.velocity; }
public override float RemainingDistance { get => m_NavMeshAgent.pathPending ? float.PositiveInfinity : m_NavMeshAgent.remainingDistance; }
public override Vector3 Destination { get => m_NavMeshAgent.destination; }
public override float Speed { get => m_NavMeshAgent.speed; set => m_NavMeshAgent.speed = value; }
public float StoppingDistance { get => m_StoppingDistance.Value; set => m_StoppingDistance.Value = value; }
/// <summary>
/// Initializes the Pathfinder.
/// </summary>
/// <param name="gameObject">The parent GameObject.</param>
public override void Initialize(GameObject gameObject)
{
m_NavMeshAgent = gameObject.GetComponent<NavMeshAgent>();
if (m_NavMeshAgent == null) {
Debug.LogError($"Error: Unable to find the NavMeshAgent component on the {gameObject} GameObject.");
return;
}
if (m_StoppingDistance.Value <= 0) {
Debug.LogWarning("Warning: The NavMeshAgent stopping distance is set to 0. This should be a positive value to ensure the agent is able to arrive at their destination.");
m_StoppingDistance.Value = 0.2f;
}
}
/// <summary>
/// The task has started.
/// </summary>
public override void OnStart()
{
m_NavMeshAgent.isStopped = false;
m_NavMeshAgent.stoppingDistance = m_StoppingDistance.Value;
m_StartUpdateRotation = m_NavMeshAgent.updateRotation;
m_SetDestinationTime = -m_DestinationUpdateInterval.Value;
UpdateRotation(m_UpdateRotation.Value);
}
/// <summary>
/// Specifies if the rotation should be updated.
/// </summary>
/// <param name="update">Should the rotation be updated?</param>
private void UpdateRotation(bool update)
{
m_NavMeshAgent.updateRotation = update;
m_NavMeshAgent.updateUpAxis = update;
}
/// <summary>
/// Set a new pathfinding destination.
/// </summary>
/// <param name="destination">The destination to set.</param>
/// <returns>True if the destination is valid.</returns>
public override bool SetDesination(Vector3 destination)
{
if (!m_NavMeshAgent.isStopped && m_NavMeshAgent.destination == destination) {
return true;
}
// Prevent the destination from being set too often.
if (m_SetDestinationTime + m_DestinationUpdateInterval.Value > Time.time) {
return true;
}
m_NavMeshAgent.isStopped = false;
m_SetDestinationTime = Time.time;
return m_NavMeshAgent.SetDestination(destination);
}
/// <summary>
/// Does the agent have a pathfinding path?
/// </summary>
/// <returns>True if the agent has a pathfinding path.</returns>
public override bool HasPath()
{
return m_NavMeshAgent.hasPath && RemainingDistance > m_NavMeshAgent.stoppingDistance && !m_NavMeshAgent.isStopped;
}
/// <summary>
/// Returns true if the position is a valid pathfinding position.
/// </summary>
/// <param name="position">The position to sample. The position will be updated to the valid sampled position.</param>
/// <returns>True if the position is a valid pathfinding position.</returns>
public override bool SamplePosition(ref Vector3 position)
{
NavMeshHit hit;
if (NavMesh.SamplePosition(position, out hit, m_NavMeshAgent.height * 2, NavMesh.AllAreas)) {
position = hit.position;
return true;
}
return false;
}
/// <summary>
/// Has the agent arrived at the destination?
/// </summary>
/// <returns>True if the agent has arrived at the destination.</returns>
public override bool HasArrived()
{
// The path hasn't been computed yet if the path is pending.
float remainingDistance;
if (m_NavMeshAgent.pathPending) {
remainingDistance = float.PositiveInfinity;
} else {
remainingDistance = m_NavMeshAgent.remainingDistance;
}
return remainingDistance <= m_NavMeshAgent.stoppingDistance;
}
/// <summary>
/// The agent should stop moving.
/// </summary>
public override void Stop()
{
if (m_NavMeshAgent != null && m_NavMeshAgent.isActiveAndEnabled) {
m_NavMeshAgent.isStopped = true;
}
}
/// <summary>
/// The task has stopped.
/// </summary>
public override void OnEnd()
{
// UpdateRotation(m_StartUpdateRotation);
}
/// <summary>
/// Resets the values back to their default.
/// </summary>
public override void Reset()
{
base.Reset();
m_UpdateRotation = true;
m_StoppingDistance = 0.2f;
}
}
}