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.
Checking the stable version of the documentation...
使用 NavigationObstacle
2D 和 3D 版本的 NavigationObstacle 节点分别为 NavigationObstacle2D 和 NavigationObstacle3D 。
障碍物具有双重作用,可以影响导航网格烘焙和代理避障。
affect_navigation_mesh
启用时,障碍物会影响导航网格的烘焙。avoidance_enabled
启用时,障碍物会影响代理避障。
小技巧
避障默认是启用的。如果障碍物不需要参与避障,请禁用 enabled_avoidance
节省性能。
障碍物与导航网格

导航障碍物对导航网格烘焙的影响。
For navigation mesh baking, obstacles can be used to discard parts of all other source geometry inside the obstacle shape.
这可以用来避免在不需要的地方烘焙出导航网格,例如,类似厚墙的实心集合体的内部,或屋顶等不在游戏范围内的区域。

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

属性 affect_navigation_mesh
使得障碍物能够参与到导航网格的烘焙过程中。它将像导航网格烘焙过程中的其他节点对象一样被解析或未解析。
属性 carve_navigation_mesh
使得形状不受烘焙过程中的偏移影响,例如由导航网格的 agent_radius
添加的偏移。它基本上会像一个模板一样,在已经加上偏移的导航网格表面上切割。它仍然会受到烘焙过程后期处理的影响,比如边缘简化。
障碍物的形状和放置位置由 height
和 vertices
属性以及障碍物的 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)
Vector2[] obstacleOutline
[
new Vector2(-50, -50),
new Vector2(50, -50),
new Vector2(50, 50),
new Vector2(-50, 50),
];
var navigationMesh = new NavigationPolygon();
var sourceGeometry = new NavigationMeshSourceGeometryData2D();
NavigationServer2D.ParseSourceGeometryData(navigationMesh, sourceGeometry, GetNode<Node2D>("MyTestRootNode"));
bool obstacleCarve = true;
sourceGeometry.AddProjectedObstruction(obstacleOutline, obstacleCarve);
NavigationServer2D.BakeFromSourceGeometryData(navigationMesh, sourceGeometry);
var obstacle_outline = PackedVector3Array([
Vector3(-5, 0, -5),
Vector3(5, 0, -5),
Vector3(5, 0, 5),
Vector3(-5, 0, 5)
])
var navigation_mesh = NavigationMesh.new()
var source_geometry = NavigationMeshSourceGeometryData3D.new()
NavigationServer3D.parse_source_geometry_data(navigation_mesh, source_geometry, $MyTestRootNode)
var obstacle_elevation: float = $MyTestObstacleNode.global_position.y
var obstacle_height: float = 50.0
var obstacle_carve: bool = true
source_geometry.add_projected_obstruction(obstacle_outline, obstacle_elevation, obstacle_height, obstacle_carve)
NavigationServer3D.bake_from_source_geometry_data(navigation_mesh, source_geometry)
Vector3[] obstacleOutline =
[
new Vector3(-5, 0, -5),
new Vector3(5, 0, -5),
new Vector3(5, 0, 5),
new Vector3(-5, 0, 5),
];
var navigationMesh = new NavigationMesh();
var sourceGeometry = new NavigationMeshSourceGeometryData3D();
NavigationServer3D.ParseSourceGeometryData(navigationMesh, sourceGeometry, GetNode<Node3D>("MyTestRootNode"));
float obstacleElevation = GetNode<Node3D>("MyTestObstacleNode").GlobalPosition.Y;
float obstacleHeight = 50.0f;
bool obstacleCarve = true;
sourceGeometry.AddProjectedObstruction(obstacleOutline, obstacleElevation, obstacleHeight, obstacleCarve);
NavigationServer3D.BakeFromSourceGeometryData(navigationMesh, sourceGeometry);
障碍物与代理避障
避障导航中的障碍物可以是静态障碍物也可以是动态障碍物,能够影响启用了避障处理的代理。
当静态使用时,导览障碍会限制多边形定义区域外部或内部的回避控制代理。
当动态使用时,导览障碍会推开其周围半径范围内的回避控制代理。
静态避障障碍物
避障障碍物的 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)
// Create a new "obstacle" and place it on the default navigation map.
Rid newObstacleRid = NavigationServer2D.ObstacleCreate();
Rid defaultMapRid = GetWorld2D().NavigationMap;
NavigationServer2D.ObstacleSetMap(newObstacleRid, defaultMapRid);
NavigationServer2D.ObstacleSetPosition(newObstacleRid, GlobalPosition);
// Use obstacle dynamic by increasing radius above zero.
NavigationServer2D.ObstacleSetRadius(newObstacleRid, 5.0f);
// Use obstacle static by adding a square that pushes agents out.
Vector2[] outline =
[
new Vector2(-100, -100),
new Vector2(100, -100),
new Vector2(100, 100),
new Vector2(-100, 100),
];
NavigationServer2D.ObstacleSetVertices(newObstacleRid, outline);
// Enable the obstacle.
NavigationServer2D.ObstacleSetAvoidanceEnabled(newObstacleRid, true);
# Create a new "obstacle" and place it on the default navigation map.
var new_obstacle_rid: RID = NavigationServer3D.obstacle_create()
var default_map_rid: RID = get_world_3d().get_navigation_map()
NavigationServer3D.obstacle_set_map(new_obstacle_rid, default_map_rid)
NavigationServer3D.obstacle_set_position(new_obstacle_rid, global_position)
# Use obstacle dynamic by increasing radius above zero.
NavigationServer3D.obstacle_set_radius(new_obstacle_rid, 0.5)
# 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)
# Set the obstacle height on the y-axis.
NavigationServer3D.obstacle_set_height(new_obstacle_rid, 1.0)
# Enable the obstacle.
NavigationServer3D.obstacle_set_avoidance_enabled(new_obstacle_rid, true)
// Create a new "obstacle" and place it on the default navigation map.
Rid newObstacleRid = NavigationServer3D.ObstacleCreate();
Rid defaultMapRid = GetWorld3D().NavigationMap;
NavigationServer3D.ObstacleSetMap(newObstacleRid, defaultMapRid);
NavigationServer3D.ObstacleSetPosition(newObstacleRid, GlobalPosition);
// Use obstacle dynamic by increasing radius above zero.
NavigationServer3D.ObstacleSetRadius(newObstacleRid, 5.0f);
// Use obstacle static by adding a square that pushes agents out.
Vector3[] outline =
[
new Vector3(-5, 0, -5),
new Vector3(5, 0, -5),
new Vector3(5, 0, 5),
new Vector3(-5, 0, 5),
];
NavigationServer3D.ObstacleSetVertices(newObstacleRid, outline);
// Set the obstacle height on the y-axis.
NavigationServer3D.ObstacleSetHeight(newObstacleRid, 1.0f);
// Enable the obstacle.
NavigationServer3D.ObstacleSetAvoidanceEnabled(newObstacleRid, true);