navigation_using_navigationservers.rst 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. .. _doc_navigation_using_navigationservers:
  2. Using NavigationServer
  3. ======================
  4. 2D and 3D version of the NavigationServer are available as
  5. :ref:`NavigationServer2D<class_NavigationServer2D>` and
  6. :ref:`NavigationServer3D<class_NavigationServer3D>` respectively.
  7. Communicating with the NavigationServer
  8. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  9. To work with the NavigationServer means to prepare parameters for a **query** that can be sent to the NavigationServer for updates or requesting data.
  10. To reference the internal NavigationServer objects like maps, regions and agents RIDs are used as identification numbers.
  11. Every navigation related node in the scene tree has a function that returns the RID for this node.
  12. Threading and Synchronization
  13. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  14. The NavigationServer does not update every change immediately but waits until
  15. the end of the **physics frame** to synchronize all the changes together.
  16. Waiting for synchronization is required to apply changes to all maps, regions and agents.
  17. Synchronization is done because some updates like a recalculation of the entire navigation map are very expensive and require updated data from all other objects.
  18. Also the NavigationServer uses a **threadpool** by default for some functionality like avoidance calculation between agents.
  19. Waiting is not required for most ``get()`` functions that only request data from the NavigationServer without making changes.
  20. Note that not all data will account for changes made in the same frame.
  21. E.g. if an avoidance agent changed the navigation map this frame the ``agent_get_map()`` function will still return the old map before the synchronization.
  22. The exception to this are nodes that store their values internally before sending the update to the NavigationServer.
  23. When a getter on a node is used for a value that was updated in the same frame it will return the already updated value stored on the node.
  24. The NavigationServer is **thread-safe** as it places all API calls that want to make changes in a queue to be executed in the synchronization phase.
  25. Synchronization for the NavigationServer happens in the middle of the physics frame after scene input from scripts and nodes are all done.
  26. .. note::
  27. The important takeaway is that most NavigationServer changes take effect after the next physics frame and not immediately.
  28. This includes all changes made by navigation related nodes in the scene tree or through scripts.
  29. .. note::
  30. All setters and delete functions require synchronization.
  31. 2D and 3D NavigationServer differences
  32. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  33. NavigationServer2D and NavigationServer3D are equivalent in functionality for their dimension.
  34. Technically it is possible to use the tools for creating navigation meshes in one dimension for the other
  35. dimension, e.g. baking a 2D navigation mesh with the 3D NavigationMesh when using
  36. flat 3D source geometry or creating 3D flat navigation meshes with the
  37. polygon outline draw tools of NavigationRegion2D and NavigationPolygons.
  38. Waiting for synchronization
  39. ~~~~~~~~~~~~~~~~~~~~~~~~~~~
  40. At the start of the game, a new scene or procedural navigation changes any path query to a NavigationServer will return empty or wrong.
  41. The navigation map is still empty or not updated at this point.
  42. All nodes from the scene tree need to first upload their navigation related data to the NavigationServer.
  43. Each added or changed map, region or agent need to be registered with the NavigationServer.
  44. Afterward the NavigationServer requires a **physics frame** for synchronization to update the maps, regions and agents.
  45. One workaround is to make a deferred call to a custom setup function (so all nodes are ready).
  46. The setup function makes all the navigation changes, e.g. adding procedural stuff.
  47. Afterwards the function waits for the next physics frame before continuing with path queries.
  48. .. tabs::
  49. .. code-tab:: gdscript GDScript
  50. extends Node3D
  51. func _ready():
  52. # Use call deferred to make sure the entire scene tree nodes are setup
  53. # else await on 'physics_frame' in a _ready() might get stuck.
  54. custom_setup.call_deferred()
  55. func custom_setup():
  56. # Create a new navigation map.
  57. var map: RID = NavigationServer3D.map_create()
  58. NavigationServer3D.map_set_up(map, Vector3.UP)
  59. NavigationServer3D.map_set_active(map, true)
  60. # Create a new navigation region and add it to the map.
  61. var region: RID = NavigationServer3D.region_create()
  62. NavigationServer3D.region_set_transform(region, Transform3D())
  63. NavigationServer3D.region_set_map(region, map)
  64. # Create a procedural navigation mesh for the region.
  65. var new_navigation_mesh: NavigationMesh = NavigationMesh.new()
  66. var vertices: PackedVector3Array = PackedVector3Array([
  67. Vector3(0, 0, 0),
  68. Vector3(9.0, 0, 0),
  69. Vector3(0, 0, 9.0)
  70. ])
  71. new_navigation_mesh.set_vertices(vertices)
  72. var polygon: PackedInt32Array = PackedInt32Array([0, 1, 2])
  73. new_navigation_mesh.add_polygon(polygon)
  74. NavigationServer3D.region_set_navigation_mesh(region, new_navigation_mesh)
  75. # Wait for NavigationServer sync to adapt to made changes.
  76. await get_tree().physics_frame
  77. # Query the path from the navigation server.
  78. var start_position: Vector3 = Vector3(0.1, 0.0, 0.1)
  79. var target_position: Vector3 = Vector3(1.0, 0.0, 1.0)
  80. var optimize_path: bool = true
  81. var path: PackedVector3Array = NavigationServer3D.map_get_path(
  82. map,
  83. start_position,
  84. target_position,
  85. optimize_path
  86. )
  87. print("Found a path!")
  88. print(path)
  89. .. code-tab:: csharp C#
  90. using Godot;
  91. public partial class MyNode3D : Node3D
  92. {
  93. public override void _Ready()
  94. {
  95. // Use call deferred to make sure the entire scene tree nodes are setup
  96. // else await on 'physics_frame' in a _Ready() might get stuck.
  97. CallDeferred(MethodName.CustomSetup);
  98. }
  99. private async void CustomSetup()
  100. {
  101. // Create a new navigation map.
  102. Rid map = NavigationServer3D.MapCreate();
  103. NavigationServer3D.MapSetUp(map, Vector3.Up);
  104. NavigationServer3D.MapSetActive(map, true);
  105. // Create a new navigation region and add it to the map.
  106. Rid region = NavigationServer3D.RegionCreate();
  107. NavigationServer3D.RegionSetTransform(region, Transform3D.Identity);
  108. NavigationServer3D.RegionSetMap(region, map);
  109. // Create a procedural navigation mesh for the region.
  110. var newNavigationMesh = new NavigationMesh()
  111. {
  112. Vertices =
  113. [
  114. new Vector3(0.0f, 0.0f, 0.0f),
  115. new Vector3(9.0f, 0.0f, 0.0f),
  116. new Vector3(0.0f, 0.0f, 9.0f),
  117. ],
  118. };
  119. int[] polygon = [0, 1, 2];
  120. newNavigationMesh.AddPolygon(polygon);
  121. NavigationServer3D.RegionSetNavigationMesh(region, newNavigationMesh);
  122. // Wait for NavigationServer sync to adapt to made changes.
  123. await ToSignal(GetTree(), SceneTree.SignalName.PhysicsFrame);
  124. // Query the path from the navigation server.
  125. var startPosition = new Vector3(0.1f, 0.0f, 0.1f);
  126. var targetPosition = new Vector3(1.0f, 0.0f, 1.0f);
  127. Vector3[] path = NavigationServer3D.MapGetPath(map, startPosition, targetPosition, optimize: true);
  128. GD.Print("Found a path!");
  129. GD.Print((Variant)path);
  130. }
  131. }
  132. Server Avoidance Callbacks
  133. ~~~~~~~~~~~~~~~~~~~~~~~~~~
  134. If RVO avoidance agents are registered for avoidance callbacks the NavigationServer dispatches
  135. their ``velocity_computed`` signals just before the PhysicsServer synchronization.
  136. To learn more about NavigationAgents see :ref:`doc_navigation_using_navigationagents`.
  137. The simplified order of execution for NavigationAgents that use avoidance:
  138. - physics frame starts.
  139. - ``_physics_process(delta)``.
  140. - ``velocity`` property is set on NavigationAgent Node.
  141. - Agent sends velocity and position to NavigationServer.
  142. - NavigationServer waits for synchronization.
  143. - NavigationServer synchronizes and computes avoidance velocities for all registered avoidance agents.
  144. - NavigationServer sends safe velocity vector with signals for each registered avoidance agents.
  145. - Agents receive the signal and move their parent e.g. with ``move_and_slide`` or ``linear_velocity``.
  146. - PhysicsServer synchronizes.
  147. - physics frame ends.
  148. Therefore moving a physicsbody actor in the callback function with the safe velocity is perfectly thread- and physics-safe
  149. as all happens inside the same physics frame before the PhysicsServer commits to changes and does its own calculations.