terminalCode Example
NavMeshLink custom traversal — parabolic jump, ladder climb, and physics jump
NavMeshLink Custom Traversal
NavMeshLink connects two NavMesh points for agents to traverse (jumps, ladders, teleports). Default traversal linearly interpolates between start and end. Override OnLinkEntered for custom movement, and always call agent.CompleteLinkTraversal() when done.Disable auto-traversal on the agent first:
CSHARP
agent.AutoTraverseLinks = false;Parabolic Jump
CSHARP
public sealed class JumpLink : NavMeshLink
{
protected override void OnLinkEntered( NavMeshAgent agent )
{
ParabolicJump( agent );
}
private async void ParabolicJump( NavMeshAgent agent )
{
var start = agent.CurrentLinkTraversal.Value.AgentInitialPosition;
var end = agent.CurrentLinkTraversal.Value.LinkExitPosition;
var peakHeight = MathF.Abs( end.z - start.z ) + 25f;
var mid = (start + end) / 2f;
var duration = MathF.Max( 0.75f,
((mid.WithZ(peakHeight) - start) + (end - mid.WithZ(peakHeight))).Length / agent.MaxSpeed );
TimeSince elapsed = 0;
while ( elapsed < duration )
{
var t = elapsed / duration;
var pos = Vector3.Lerp( start, end, t );
pos.z = MathX.Lerp( start.z, end.z, t ) + 4f * peakHeight * t * (1f - t);
agent.SetAgentPosition( pos );
await Task.Frame();
}
agent.SetAgentPosition( end );
agent.CompleteLinkTraversal();
}
}Ladder Climb
CSHARP
public sealed class LadderLink : NavMeshLink
{
protected override void OnLinkEntered( NavMeshAgent agent )
{
ClimbLadder( agent );
}
private async void ClimbLadder( NavMeshAgent agent )
{
var initialPos = agent.CurrentLinkTraversal.Value.AgentInitialPosition;
var start = agent.CurrentLinkTraversal.Value.LinkEnterPosition;
var endVertical = start.WithZ( agent.CurrentLinkTraversal.Value.LinkExitPosition.z );
var end = agent.CurrentLinkTraversal.Value.LinkExitPosition;
const float climbSpeed = 100f;
var t0 = (start - initialPos).Length / climbSpeed;
var t1 = (endVertical - start).Length / climbSpeed;
var t2 = (end - endVertical).Length / climbSpeed;
var total = t0 + t1 + t2;
TimeSince elapsed = 0;
while ( elapsed < total )
{
Vector3 pos;
if ( elapsed < t0 ) pos = Vector3.Lerp( initialPos, start, elapsed / t0 );
else if ( elapsed < t0 + t1 ) pos = Vector3.Lerp( start, endVertical, (elapsed - t0) / t1 );
else pos = Vector3.Lerp( endVertical, end, (elapsed - t0 - t1) / t2 );
agent.SetAgentPosition( pos );
await Task.Frame();
}
agent.SetAgentPosition( end );
agent.CompleteLinkTraversal();
}
}Physics-Based Jump (on the Agent)
Requires Rigidbody and a Collider on the same GameObject as the agent.
CSHARP
public sealed class NavigationLinkTraversal : Component
{
[RequireComponent] NavMeshAgent Agent { get; set; }
[RequireComponent] Rigidbody Body { get; set; }
protected override void OnEnabled()
{
Agent.AutoTraverseLinks = false;
Agent.LinkEnter += PhysicsJump;
}
protected override void OnDisabled() => Agent.LinkEnter -= PhysicsJump;
private async void PhysicsJump()
{
var start = Agent.CurrentLinkTraversal.Value.AgentInitialPosition;
var end = Agent.CurrentLinkTraversal.Value.LinkExitPosition;
Agent.UpdatePosition = false;
Body.Velocity = Agent.MaxSpeed * (end.WithZ(0) - start.WithZ(0)).Normal * 1.25f
+ Vector3.Up * MathF.Max( 500f, (end.z - start.z) * 8f );
TimeSince t = 0;
while ( true )
{
Agent.SetAgentPosition( WorldPosition );
var tr = Scene.Trace
.Ray( WorldPosition + Vector3.Up * 0.1f, WorldPosition + Vector3.Down * 1000 )
.IgnoreGameObjectHierarchy( GameObject )
.Run();
if ( tr.Hit && t > 0.5f && tr.Distance < 4f ) break;
await Task.Frame();
}
Agent.SetAgentPosition( WorldPosition );
Agent.UpdatePosition = true;
Agent.CompleteLinkTraversal();
}
}
Was this helpful?