使用 NavigationObstacle

2D 和 3D 版本的 NavigationObstacle 节点分别为 NavigationObstacle2DNavigationObstacle3D

Navigation obstacles are dual purpose in that they can affect both the navigation mesh baking, and the agent avoidance.

  • With affect_navigation_mesh enabled the obstacle will affect navigation mesh when baked.

  • With avoidance_enabled the obstacle will affect avoidance agents.

小技巧

避障默认是启用的。如果障碍物不需要参与避障,请禁用 enabled_avoidance 节省性能。

障碍物与导航网格

导航障碍物对导航网格烘焙的影响

导航障碍物对导航网格烘焙的影响。

烘焙导航网格时,可以用障碍物来丢弃来源几何体中所有位于障碍物形状内部的部分。

This can be used to stop navigation meshes being baked in unwanted places, e.g. inside “solid” geometry like thick walls or on top of other geometry that should not be included for gameplay like roofs.

导航障碍物弃置不需要的导航网格

导航障碍物弃置不需要的导航网格。

An obstacle does not add geometry in the baking process, it only removes geometry. It does so by nullifying all the (voxel) cells with rasterized source geometry that are within the obstacle shape. As such its effect and shape detail is limited to the cell resolution used by the baking process.

更多关于导航网格烘焙的信息见 使用导航网格

../../_images/nav_mesh_obstacles_properties.webp

The property affect_navigation_mesh makes the obstacle contribute to the navigation mesh baking. It will be parsed or unparsed like all other node objects in a navigation mesh baking process.

The carve_navigation_mesh property makes the shape unaffected by offsets of the baking, e.g. the offset added by the navigation mesh agent_radius. It will basically act as a stencil and cut into the already offset navigation mesh surface. It will still be affected by further postprocessing of the baking process like edge simplification.

The obstacle shape and placement is defined with the height and vertices properties, and the global_position of the obstacle. The y-axis value of any Vector3 used for the vertices is ignored as the obstacle is projected on a flat horizontal plane.

When baking navigation meshes in scripts obstacles can be added procedurally as a projected obstruction. Obstacles are not involved in the source geometry parsing so adding them just before baking is enough.

2D GDScript3D GDScript

  1. var obstacle_outline = PackedVector2Array([
  2. Vector2(-50, -50),
  3. Vector2(50, -50),
  4. Vector2(50, 50),
  5. Vector2(-50, 50)
  6. ])
  7. var navigation_mesh = NavigationPolygon.new()
  8. var source_geometry = NavigationMeshSourceGeometryData2D.new()
  9. NavigationServer2D.parse_source_geometry_data(navigation_mesh, source_geometry, $MyTestRootNode)
  10. var obstacle_carve: bool = true
  11. source_geometry.add_projected_obstruction(obstacle_outline, obstacle_carve)
  12. NavigationServer2D.bake_from_source_geometry_data(navigation_mesh, source_geometry)
  1. var obstacle_outline = PackedVector3Array([
  2. Vector3(-5, 0, -5),
  3. Vector3(5, 0, -5),
  4. Vector3(5, 0, 5),
  5. Vector3(-5, 0, 5)
  6. ])
  7. var navigation_mesh = NavigationMesh.new()
  8. var source_geometry = NavigationMeshSourceGeometryData3D.new()
  9. NavigationServer3D.parse_source_geometry_data(navigation_mesh, source_geometry, $MyTestRootNode)
  10. var obstacle_elevation: float = $MyTestObstacleNode.global_position.y
  11. var obstacle_height: float = 50.0
  12. var obstacle_carve: bool = true
  13. source_geometry.add_projected_obstruction(obstacle_outline, obstacle_elevation, obstacle_height, obstacle_carve)
  14. NavigationServer3D.bake_from_source_geometry_data(navigation_mesh, source_geometry)

障碍物与代理避障

避障导航中的障碍物可以是静态障碍物也可以是动态障碍物,能够影响启用了避障处理的代理。

  • 当静态使用时,导览障碍会限制多边形定义区域外部或内部的回避控制代理。

  • 当动态使用时,导览障碍会推开其周围半径范围内的回避控制代理。

静态避障障碍物

避障障碍物的 vertices 属性填充轮廓位置数组形成多边形后,就会被认为是静态障碍物。

编辑器中绘制的静态障碍物,障碍物可以阻挡或容纳导航代理

编辑器中绘制的静态障碍物,障碍物可以阻挡或容纳导航代理。

  • 静态障碍物起着硬性作用——使代理无法跨越边界而回避,例如类似于物理碰撞,但用于回避。

  • 静态障碍物用一组轮廓 vertices (位置)定义其边界,在3D的情况下,用额外的 height 属性定义其边界。

  • 静态障碍物仅适用于使用2D回避模式的代理。

  • 如果代理人被推出或吸入,静态障碍物透过顶点的缠绕顺序来定义。

  • 静态障碍物不能改变位置,只能先传送到新的位置,然后重新构建。因此,静态障碍物不适合位置每帧都会改变的场合,因为不断重建会产生很高的性能成本。

  • 代理无法预测扭曲到另一个位置的静态障碍物。如果静态障碍物扭曲到代理顶部,这就会产生代理被卡住的风险。

当在3D中使用2D回避时,Vector3顶点的y轴将被忽略。相反,障碍物的全局y轴位置用作海拔高度。代理将忽略3D中低于或高于其的静态障碍物。这是由障碍物和代理的全局y轴位置自动确定为海拔高度以及它们各自的高度属性。

动态避障障碍物

避障障碍物的 radius 属性大于零时就会被认为是动态障碍物。

  • 对于启用了避障的代理而言,动态障碍物就是一个“请离我远点”的对象,类似于它们自己躲避其他代理的行为。

  • 动态障碍物使用 radius 半径来定义边界,2D 中是圆形,3D 中是球形。

  • 动态障碍物每一帧都可以改变位置,不会有额外的性能开销。

  • 动态障碍物设置速度后,其他代理就能够预测移动。

  • 动态障碍物不适合用来将代理限制在拥挤、狭窄的空间中。

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 safe space around the obstacle it should add the static vertices again and remove the radius. This helps avoid getting agents stuck in the suddenly appearing static obstacle when the rebuilt static boundary is finished.

和代理类似,障碍物也能够使用 avoidance_layers 位掩码。自身的避障掩码中与之存在匹配位的代理都会躲避这个障碍物。

程序式障碍物

可以不借助节点,直接在脚本中使用 NavigationServer 来新建障碍物。

使用脚本创建的障碍物至少需要有一个 map 和一个 position。动态障碍物还需要 radius。静态障碍物还需要 vertices 属性。

2D GDScript3D GDScript

  1. # create a new "obstacle" and place it on the default navigation map.
  2. var new_obstacle_rid: RID = NavigationServer2D.obstacle_create()
  3. var default_map_rid: RID = get_world_2d().get_navigation_map()
  4. NavigationServer2D.obstacle_set_map(new_obstacle_rid, default_map_rid)
  5. NavigationServer2D.obstacle_set_position(new_obstacle_rid, global_position)
  6. # Use obstacle dynamic by increasing radius above zero.
  7. NavigationServer2D.obstacle_set_radius(new_obstacle_rid, 5.0)
  8. # Use obstacle static by adding a square that pushes agents out.
  9. var outline = PackedVector2Array([Vector2(-100, -100), Vector2(100, -100), Vector2(100, 100), Vector2(-100, 100)])
  10. NavigationServer2D.obstacle_set_vertices(new_obstacle_rid, outline)
  11. # Enable the obstacle.
  12. NavigationServer2D.obstacle_set_avoidance_enabled(new_obstacle_rid, true)
  1. # Create a new "obstacle" and place it on the default navigation map.
  2. var new_obstacle_rid: RID = NavigationServer3D.obstacle_create()
  3. var default_map_rid: RID = get_world_3d().get_navigation_map()
  4. NavigationServer3D.obstacle_set_map(new_obstacle_rid, default_map_rid)
  5. NavigationServer3D.obstacle_set_position(new_obstacle_rid, global_position)
  6. # Use obstacle dynamic by increasing radius above zero.
  7. NavigationServer3D.obstacle_set_radius(new_obstacle_rid, 0.5)
  8. # Use obstacle static by adding a square that pushes agents out.
  9. var outline = PackedVector3Array([Vector3(-5, 0, -5), Vector3(5, 0, -5), Vector3(5, 0, 5), Vector3(-5, 0, 5)])
  10. NavigationServer3D.obstacle_set_vertices(new_obstacle_rid, outline)
  11. # Set the obstacle height on the y-axis.
  12. NavigationServer3D.obstacle_set_height(new_obstacle_rid, 1.0)
  13. # Enable the obstacle.
  14. NavigationServer3D.obstacle_set_avoidance_enabled(new_obstacle_rid, true)