navigation_using_navigationservers.rst 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  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. Both 2D and 3D use the same NavigationServer with NavigationServer3D being the primary server. The NavigationServer2D is a frontend that converts 2D positions into 3D positions and back.
  8. Hence it is entirely possible (if not a little cumbersome) to exclusively use the NavigationServer3D API for 2D navigation.
  9. Communicating with the NavigationServer
  10. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  11. To work with the NavigationServer means to prepare parameters for a ``query`` that can be send to the NavigationServer for updates or requesting data.
  12. To reference the internal NavigationServer objects like maps, regions and agents RIDs are used as identification numbers.
  13. Every navigation related node in the SceneTree has a function that returns the RID for this node.
  14. Threading and Synchronization
  15. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  16. The NavigationServer does not update every change immediately but waits until
  17. the end of the ``physics_frame`` to synchronize all the changes together.
  18. Waiting for synchronization is required to apply changes to all maps, regions and agents.
  19. 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.
  20. Also the NavigationServer uses a ``threadpool`` by default for some functionality like avoidance calculation between agents.
  21. Waiting is not required for most ``get()`` functions that only request data from the NavigationServer without making changes.
  22. Note that not all data will account for changes made in the same frame.
  23. 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.
  24. The exception to this are nodes that store their values internally before sending the update to the NavigationServer.
  25. 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.
  26. 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.
  27. Synchronization for the NavigationServer happens in the middle of the physics frame after scene input from scripts and nodes are all done.
  28. .. note::
  29. The important takeaway is that most NavigationServer changes take effect after the next physics frame and not immediately.
  30. This includes all changes made by navigation related nodes in the SceneTree or through scripts.
  31. The following functions will be executed in the synchronization phase only:
  32. - map_set_active()
  33. - map_set_up()
  34. - map_set_cell_size()
  35. - map_set_edge_connection_margin()
  36. - region_set_map()
  37. - region_set_transform()
  38. - region_set_enter_cost()
  39. - region_set_travel_cost()
  40. - region_set_navigation_layers()
  41. - region_set_navigation_mesh()
  42. - agent_set_map()
  43. - agent_set_neighbor_dist()
  44. - agent_set_max_neighbors()
  45. - agent_set_time_horizon()
  46. - agent_set_radius()
  47. - agent_set_max_speed()
  48. - agent_set_velocity()
  49. - agent_set_target_velocity()
  50. - agent_set_position()
  51. - agent_set_ignore_y()
  52. - agent_set_callback()
  53. - free()
  54. 2D and 3D NavigationServer differences
  55. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  56. NavigationServer2D and NavigationServer3D are equivalent in functionality
  57. for their dimension and both use the same NavigationServer behind the scene.
  58. Strictly technical a NavigationServer2D is a myth.
  59. The NavigationServer2D is a frontend to facilitate conversions of Vector2(x, y) to
  60. Vector3(x, 0.0, z) and back for the NavigationServer3D API. 2D uses a flat 3D mesh
  61. pathfinding and the NavigationServer2D facilitates the conversions.
  62. When a guide uses just NavigationServer without the 2D or 3D suffix it usually works for both servers
  63. by exchange Vector2(x, y) with Vector3(x, 0.0, z) or reverse.
  64. Technically it is possible to use the tools for creating navigationmesh for the other
  65. dimension, e..g. baking 2D navigationmesh with the 3D NavigationMesh when using
  66. flat 3D source geometry or creating 3D flat navigationmesh with the
  67. polygon outline drawtools of NavigationRegion2D and NavigationPolygons.
  68. Any RID created with the NavigationServer2D API works on the NavigationServer3D API
  69. as well and both 2D and 3D avoidance agents can exist on the same map.
  70. .. note::
  71. Regions created in 2D and 3D will merge their navigationmeshes when placed on the same map and merge conditions apply.
  72. The NavigationServer does not discriminate between NavigationRegion2D and NavigationRegion3D nodes as both are regions on the server.
  73. By default those nodes register on different navigation maps so this merge can only happen when maps are changed manually e.g. with scripts.
  74. Actors with avoidance enabled will avoid both 2D and 3D avoidance agents when placed on the same map.
  75. .. warning::
  76. It is not possible to use NavigationServer2D while disabling 3D on a Godot custom build.
  77. Waiting for synchronization
  78. ~~~~~~~~~~~~~~~~~~~~~~~~~~~
  79. At the start of the game, a new scene or procedural navigation changes any path query to a NavigationServer will return empty or wrong.
  80. The navigation map is still empty or not updated at this point.
  81. All nodes from the SceneTree need to first upload their navigation related data to the NavigationServer.
  82. Each added or changed map, region or agent need to be registered with the NavigationServer.
  83. Afterward the NavigationServer requires a ``physics_frame`` for synchronization to update the maps, regions and agents.
  84. One workaround is to make a deferred call to a custom setup function (so all nodes are ready).
  85. The setup function makes all the navigation changes, e.g. adding procedural stuff.
  86. Afterwards the function waits for the next physics_frame before continuing with path queries.
  87. .. tabs::
  88. .. code-tab:: gdscript GDScript
  89. extends Node3D
  90. func _ready():
  91. # use call deferred to make sure the entire SceneTree Nodes are setup
  92. # else await / yield on 'physics_frame' in a _ready() might get stuck
  93. call_deferred("custom_setup")
  94. func custom_setup():
  95. # create a new navigation map
  96. var map: RID = NavigationServer3D.map_create()
  97. NavigationServer3D.map_set_up(map, Vector3.UP)
  98. NavigationServer3D.map_set_active(map, true)
  99. # create a new navigation region and add it to the map
  100. var region: RID = NavigationServer3D.region_create()
  101. NavigationServer3D.region_set_transform(region, Transform())
  102. NavigationServer3D.region_set_map(region, map)
  103. # create a procedural navigation mesh for the region
  104. var new_navigation_mesh: NavigationMesh = NavigationMesh.new()
  105. var vertices: PackedVector3Array = PoolVector3Array([
  106. Vector3(0,0,0),
  107. Vector3(9.0,0,0),
  108. Vector3(0,0,9.0)
  109. ])
  110. new_navigation_mesh.set_vertices(vertices)
  111. var polygon: PackedInt32Array = PackedInt32Array([0, 1, 2])
  112. new_navigation_mesh.add_polygon(polygon)
  113. NavigationServer3D.region_set_navigation_mesh(region, new_navigation_mesh)
  114. # wait for NavigationServer sync to adapt to made changes
  115. await get_tree().physics_frame
  116. # query the path from the navigationserver
  117. var start_position: Vector3 = Vector3(0.1, 0.0, 0.1)
  118. var target_position: Vector3 = Vector3(1.0, 0.0, 1.0)
  119. var optimize_path: bool = true
  120. var path: PackedVector3Array = NavigationServer3D.map_get_path(
  121. map,
  122. start_position,
  123. target_position,
  124. optimize_path
  125. )
  126. print("Found a path!")
  127. print(path)
  128. Server Avoidance Callbacks
  129. ~~~~~~~~~~~~~~~~~~~~~~~~~~
  130. If RVO avoidance agents are registered for avoidance callbacks the NavigationServer dispatches
  131. their ``safe_velocity`` signals just before the PhysicsServer synchronization.
  132. To learn more about NavigationAgents see :ref:`doc_navigation_using_navigationagents`.
  133. To learn more about RVO Avoidance see :ref:`doc_navigation_using_agent_avoidance`.
  134. The simplified order of execution for NavigationAgents that use avoidance:
  135. - physics frame starts.
  136. - _physics_process(delta).
  137. - set_velocity() on NavigationAgent Node.
  138. - Agent sends velocity and position to NavigationServer.
  139. - NavigationServer waits for synchronization.
  140. - NavigationServer synchronizes and computes avoidance velocities for all registered avoidance agents.
  141. - NavigationServer sends safe_velocity vector with signals for each registered avoidance agents.
  142. - Agents receive the signal and move their parent e.g. with move_and_slide or linear_velocity.
  143. - PhysicsServer synchronizes.
  144. - physics frame ends.
  145. Therefore moving a physicsbody actor in the callback function with the safe_velocity is perfectly thread- and physics-safe
  146. as all happens inside the same physics_frame before the PhysicsServer commits to changes and does its own calculations.