Attention: Here be dragons

This is the latest (unstable) version of this documentation, which may document features not available in or compatible with released stable versions of Godot.

使用 NavigationObstacle

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

障碍物具有双重作用,可以影响导航网格烘焙和代理避障。

  • affect_navigation_mesh 启用时,障碍物会影响导航网格的烘焙。

  • avoidance_enabled 启用时,障碍物会影响代理避障。

小技巧

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

障碍物与导航网格

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

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

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

这可以用来避免在不需要的地方烘焙出导航网格,例如,类似厚墙的实心几何体的内部,或屋顶等不在游戏范围内的区域。

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

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

障碍物在烘焙过程中并不会添加几何形状,它只会移除几何形状。它是通过消除障碍物形状内部所有被栅格化源几何覆盖的(体素)单元格来实现这一点的。因此,它的效果和形状细节受限于烘焙过程中使用的单元格分辨率。

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

../../_images/nav_mesh_obstacles_properties.webp

属性 affect_navigation_mesh 使得障碍物能够参与到导航网格的烘焙过程中。它将像导航网格烘焙过程中的其他节点对象一样被解析或未解析。

属性 carve_navigation_mesh 使得形状不受烘焙过程中的偏移影响,例如由导航网格的 agent_radius 添加的偏移。它基本上会像一个模板一样,切除已经加上偏移的导航网格表面。它仍然会受到烘焙过程后期处理的影响,比如边缘简化。

障碍物的形状和放置位置由 heightvertices 属性以及障碍物的 global_position 定义。用于定义顶点的任何 Vector3 的 y 轴值会被忽略,因为障碍物是在一个平坦的水平面上投影的。

在脚本中烘焙导航网格时,可以过程式地添加障碍物作为投影遮挡。障碍物不会参与源几何的解析,所以恰好在烘焙前添加它们就足够了。

var obstacle_outline = PackedVector2Array([
    Vector2(-50, -50),
    Vector2(50, -50),
    Vector2(50, 50),
    Vector2(-50, 50)
])

var navigation_mesh = NavigationPolygon.new()
var source_geometry = NavigationMeshSourceGeometryData2D.new()

NavigationServer2D.parse_source_geometry_data(navigation_mesh, source_geometry, $MyTestRootNode)

var obstacle_carve: bool = true

source_geometry.add_projected_obstruction(obstacle_outline, obstacle_carve)

NavigationServer2D.bake_from_source_geometry_data(navigation_mesh, source_geometry)

障碍物与代理避障

避障导航中的障碍物可以作为静态也可以作为动态来影响避障代理。

  • 当作为静态时,NavigationObstacle 会将避障代理限制在多边形定义区域的外部或内部。

  • 当作为动态时,NavigationObstacle 会推开其周围半径范围内的避障代理。

静态避障障碍物

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

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

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

  • 静态障碍物对于避障代理起着硬性、禁止通过的边界的作用,就好似物理碰撞,只不过是用于避障的。

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

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

  • 静态障碍物通过顶点的缠绕顺序来定义代理是被推出还是吸入。

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

  • 代理无法预测被传送到另一个位置的静态障碍物。这会带来风险,万一静态障碍物被传送到代理头上,代理就会被卡住。

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

动态避障障碍物

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

  • 动态障碍物对于避障代理起着弹性、“请离我远点”的对象的作用,类似于回避其他代理。

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

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

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

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

虽然障碍物可以同时激活静态和动态属性,但是出于性能的考虑不建议这么做。理想情况下,障碍物移动时应该移除静态顶点、激活半径。障碍物到达目的地后则应该逐步增大半径,将其他代理推开。在障碍物的周围创造出足够大的安全区域后,就应该把静态顶点添加回来、移除半径。这样能够帮助避免重建静态边界后,代理因为静态障碍物的突然出现而被卡住的情况。

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

程序式障碍物

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

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

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

NavigationServer2D.obstacle_set_map(new_obstacle_rid, default_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)

# Enable the obstacle.
NavigationServer2D.obstacle_set_avoidance_enabled(new_obstacle_rid, true)