using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
#pragma warning disable IDE1006 // Unity-specific lower case public property names
namespace Unity.AI.Navigation
{
/// Component used to create a navigable link between two NavMesh locations.
[ExecuteAlways]
[DefaultExecutionOrder(-101)]
[AddComponentMenu("Navigation/NavMeshLink", 33)]
[HelpURL(HelpUrls.Manual + "NavMeshLink.html")]
public class NavMeshLink : MonoBehaviour
{
[SerializeField]
int m_AgentTypeID;
[SerializeField]
Vector3 m_StartPoint = new Vector3(0.0f, 0.0f, -2.5f);
[SerializeField]
Vector3 m_EndPoint = new Vector3(0.0f, 0.0f, 2.5f);
[SerializeField]
float m_Width;
[SerializeField]
int m_CostModifier = -1;
[SerializeField]
bool m_Bidirectional = true;
[SerializeField]
bool m_AutoUpdatePosition;
[SerializeField]
int m_Area;
/// Gets or sets the type of agent that can use the link.
public int agentTypeID { get { return m_AgentTypeID; } set { m_AgentTypeID = value; UpdateLink(); } }
/// Gets or sets the position at the middle of the link's start edge.
/// The position is relative to the GameObject transform.
public Vector3 startPoint { get { return m_StartPoint; } set { m_StartPoint = value; UpdateLink(); } }
/// Gets or sets the position at the middle of the link's end edge.
/// The position is relative to the GameObject transform.
public Vector3 endPoint { get { return m_EndPoint; } set { m_EndPoint = value; UpdateLink(); } }
/// The width of the segments making up the ends of the link.
/// The segments are created perpendicular to the line from start to end,in the XZ plane of the GameObject.
public float width { get { return m_Width; } set { m_Width = value; UpdateLink(); } }
/// Gets or sets a value that determines the cost of traversing the link.
/// A negative value implies that the traversal cost is obtained based on the area type.
/// A positive or zero value applies immediately, overriding the cost associated with the area type.
public int costModifier { get { return m_CostModifier; } set { m_CostModifier = value; UpdateLink(); } }
/// Gets or sets whether the link can be traversed in both directions.
/// A link that connects to NavMeshes at both ends can always be traversed from the start position to the end position. When this property is set to `true` it allows the agents to traverse the link also in the direction from end to start. When the value is `false` the agents will never move over the link from the end position to the start position.
public bool bidirectional { get { return m_Bidirectional; } set { m_Bidirectional = value; UpdateLink(); } }
/// Gets or sets whether the world positions of the link's edges update whenever
/// the GameObject transform changes at runtime.
public bool autoUpdate { get { return m_AutoUpdatePosition; } set { SetAutoUpdate(value); } }
/// The area type of the link.
public int area { get { return m_Area; } set { m_Area = value; UpdateLink(); } }
NavMeshLinkInstance m_LinkInstance = new NavMeshLinkInstance();
Vector3 m_LastPosition = Vector3.zero;
Quaternion m_LastRotation = Quaternion.identity;
static readonly List s_Tracked = new List();
void OnEnable()
{
AddLink();
if (m_AutoUpdatePosition && m_LinkInstance.valid)
AddTracking(this);
}
void OnDisable()
{
RemoveTracking(this);
m_LinkInstance.Remove();
}
/// Replaces the link with a new one using the current settings.
public void UpdateLink()
{
m_LinkInstance.Remove();
AddLink();
}
static void AddTracking(NavMeshLink link)
{
#if UNITY_EDITOR
if (s_Tracked.Contains(link))
{
Debug.LogError("Link is already tracked: " + link);
return;
}
#endif
if (s_Tracked.Count == 0)
NavMesh.onPreUpdate += UpdateTrackedInstances;
s_Tracked.Add(link);
}
static void RemoveTracking(NavMeshLink link)
{
s_Tracked.Remove(link);
if (s_Tracked.Count == 0)
NavMesh.onPreUpdate -= UpdateTrackedInstances;
}
void SetAutoUpdate(bool value)
{
if (m_AutoUpdatePosition == value)
return;
m_AutoUpdatePosition = value;
if (value)
AddTracking(this);
else
RemoveTracking(this);
}
void AddLink()
{
#if UNITY_EDITOR
if (m_LinkInstance.valid)
{
Debug.LogError("Link is already added: " + this);
return;
}
#endif
var link = new NavMeshLinkData();
link.startPosition = m_StartPoint;
link.endPosition = m_EndPoint;
link.width = m_Width;
link.costModifier = m_CostModifier;
link.bidirectional = m_Bidirectional;
link.area = m_Area;
link.agentTypeID = m_AgentTypeID;
m_LinkInstance = NavMesh.AddLink(link, transform.position, transform.rotation);
if (m_LinkInstance.valid)
m_LinkInstance.owner = this;
m_LastPosition = transform.position;
m_LastRotation = transform.rotation;
}
bool HasTransformChanged()
{
if (m_LastPosition != transform.position)
return true;
if (m_LastRotation != transform.rotation)
return true;
return false;
}
void OnDidApplyAnimationProperties()
{
UpdateLink();
}
static void UpdateTrackedInstances()
{
foreach (var instance in s_Tracked)
{
if (instance.HasTransformChanged())
instance.UpdateLink();
}
}
#if UNITY_EDITOR
void OnValidate()
{
m_Width = Mathf.Max(0.0f, m_Width);
if (!m_LinkInstance.valid)
return;
UpdateLink();
if (!m_AutoUpdatePosition)
{
RemoveTracking(this);
}
else if (!s_Tracked.Contains(this))
{
AddTracking(this);
}
}
#endif
}
}