Przeglądaj źródła

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.
Paul Joannon 2 lat temu
rodzic
commit
8a8abde036

+ 88 - 26
tutorials/navigation/navigation_introduction_2d.rst

@@ -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::
-
-    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.
+   .. note::
 
-4.) Add a CharacterBody2D below the region node with a basic collision shape and a sprite or mesh for visuals.
+        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.
 
-5.) Add a NavigationAgent2D node below the character node
+#. Add a CharacterBody2D below the region node with a basic collision shape and a sprite or mesh
+   for visuals.
 
-.. image:: img/nav_2d_min_setup_step3.png
+#. Add a NavigationAgent2D node below the character node.
 
-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::
+   .. image:: img/nav_2d_min_setup_step3.png
 
-    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.

+ 59 - 18
tutorials/navigation/navigation_introduction_3d.rst

@@ -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>`.