reckfellENGINE

Navigation agents: movement, states and avoidance

Move a body along a route and distinguish completion from reaching its goal.

On this page

NavigationAgent2D helps its parent Entity select path points and compute avoidance. It does not move the parent or replace a physics body. Use NavigationAgent3D in 3D; the complete executed example below is top-down 2D movement without gravity.

Configure a 2D character

Use a Reckfell 1.0.0 C# project with a prepared navigation map. Create a CharacterBody2D, a CollisionShape2D circle of radius 0.25, and a child NavigationAgent2D named exactly NavigationAgent2D. Attach NavigationWalker2D.cs to the body:

C#
using Reckfell;

public partial class NavigationWalker2D : CharacterBody2D
{
    [Export] public float Speed { get; set; } = 8;
    public NavigationAgent2D Agent { get; private set; } = null!;

    public override void _Ready()
    {
        Agent = GetEntity<NavigationAgent2D>("NavigationAgent2D");
        Agent.PathDesiredDistance = 0.25f;
        Agent.TargetDesiredDistance = 0.25f;
        Agent.Radius = 0.25f;
        Agent.MaxSpeed = Speed;
        Agent.VelocityComputed += ApplyVelocity;
    }

    public void SetDestination(Vector2 target) => Agent.TargetPosition = target;

    public override void _PhysicsProcess(double delta)
    {
        if (NavigationServer2D.MapGetIterationId(Agent.GetNavigationMap()) == 0
            || Agent.IsNavigationFinished())
        {
            Velocity = Vector2.Zero;
            if (Agent.AvoidanceEnabled) Agent.Velocity = Vector2.Zero;
            return;
        }

        Vector2 next = Agent.GetNextPathPosition();
        Vector2 desired = GlobalPosition.DirectionTo(next) * Speed;
        if (Agent.AvoidanceEnabled) Agent.Velocity = desired;
        else ApplyVelocity(desired);
    }

    private void ApplyVelocity(Vector2 velocity)
    {
        Velocity = velocity;
        MoveAndSlide();
    }

    public override void _ExitTree() => Agent.VelocityComputed -= ApplyVelocity;
}

Set a positive Speed in map units per second; the check uses 8. If changing speed at runtime with avoidance, update Agent.MaxSpeed too. Radius 0.25 matches the fixture circle, rather than being a universal pixel-character default.

For a custom map, call walker.Agent.SetNavigationMap(map). After navigation publication, pass a global goal to walker.SetDestination(target). Avoid repeatedly assigning the same target every frame: it resets the route and can cause jitter.

On rectangle x=0–20, y=0–10, the body moved from (2, 5) to (18, 5) and stopped. Velocity supplied to MoveAndSlide() is in units per second: do not multiply it by delta here. MoveAndSlide() performs physics-step integration itself.

Distinguish states

MethodMeaning
GetNextPathPosition()Next point; also updates internal agent state. Obtain it in the physics loop while the route remains unfinished.
IsNavigationFinished()The current route is finished, possibly at a partial-path endpoint.
IsTargetReached()The agent has marked the original goal reached within TargetDesiredDistance.
IsTargetReachable()The found endpoint is close enough to the goal; not a guarantee that the physics body can pass.
GetFinalPosition()Current path endpoint, not necessarily the original goal.

Goal (30, 5) lay outside the checked map. The agent finished at an accessible boundary: IsNavigationFinished() became true while IsTargetReached() stayed false. Handle this outcome separately: stop, choose another goal or report unreachability.

Tune PathDesiredDistance and TargetDesiredDistance for scale, speed and physics step. Too small a tolerance can cause overshooting and backtracking; too large a value can finish early or cut a corner. Values of 0.25 were checked only for this simple geometry.

Avoidance and physics

Without avoidance, the example supplies desired velocity directly to the body. With AvoidanceEnabled = true, it first submits velocity to the agent and moves the body from VelocityComputed. Connecting that signal does not require adding a second low-level callback on top of the agent Entity.

The executed callback produced finite velocities within MaxSpeed. This does not guarantee passing every crowd or narrow passage. Avoidance Radius does not change baked navigation clearance.

A separate physics wall stopped the body despite a straight navigation route through it. Collision-aware movement therefore works, but navigation geometry must still match the real level. Avoidance also does not replace physics collisions or guarantee reaching a goal.

Diagnose problems

  • No movement: check map, publication, layers, child-agent name and target assignment.
  • Unexpectedly slow movement: do not multiply MoveAndSlide velocity by delta again.
  • Goal jitter: stop path following when finished; inspect tolerances and target-reassignment frequency.
  • Stuck against a wall: check navigation geometry and stuck-character handling.

Executed checks cover the exact script, physics movement to a goal, stopping, a partial route, wall blocking and avoidance callback. Crowds, NavigationLink traversal, 3D movement and performance were not tested by this suite.

Reckfell Engine documentationBack to top ↑
QUICK LINKSGetting startedEntity hierarchyC# & .NET 10Scenes and prefabs
Reckfell guides and full API K
reckfellENGINE
RECKFELL ACCOUNT

Sign in

Sign in to your Reckfell account.

One account for the entire Reckfell platform.