Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update NavigationObstacle doc for avoidance rework #7334

Merged
merged 1 commit into from
May 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
101 changes: 71 additions & 30 deletions tutorials/navigation/navigation_using_navigationobstacles.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,52 +3,93 @@
Using NavigationObstacles
=========================

NavigationObstacles are used to set an avoidance radius around objects
that, due to their constant movement, cannot be efficiently (re)baked
to a 2D NavigationPolygon or 3D NavigationMesh.
NavigationObstacles can be used either as static or dynamic obstacles to affect avoidance controlled agents.

- When used statically NavigationObstacles constrain avoidance controlled agents outside or inside a polygon defined area.
- When used dynamically NavigationObstacles push away avoidance controlled agents in a radius around them.

2D and 3D versions of NavigationObstacles nodes are available as
:ref:`NavigationObstacle2D<class_NavigationObstacle2D>` and
:ref:`NavigationObstacle3D<class_NavigationObstacle3D>` respectively.

NavigationObstacles are not intended for any kind of static geometry
or temporary barriers that may change their position occasionally.
Those changes should be (re)baked so actors can follow the outlines
of these objects at higher detail with navigation paths. The obstacle avoidance
should be seen as a last resort option intended for objects that are constantly moving.
.. note::
NavigationObstacles do not change or influence the pathfinding in any way.
NavigationObstacles only affect the avoidance velocities of agents controlled by avoidance.

To use NavigationObstacles for avoidance, place a NavigationObstacle2D/3D node
below a Node2D/3D inheriting parent node. While the obstacle node has an
option to ``estimate_radius`` from child collisions, prefer to set a
more reliable manual ``radius`` value. If estimated, the obstacle will use
a radius that encapsulates the entire parent node which can result in a very large
radius value if the parent is not a circle shape but e.g. a long rectangle shape.
Static obstacles
~~~~~~~~~~~~~~~~

.. note::
A NavigationObstacle is considered static when its ``vertices`` property is populated with an outline array of positions to form a polygon.

.. image:: img/nav_static_obstacle_build.gif

- Static obstacles act as hard do-not-cross boundaries for avoidance using agents, e.g. similar to physics collision but for avoidance.
- Static obstacles define their boundaries with an array of outline ``vertices`` (positions), and in case of 3D with an additional ``height`` property.
- Static obstacles only work for agents that use the 2D avoidance mode.
- Static obstacles define through winding order of the vertices if agents are pushed out or sucked in.
- Static obstacles can not change their position. They can only be warped to a new position and rebuild from scratch. Static obstacles as a result are ill-suited for usages where the position is changed every frame as the constant rebuild has a high performance cost.
- Static obstacles that are warped to another position can not be predicted by agents. This creates the risk of getting agents stuck should a static obstacle be warped on top of agents.

When the 2D avoidance is used in 3D the y-axis of Vector3 vertices is ignored. Instead, the global y-axis position of the obstacle is used as the elevation level. Agents will ignore static obstacles in 3D that are below or above them. This is automatically determined by global y-axis position of both obstacle and agent as the elevation level as well as their respective height properties.

Dynamic obstacles
~~~~~~~~~~~~~~~~~

A NavigationObstacle is considered dynamic when its ``radius`` property is greater than zero.

- Dynamic obstacles act as a soft please-move-away-from-me object for avoidance using agents, e.g. similar to how they avoid other agents.
- Dynamic obstacles define their boundaries with a single ``radius`` for a 2D circle, or in case of 3D avoidance a sphere shape.
- Dynamic obstacles can change their position every frame without additional performance cost.
- Dynamic obstacles with a set velocity can be predicted in their movement by agents.
- Dynamic obstacles are not a reliable way to constrain agents in crowded or narrow spaces.

While both static and dynamic properties can be active at the same time on the same obstacle this is not recommended for performance.
Ideally when an obstacle is moving the static vertices are removed and instead the radius activated. When the obstacle reaches the new final position it should gradually enlarge its radius to push all other agents away. With enough created save space around the obstacle it should add the static vertices again and remove the radius. This helps to avoid getting agents stuck in the suddenly appearing static obstacle when the rebuild static boundary is finished.

The obstacle ``radius`` is the area that will be strictly avoided whenever possible.
Do not set it too large. Agents start to avoid way before
this radius depending on parameters and velocity.
Similar to agents the obstacles can make use of the ``avoidance_layers`` bitmask.
All agents with a matching bit on their own avoidance mask will avoid the obstacle.

Procedual obstacles
~~~~~~~~~~~~~~~~~~~

While NavigationObstacle nodes do require a Node parent the NavigationServer obstacles do not.
New obstacles created in scripts require only a ``map``, ``radius`` and ``position``.
Obstacles can be placed directly on the NavigationMap with the NavigationServer API.
New obstacles can be created without a Node directly on the NavigationServer.

Obstacles created with scripts require at least a ``map`` and a ``position``.
For dynamic use a ``radius`` is required.
For static use an array of ``vertices`` is required.

.. tabs::
.. code-tab:: gdscript GDScript

extends Node2D
# create a new "obstacle" and place it on the default navigation map.
var new_obstacle_rid: RID = NavigationServer2D.obstacle_create()
var default_2d_map_rid: RID = get_world_2d().get_navigation_map()

NavigationServer2D.obstacle_set_map(new_obstacle_rid, default_2d_map_rid)
NavigationServer2D.obstacle_set_position(new_obstacle_rid, global_position)

# Use obstacle dynamic by increasing radius above zero.
NavigationServer2D.obstacle_set_radius(new_obstacle_rid, 5.0)

# Use obstacle static by adding a square that pushes agents out.
var outline = PackedVector2Array([Vector2(-100, -100), Vector2(100, -100), Vector2(100, 100), Vector2(-100, 100)])
NavigationServer2D.obstacle_set_vertices(new_obstacle_rid, outline)

.. tabs::
.. code-tab:: gdscript GDScript

extends Node3D
# create a new "obstacle" agent and place it on the default map``
var new_agent_rid: RID = NavigationServer3D.agent_create()
# Create a new "obstacle" and place it on the default navigation map.
var new_obstacle_rid: RID = NavigationServer3D.obstacle_create()
var default_3d_map_rid: RID = get_world_3d().get_navigation_map()

NavigationServer3D.agent_set_map(new_agent_rid, default_3d_map_rid)
NavigationServer3D.agent_set_radius(new_agent_rid, 0.5)
NavigationServer3D.agent_set_position(new_agent_rid, global_transform.origin)
NavigationServer3D.obstacle_set_map(new_obstacle_rid, default_3d_map_rid)
NavigationServer3D.obstacle_set_position(new_obstacle_rid, global_position)

.. note::
# Use obstacle dynamic by increasing radius above zero.
NavigationServer3D.obstacle_set_radius(new_obstacle_rid, 0.5)

The NavigationServer API has no dedicated functions for obstacles.
Obstacles are technically considered just normal agents.
All "agent" prefixed functions are intended for obstacles as well.
# Use obstacle static by adding a square that pushes agents out.
var outline = PackedVector3Array([Vector3(-5, 0, -5), Vector3(5, 0, -5), Vector3(5, 0, 5), Vector3(-5, 0, 5)])
NavigationServer3D.obstacle_set_vertices(new_obstacle_rid, outline)