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...
优化导航性能
常见的与导航相关的性能问题可分为以下主题:
烘焙导航网格时解析场景树节点的性能问题。
实际烘焙导航网格时的性能问题。
NavigationAgent 路径查询的性能问题。
实际路径搜索的性能问题。
同步导航地图的性能问题。
在以下部分中可以找到有关如何识别和修复(或至少减轻)其对帧率影响的信息。
解析场景树节点的性能问题
小技巧
建议使用边数尽可能少的简单形状,避免使用圆形、球体或圆环等圆滑的形状。
建议优先使用物理碰撞形状而不是复杂的视觉网格作为源几何体,因为网格需要从 GPU 复制,并且通常不必要地过于详细。
一般来说,应避免使用非常复杂的几何体作为烘焙导航网格的源几何体。例如,切勿使用细节丰富的视觉网格,因为将其形状解析为数据数组并体素化以供导航网格烘焙将花费大量时间,而最终导航网格的质量却得不到真正提升。相反,应使用形状高度简化的细节层次版本。更好的做法是,使用非常原始的形体(例如盒子和矩形)来大致覆盖原几何体,这样既能显著缩短烘焙时间,又能获得足以满足寻路需求的导航网格。
建议优先使用简单的物理碰撞形状而不是视觉网格作为烘焙导航网格的源几何体。默认情况下,物理形状是非常有限且经过优化的形状,可以轻松快速地解析。相比之下,视觉网格可以十分简单,也可以十分复杂。更重要的是,为了访问视觉网格数据,解析器需要从渲染服务器请求网格数据数组,因为视觉网格数据直接存储在 GPU 上,而不是缓存在 CPU 上。这需要锁定渲染服务器线程,在渲染以多线程运行时会严重影响运行时的帧率。而如果渲染以单线程运行,帧率影响可能会更严重,网格解析可能会在复杂网格上冻结整个游戏几秒钟。
烘焙导航网格的性能问题
小技巧
在运行时,建议始终使用后台线程来烘焙导航网格。
增大 NavigationMesh 的 cell_size 和 cell_height 以创建较少的体素。
将 SamplePartitionType 从 watershed 更改为 monotone 或 layers 以获得烘焙性能。
警告
切勿使用节点缩放源几何体以避免精度错误。大多数缩放仅适用于视觉,未缩放时非常大的形状即使缩小后也需要大量额外的处理。
如果可能的话,在运行时烘焙导航网格应该始终在后台线程中完成。即使是小尺寸的导航网格的烘焙时间也可能远远超出单个帧所能容纳的时长,前提是想要将帧率保持在可忍受的水平。
从场景树节点解析的源几何数据的复杂性对烘焙性能有很大影响,因为所有内容都需要映射到一个栅格/体素上。对于运行时烘焙的性能,NavigationMesh 单元格大小和单元格高度应设置得尽可能高,前提是不会产生导航网格质量问题。如果单元格大小或单元格高度设置得太低,则烘焙将被迫创建过多的体素来处理源几何体。如果源几何体跨越非常大的游戏世界,这甚至可能导致烘焙过程在中途耗尽内存,使游戏崩溃。分区类型也可以根据游戏源几何体的复杂程度来降低,以获得一些性能。例如,具有块状几何形状、大部分表面平坦的游戏可以使用 monotone 或 layers 模式,它们的烘焙速度要快得多(比如因为它们不需要距离场阶段)。
切勿使用节点缩放源几何体。它不仅会导致大量精确度错误(顶点和边配对错误),而且某些缩放仅以视觉形式存在,而不存在于实际解析的数据中。例如,如果网格在编辑器中以视觉方式缩小,例如在 MeshInstance 上将比例设为 0.001,则网格仍然需要巨大且非常复杂的体素网格来处理烘焙。
NavigationAgent 路径查询的性能问题
小技巧
避免在 NavigationAgent 脚本中每一帧都对路径进行不必要的重置和查询。
避免在同一帧里更新所有 NavigationAgent 的路径。
自定义 NavigationAgent 脚本中的逻辑错误和浪费性操作是导致性能问题的常见原因,例如,请注意不要每帧重置路径。默认情况下,NavigationAgent 经过优化,仅在目标位置变更、导航地图变更或被迫远离所需路径距离时才会查询新路径。
例如,当 AI 应移动到玩家时,目标位置不应每帧都设定为玩家位置,因为这会使得每帧都查询新路径。相反,应该比较从当前目标位置到玩家位置的距离,并只在玩家移动得太远时才设定新的目标位置。
不要每帧预先检查是否可以到达目标位置。这种看似无害的检查在幕后实际上是昂贵的路径查询。如果计划本就是(检查到位置可达后)请求一条路径,则应直接查询路径。要回答“该位置是否可到达?”的问题,可以查看返回路径的最后一个位置,检查该位置距目标位置的距离是否在“可到达”的范围内。这避免了对同一个 NavigationAgent 每帧事实上执行两次完整路径查询。
将导航代理的总数分割为更新组或使用随机定时器,以便它们不会在同一帧中全部请求新路径。
实际路径搜索的性能问题
小技巧
通过减少多边形和边的数量来优化过于详细的导航网格。
实际路径搜索的成本与导航网格多边形和边缘的数量直接相关,而不是与游戏世界的实际大小相关。如果一个巨大的游戏世界使用非常优化的导航网格,只有少数多边形覆盖大面积,那么性能应该是可以接受的。如果游戏世界被分割成非常小的导航网格,每个导航网格都有微小的多边形(如 TileMap),则寻路性能将会降低。
一个常见问题是当路径查询中无法到达目标位置时性能突然下降。这种性能下降是“正常的”,是导航网格太大、未经优化,需要搜索大量多边形和边的结果。在可以快速到达目标位置的正常路径搜索中,一旦到达该位置,寻路就会提前退出,暂时隐藏这种优化不足的情况。如果无法到达目标位置,则寻路必须对现有多边形进行更久的搜索,以确认该位置绝对无法到达。
导航地图同步的性能问题
小技巧
尽可能通过顶点而不是边连接来合并导航网格多边形。
当对导航网格或导航区域进行变更时,导航服务器需要同步导航地图。根据导航网格的复杂性,这可能需要大量时间,这可能会影响帧率。
导航服务器通过顶点或边连接合并导航网格。当两条不同边的两个顶点落在同一地图网格单元中时,就会发生以顶点合并。这是一个相当快速且低成本的操作。第二遍处理是按边合并,用于其余所有尚未合并的边。这会通过距离和角度检查所有自由边是否有可能的边连接,是相当昂贵的。
因此,除了使用尽可能少的多边形边这条通用规则之外,还应通过顶点预先合并尽可能多的边,以便只留下少量边用于成本更高的边连接计算。调试导航性能监视器可用于获取有关可用多边形和边的数量以及其中有多少未合并或未按顶点合并的统计数据。如果顶点合并和边连接之间的比率和预想相差很大(顶点应该明显更高),则导航网格未能正确创建或放置得非常低效。