Update pages for 2D/3D Navigation Overview

- Add C# examples.
- Fix the list formatting.
- Fix punctuation.
- Reference 2D explanation of the await in the 3D page.
- Simplify comments in the 2D GDScript example.
This commit is contained in:
Paul Joannon
2023-01-20 14:15:05 +01:00
parent e8131bfd56
commit 8a8abde036
2 changed files with 147 additions and 44 deletions

View File

@@ -3,7 +3,7 @@
2D Navigation Overview
======================
Godot provides multiple objects, classes and servers to facilitate grid-based or mesh-based navigation and pathfinding for 2D and 3D games.
Godot provides multiple objects, classes and servers to facilitate grid-based or mesh-based navigation and pathfinding for 2D and 3D games.
The following section provides a quick overview over all available navigation related objects in Godot for 2D scenes and their primary use.
Godot provides the following objects and classes for 2D navigation:
@@ -67,33 +67,31 @@ Setup for 2D scene
The following steps show the basic setup for a minimum viable navigation in 2D that uses the
NavigationServer2D and a NavigationAgent2D for path movement.
1.) Add a NavigationRegion2D Node to the scene.
#. Add a NavigationRegion2D Node to the scene.
2.) Click on the region node and add a new NavigationPolygon Resource to the region node
#. Click on the region node and add a new NavigationPolygon Resource to the region node.
.. image:: img/nav_2d_min_setup_step1.png
.. image:: img/nav_2d_min_setup_step1.png
3.) Define the moveable navigation area with the NavigationPolygon draw tool
#. Define the moveable navigation area with the NavigationPolygon draw tool.
.. image:: img/nav_2d_min_setup_step2.png
.. image:: img/nav_2d_min_setup_step2.png
.. note::
.. note::
The navigation mesh defines the area where an actor can stand and move with its center.
Leave enough margin between the navpolygon edges and collision objects to not get path following actors repeatedly stuck on collision.
The navigation mesh defines the area where an actor can stand and move with its center.
Leave enough margin between the navpolygon edges and collision objects to not get path
following actors repeatedly stuck on collision.
4.) Add a CharacterBody2D below the region node with a basic collision shape and a sprite or mesh for visuals.
#. Add a CharacterBody2D below the region node with a basic collision shape and a sprite or mesh
for visuals.
5.) Add a NavigationAgent2D node below the character node
#. Add a NavigationAgent2D node below the character node.
.. image:: img/nav_2d_min_setup_step3.png
.. image:: img/nav_2d_min_setup_step3.png
6.) Add the following script to the CharacterBody2D node. Set a movement target with the set_movement_target() function after the scene has fully loaded and the NavigationServer had time to sync.
.. note::
On the first frame the NavigationServer map has not synchronised region data and any path query will return empty.
Use ``await get_tree().physics_frame`` to pause scripts until the NavigationServer had time to sync.
#. Add the following script to the CharacterBody2D node. We make sure to set a movement target
after the scene has fully loaded and the NavigationServer had time to sync.
.. tabs::
.. code-tab:: gdscript GDScript
@@ -106,28 +104,25 @@ NavigationServer2D and a NavigationAgent2D for path movement.
@onready var navigation_agent : NavigationAgent2D = $NavigationAgent2D
func _ready():
# these values need to be adjusted for the actor's speed
# and the navpolygon layout as each crossed edge will create a path point
# If the actor moves to fast it might overshoot
# multiple path points in one frame and start to backtrack
# These values need to be adjusted for the actor's speed
# and the navigation layout.
navigation_agent.path_desired_distance = 4.0
navigation_agent.target_desired_distance = 4.0
# make a deferred function call to assure the entire Scenetree is loaded
# Make sure to not await during _ready.
call_deferred("actor_setup")
func actor_setup():
# wait for the first physics frame so the NavigationServer can sync
# Wait for the first physics frame so the NavigationServer can sync.
await get_tree().physics_frame
# now that the navigation map is no longer empty set the movement target
# Now that the navigation map is no longer empty, set the movement target.
set_movement_target(movement_target_position)
func set_movement_target(movement_target : Vector2):
navigation_agent.set_target_location(movement_target)
func _physics_process(delta):
if navigation_agent.is_target_reached():
return
@@ -141,3 +136,70 @@ NavigationServer2D and a NavigationAgent2D for path movement.
set_velocity(new_velocity)
move_and_slide()
.. code-tab:: csharp C#
using Godot;
public partial class MyCharacterBody2D : CharacterBody2D
{
private NavigationAgent2D _navigationAgent;
private float _movementSpeed = 200.0f;
private Vector2 _movementTargetPosition = new Vector2(70.0f, 226.0f);
public Vector2 MovementTarget
{
get { return _navigationAgent.TargetLocation; }
set { _navigationAgent.TargetLocation = value; }
}
public override void _Ready()
{
base._Ready();
_navigationAgent = GetNode<NavigationAgent2D>("NavigationAgent2D");
// These values need to be adjusted for the actor's speed
// and the navigation layout.
_navigationAgent.PathDesiredDistance = 4.0f;
_navigationAgent.TargetDesiredDistance = 4.0f;
// Make sure to not await during _Ready.
Callable.From(ActorSetup).CallDeferred();
}
public override void _PhysicsProcess(double delta)
{
base._PhysicsProcess(delta);
if (_navigationAgent.IsTargetReached())
{
return;
}
Vector2 currentAgentPosition = GlobalTransform.origin;
Vector2 nextPathPosition = _navigationAgent.GetNextLocation();
Vector2 newVelocity = (nextPathPosition - currentAgentPosition).Normalized();
newVelocity *= _movementSpeed;
Velocity = newVelocity;
MoveAndSlide();
}
private async void ActorSetup()
{
// Wait for the first physics frame so the NavigationServer can sync.
await ToSignal(GetTree(), SceneTree.SignalName.PhysicsFrame);
// Now that the navigation map is no longer empty, set the movement target.
MovementTarget = _movementTargetPosition;
}
}
.. note::
On the first frame the NavigationServer map has not synchronised region data and any path query
will return empty. Await one frame to pause scripts until the NavigationServer had time to sync.

View File

@@ -72,31 +72,34 @@ Setup for 3D scene
The following steps show how to setup a minimum viable navigation in 3D that uses the NavigationServer3D and
a NavigationAgent3D for path movement.
1.) Add a NavigationRegion3D Node to the scene.
#. Add a NavigationRegion3D Node to the scene.
2.) Click on the region node and add a new :ref:`NavigationMesh<class_NavigationMesh>` Resource to the region node
#. Click on the region node and add a new :ref:`NavigationMesh<class_NavigationMesh>` Resource to
the region node.
.. image:: img/nav_3d_min_setup_step1.png
.. image:: img/nav_3d_min_setup_step1.png
3.) Add a new MeshInstance3D node as a child of the region node
#. Add a new MeshInstance3D node as a child of the region node.
4.) Select the MeshInstance3D node and add a new PlaneMesh and increase the xy size to 10.
#. Select the MeshInstance3D node and add a new PlaneMesh and increase the xy size to 10.
5.) Select the region node again and press the "Bake Navmesh" button on the top bar
#. Select the region node again and press the "Bake Navmesh" button on the top bar.
.. image:: img/nav_3d_min_setup_step2.png
.. image:: img/nav_3d_min_setup_step2.png
7.) Now a transparent navigation mesh appeared that hovers some distance on top the planemesh.
#. Now a transparent navigation mesh appeared that hovers some distance on top the planemesh.
.. image:: img/nav_3d_min_setup_step3.png
.. image:: img/nav_3d_min_setup_step3.png
8.) Add a CharacterBody3D below the region node with a basic collision shape and some mesh for visuals.
#. Add a CharacterBody3D below the region node with a basic collision shape and some mesh for visuals.
9.) Add a NavigationAgent3D node below the character node
#. Add a NavigationAgent3D node below the character node.
.. image:: img/nav_3d_min_setup_step4.png
.. image:: img/nav_3d_min_setup_step4.png
10.) Add a script to the CharacterBody3D node with the following content.
#. Add a script to the CharacterBody3D node with the following content. Set a movement target after
the scene has fully loaded and the NavigationServer had time to sync. Also, add a Camera3D and some
light and environment to see something.
.. tabs::
.. code-tab:: gdscript GDScript
@@ -122,10 +125,48 @@ a NavigationAgent3D for path movement.
set_velocity(new_velocity)
move_and_slide()
Set a movement target with the set_movement_target() function after the scene has fully loaded.
Also add a Camera3D and some light and environment to see something.
.. code-tab:: csharp C#
.. warning::
using Godot;
On the first frame the NavigationServer map has not synchronised region data and any path query will return empty.
Use ``await get_tree().physics_frame`` to pause scripts until the NavigationServer had time to sync.
public partial class MyCharacterBody3D : CharacterBody3D
{
private float _movementSpeed = 0.3f;
private NavigationAgent3D _navigationAgent;
public Vector3 MovementTarget
{
get { _navigationAgent.TargetLocation; }
set { _navigationAgent.TargetLocation = value; }
}
public override void _Ready()
{
base._Ready();
_navigationAgent = GetNode<NavigationAgent3D>("NavigationAgent3D");
}
public override void _PhysicsProcess(double delta)
{
base._PhysicsProcess(delta);
Vector3 currentAgentPosition = GlobalTransform.origin;
Vector3 nextPathPosition = _navigationAgent.GetNextLocation();
Vector3 newVelocity = (nextPathPosition - currentAgentPosition).Normalized();
newVelocity *= _movementSpeed;
Velocity = newVelocity;
MoveAndSlide();
}
}
.. note::
On the first frame the NavigationServer map has not synchronised region data and any path query
will return empty. Await one frame to pause scripts until the NavigationServer had time to sync.
You can find more details about this in the :ref:`2D Navigation Overview page <doc_navigation_overview_2d>`.