Custom Link Traversal
Component Agent NavMesh Pathing has property Link Traversal that enables the agent to automatically traverse off-mesh links. By default, it uses its own locomotion to traverse it, but there is a way to create custom traversal of it. The whole agent navmesh link traversal data is contained in NavMeshLinkTraversal structure.
In this tutorial, I will show how to create custom link traversal that does a parabolic jump instead of regular locomotion. This tutorial is recommended for those who are already familiar with navmesh and navmesh links.
Important
Tutorials may have code that does not compile and may be outdated. Their purpose here is to set you in the right direction. If you have questions, do not hesitate to reach the creator in the discord channel.
Game Objects
To get data of currently traversed link you need to access property AgentNavMeshAuthoring.EntityLinkTraversal.
First of all, you will need to disable the default seeking; this can be done by setting Link Traversal Mode in component to Custom.
Next in update we firstly need to check, if agent is currently traversing link.
This can be done by checking Active property in NavMeshLinkTraversal.
void Update()
{
// Skip, if not on active link
if (!m_NavMesh.OnLinkTraversal)
return;
...
Finally, we do the actual parabolic interpolation.
if (!m_Jumping)
{
// Start jumping sequence
m_JumpTimeStamp = Time.realtimeSinceStartupAsDouble;
m_Jumping = true;
}
else
{
float timeInAir = (float)((Time.realtimeSinceStartupAsDouble - m_JumpTimeStamp) / Duration);
if (timeInAir > Duration)
{
// Finish link traversal
m_NavMesh.OnLinkTraversal = false;
m_Jumping = false;
}
else
{
// Do nice parabolic jump
float progress = math.saturate(timeInAir / Duration);
var seek = m_NavMesh.NavMeshLinkTraversal.Seek;
float3 start = seek.Start.Left;
float3 end = seek.End.Left;
transform.position = ParabolicLerp(start, end, progress);
}
}
Here is the full script.
using ProjectDawn.Navigation.Hybrid;
using Unity.Mathematics;
using UnityEngine;
public class AgentJump : MonoBehaviour
{
public float Duration = 1.0f;
AgentNavMeshAuthoring m_NavMesh;
bool m_Jumping;
double m_JumpTimeStamp;
void Start()
{
m_NavMesh = GetComponent<AgentNavMeshAuthoring>();
}
void Update()
{
// Skip, if not on active link
if (!m_NavMesh.OnLinkTraversal)
return;
if (!m_Jumping)
{
// Start jumping sequence
m_JumpTimeStamp = Time.realtimeSinceStartupAsDouble;
m_Jumping = true;
}
else
{
float timeInAir = (float)((Time.realtimeSinceStartupAsDouble - m_JumpTimeStamp) / Duration);
if (timeInAir > Duration)
{
// Finish link traversal
m_NavMesh.OnLinkTraversal = false;
m_Jumping = false;
}
else
{
// Do nice parabolic jump
float progress = math.saturate(timeInAir / Duration);
var seek = m_NavMesh.NavMeshLinkTraversal.Seek;
float3 start = seek.Start.Left;
float3 end = seek.End.Left;
transform.position = ParabolicLerp(start, end, progress);
}
}
}
// Generated by chatgpt
static Vector3 ParabolicLerp(Vector3 pointA, Vector3 pointB, float lerpValue)
{
// Ensure lerpValue is clamped between 0 and 1
lerpValue = Mathf.Clamp01(lerpValue);
// Calculate the control point position
Vector3 controlPoint = CalculateControlPoint(pointA, pointB);
// Perform quadratic Bezier interpolation
return QuadraticBezier(pointA, controlPoint, pointB, lerpValue);
}
// Generated by chatgpt
// Calculate the control point for quadratic Bezier curve
static Vector3 CalculateControlPoint(Vector3 pointA, Vector3 pointB)
{
// Calculate the mid-point between pointA and pointB
Vector3 midPoint = (pointA + pointB) / 2f;
// Adjust the y-coordinate of the mid-point to form a parabolic curve
midPoint.y = Mathf.Max(pointA.y, pointB.y) + Mathf.Abs(pointA.y - pointB.y) * 0.5f;
return midPoint;
}
// Generated by chatgpt
// Perform quadratic Bezier interpolation
static Vector3 QuadraticBezier(Vector3 pointA, Vector3 controlPoint, Vector3 pointB, float t)
{
float u = 1f - t;
float tt = t * t;
float uu = u * u;
Vector3 result = uu * pointA;
result += 2 * u * t * controlPoint;
result += tt * pointB;
return result;
}
}