3
\$\begingroup\$

I've just started using Godot and obviously fail to use NavigationAgent3D correctly. I've added a NavigationRegion and baked a navigation mesh successfully (not too hard, though). The navigation mesh hovers the actual geometry by one cell unit, which is correct according to the Godot documentation.

In my player node I've added a NavigationAgent3D and implemented movement in the _PhysicsProcess method (using C#, because I'm comfortable with it)

public override void _PhysicsProcess(double delta)
{
    if (NavigationAgent.IsNavigationFinished())
    {
        AnimationPlayer.Play("Idle");
        return;
    }
    
    AnimationPlayer.Play("Walk");
    
    var currentPosition = Position;
    var nextPosition = NavigationAgent.GetNextPathPosition();
    LookAt(new Vector3(nextPosition.X, currentPosition.Y, nextPosition.Z));
    Velocity = (nextPosition - currentPosition).Normalized() * Speed;
    
    MoveAndSlide();
}

When implementing movement this way, the player would move to the desired position on the navigation mesh, i.e. hovers the actual ground, which might work in some cases, but not for mine.

Moving the NavigationRegion3D downwards somehow works, but looks weird when the character moves on stairs, plus the docs say

Do not expect the NavigationMesh to perfectly follow the original surfaces. Especially navigation polygons placed over ramps will not keep an equal distance to the ground surface. To align an actor perfectly with the ground use other means like physics.

So I tried to correct the players position, but when I'm doing so, the player won't move at all. It looks as if NavigationAgent.GetNextPathPosition() won't give the next path position, I assume because the last one is still too far away.

All of the tutorials I've watched did not really address the issues I'm facing and neither does the official documentation as far as I can tell. Having the NavigationAgent3D within the player node seems to be the way to go according to Docs: Setup for 3D scene.

What is the correct way to have a NavigationAgent3D with a non-floating 3D character?

\$\endgroup\$
1
  • \$\begingroup\$ Really interesting. I have exactly the same problem. The node moves to the first NavPoint and then stops. Did you find a solution to this in the end? The tutorials I have seen have the object continue to path along the path. \$\endgroup\$
    – Jordan
    Nov 7, 2023 at 21:40

1 Answer 1

1
\$\begingroup\$

Usually, a good place to start is the godot demos. There's a 3D Navigation demo with a character moving along ramps and multiple levels. However, only the Godot 3.x version of that demo uses NavAgents. But I think you can translate that solution to what you're doing. Here's their code:

func _physics_process(delta):
    if _nav_agent.is_navigation_finished():
        return
    var next_position = _nav_agent.get_next_location()
    var offset = next_position - global_position
    global_position = global_position.move_toward(next_position, delta * character_speed)

    # Make the robot look at the direction we're traveling.
    # Clamp y to 0 so the robot only looks left and right, not up/down.
    offset.y = 0
    look_at(global_position + offset, Vector3.UP)

If I C#-ifiy that code, it should look roughly like this:

NavigationAgent _nav_agent;
public override void _PhysicsProcess(double delta)
{
    if (_nav_agent.IsNavigationFinished())
    {
        AnimationPlayer.Play("Idle");
        return;
    }
    AnimationPlayer.Play("Walk");

    var next_position = _nav_agent.GetNextLocation();
    var offset = next_position - GlobalPosition;
    GlobalPosition = GlobalPosition.MoveToward(next_position, delta * character_speed);

    // Make the robot look at the direction we're traveling.
    // Clamp y to 0 so the robot only looks left and right, not up/down.
    offset.y = 0;
    look_at(GlobalPosition + offset, Vector3.UP);
}

Comparing that to your code there are three core differences:

  • Movement is done by modifying the transform
  • Local vs global positions
  • Look is done after movement

Movement

I'd suggest getting it to work without MoveAndSlide first to reduce the scope of your problem. Get NavAgents working first, then start figuring out physics.

Local vs Global

The code in your question uses Position and compares it to the value returned by GetNextPathPosition, but the docs say:

Vector3 get_next_path_position()

Returns the next position in global coordinates that can be moved to...

So that's working with positions with two (potentially) different frames of reference so the results are meaningless. (If the player is under the level and the level is at (100, 0, 0), then the values will all have x values off by 100. Use GlobalPosition instead.

LookAt

LookAt is calculated based on your current position (imagine looking at a sign and then walking past it without changing your head's direction), so you want to do it after you apply the move.

Going Further

If that's not sufficient to get your character feeling grounded, then you should look into raycasts to snap them to the ground or IK (Inverse Kinematics) to position their feet on the ground.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .