UDN
Search public documentation:

NavMeshConstraintsAndGoalEvaluatorsCH
English Translation
日本語訳
한국어

Interested in the Unreal Engine?
Visit the Unreal Technology site.

Looking for jobs and company info?
Check out the Epic games site.

Questions about support via UDN?
Contact the UDN Staff

UE3 主页 > AI & 导航 > 导航网格物体路径约束及目标点求值器

导航网格物体路径约束及目标点求值器


概述


本文档对路径约束及路径目标点求值程序的目的及应用进行了概述,因为它们和寻路及一般的路径遍历相关。

为什么?

这些模块化的自定义的约束的出现允许 AI 程序员只需要很少的代码修改便可以运行特定的路径搜索来满足游戏的特定需要。过去,如果您想执行类似于 查找远离实体的从 A->B 的路径 的操作,您必须在 native 代码中的路径消耗函数中添加这种特殊的情况,并且对于每一种新的情况都需要执行这个操作。很快便会发现这是不实用的。对于您需要的用于进行特定路径搜索的每个自定义考虑项(约束)的不同组合,都要针对每个新的搜索添加新的代码。

而我们的方法是把关系的对象打包到称为 PathConstraints 的自包含对象中,这里可以添加在寻路过程中考虑的所有信息。所以,如果您正在尝试查找到您的目的地的最短路径,那么您可以简单地添加一个 Path_TowardGoal 约束,这样您将会获得 AI 程序员所熟知的经典的线性的 A* 启发式搜索。然而,如果您想进行更加复杂的操作,比如查找避开触发器从 A -> B 的最短路径,您将需要简单地添加 Path_TowardGoal 约束和一个(假想的) Path_AvoidFire 约束。这是很容易完成,并且可以通过脚本访问它们,所以通过拼凑自定义的、一次性的路径搜索来满足您的精确地特定情况是很容易的。

然而,约束只是成功的一半。您也需要具备指出路径搜索的结束条件并在遍历路径的过程中标记数据。这便是路径目标点求值器设计的目的所在。 它们既指出了搜索何时完成(比如,当我们找到一个有效的目标节点时),也处理了计算得出的数据的标记和存储。比如,在战争机器中,当搜索掩体时,则会使用一个 掩体目标点求值器 。 这个自定义的目标点求值器将会继续搜索直到找到满足条件的掩体节点为止,它将会跟踪到现在为止找到的掩体节点,并沿着路线记录它们。最基本的(经常使用的)路径目标求值程序是 Goal_AtActor ,当找到了包含着搜索的目标点的导航网格物体多边形时,它将会简单地停止搜索。这是您在查找从 A->B 之间的最短路径的目标求值器。

约束和目标求值器存储在导航句柄上的两个列表上。

路径约束


正如上面所提到的,路径约束既可以修改存储的实际距离 (g),也可以修改启发式距离 (h)。最近,路径约束也可以在特定的节点上返回 false,这意味着这个节点是完全不适合的并且根本不应该将其添加到打开列表。

可以应用路径约束的情况:

  • 朝向目标点的遍历(比如,通过首先尝试朝向目标点的节点来优化搜索)。
  • 通过或条件性地限制不能遍历的区域来限制遍历(比如,触发器、熔岩、仅用户优化的门)。
  • 远离不需要的对象来调整遍历(比如,敌人玩家)。

不应该/不能 应用路径约束的情况:

  • 收集/存储 数据时(如果前一个约束返回 false,那么您的约束可能不会被调用)。

路径目标点求值器


路径目标点求值器稍微有点复杂,因为它们处理了大多数的节本寻路函数,比如决定开始节点和结束节点,以及一旦由于任何原因导致搜索终止,那么那个节点来调用‘目标节点’。 目标点求值器建立了搜索、决定了什么时候完成、并且当找到目标点时将会把找到的路径保存到导航句柄中。

ALERT! 注意: 列表中的 第一个 路径目标点求值器有特殊的意义。它作为主要的求值器,因此它将控制搜索的初始化(比如 seedworkingset(搜索工作的节点集合)、initializesearch(初始化搜索))

这里是典型的路径目标点求值器的所做的事

  1. InitializeSearch() - 这个函数在路径搜索的开始阶段进行调用。这允许目标求值器设置它进行搜索过程所需要的任何东西。默认的功能是查找到包含 SearchStart 的多边形,并设置它为 AnchorPoly
  2. SeedWorkingSet() - 它是家下来调用的函数。 这个函数负责向打开列表填充一个或多个多边形来供其进行搜索。 默认的功能是简单地把锚点多边形添加到工作集中。

现在我们进入了路径搜索的主循环。

  1. EvaluateGoal() - 当最好的节点离开工作集后调用这个函数… 这个函数将会在每个节点上依次调用称为 EvaluateGoal() 的整个路径目标求值器列表。 这使得列表中的每个目标点求值器都有机会标记多边形以及当找到适当的节点时调用停止函数来终止搜索。
  2. DetermineFinalGoal() - 只要路径完成,便会调用这个函数。(或者因为找到了一个目标点或者因为已经搜索过了要测试的所有的节点)这允许目标点求值器来执行最终的计算并决定实际的最佳目标点是什么(如果存在)。这里函数的默认行为是判断是否找到了目标点,但是在更加复杂的求值器比如 Goal_AtCover 中,最终的结果由到目前为止所访问的多边形来决定最适合的目标点是什么。
  3. SaveResultingPath() - 是求值器中最后调用的函数。在几乎所有的目标点求值器上,这个函数简单地反向遍历前面的节点链来按照从开始节点到目标点的顺序来保存路径。 但是,这允许自定义的求值器来在这里执行一些不同的操作。比如, Goal_ClosestActorInList (按照从目标点到开始点进行寻路)是按照顺序的顺序来存储路径的,因为它反转到了开始点(请参照以下获得关于这个求值器的更多信息)。
  4. NotifyExceededMaxPathVisits() - 是在例外情况下调用的一个函数。当访问的多边形的数量超过 navigation handle(导航句柄)上的 MaxPathVisits 参数时,将会调用这个函数。在大多数情况下,这意味着失败(比如,在最大的样本范围内没有找到目标点)。但是有时这仅意味着搜索完成(比如 Goal_Null 仅在搜索完所有节点或者碰到最大的访问范围时才停止搜索..这对于找到一个区域内的最佳节点是有用的)。

唯一需要新的路径目标点求值器实现的函数是 EvaluateGoal() 。这是目标点求值器的主要函数(也就是,它决定了什么时候找到有效地目标点)。

有很多影响 EvaluateGoal() 操作方式的布尔值。我们所进行的 99% 的遍历使用的都是默认设置(并且实际上大多数遍历仅有一个目标点求值器,所以这些值实际是没有使用),但是对于目标点求值器的组合来说,添加这些布尔值可以使得事情变得更加简单:

  • bUseORforEvaluateGoal (在 UNavigationHandle 上) - 默认情况下,为了判断搜索的结束与否, 所有 的目标点求值器都会从 EvaluateGoal() 中返回 true。 这个返回值可以通过使用 navigation handle(导航句柄)上的 bUseORforEvaluateGoal 来进行修改。(当返回值为 true 时,如果从目标求值器中的任何 EvaluateGoal() 中返回 true,那么搜索将会终止)。
  • bAlwaysCallEvaluateGoal (在 GoalEvaluator 上) - 当特定的目标点求值器的这项为 true 时,那么即使目标点求值器已经判定了当前的节点是否是最终目标点,仍然会调用目标求值器的 EvaluateGoal() 函数。 这对于需要针对每个路径迭代存储信息或计算某些数据的目标点求值器是有用的。

Constraint(约束)/GoalEval(目标点求值器)池


为了防止不断地新建不必要的路径约束和目标点求值器,可以根据需要新建它们并缓存它们。 这允许在不需要新建及垃圾回收大量约束的情况下重新使用缓存的约束及目标点求值器。

为了达到这个目的,您应该永远都不要直接地新建 PathConstraint 或 GoalEvaluator。反之,您应该在 NavigationHandle 上调用 CreatePathConstraint()CreatePathGoalEvaluator() 函数。在还没有缓存一个约束或者检索到一个缓存的副本,那么它将新建一个该约束的副本。

作为缓存过程的一部分,当从句柄中清除一个约束时,将会调用 Recycle() 事件。在这个函数中,所有的状态都将会被清除并重置为默认值。 当重写 PathConstraint 和 GoalEvaluator 时,重写这个函数并清除任何可能成为您的衍生类一部分的任何新的状态是很重要的,以便您不会使用来自循环使用的约束的旧数据。

示例使用情况


在 约束/求值器 上实现一个静态函数是很方便的,您仅需要调用它将该约束或求值器添加到句柄列表即可。这里是 Path_TowardGoal 约束的一个这样的静态函数实例:

Path_TowardGoal.uc
static function bool TowardGoal(NavigationHandle NavHandle, Actor Goal)
{
  local NavMeshPath_Toward Con;

  if (NavHandle != None && Goal != None)
  {
    Con = NavMeshPath_Toward(NavHandle.CreatePathConstraint(default.class));
    if (Con != None)
    {
      Con.GoalActor = Goal;
      NavHandle.AddPathConstraint( Con );
      return TRUE;
    }
  }

  return FALSE;
}

正如您所看到的,它调用了 CreatePathConstraint() 函数来获得该类的缓存版本,然后调用了 AddPathConstraint() 将它放到约束列表中,以便在搜索时使用。*AddPathConstraint()* 、 AddGoalEvaluator()ClearConstraints() 是为您的遍历指定约束条件的界面。

ALERT! 注意: 通常没有必要调用 ClearConstraints() 函数,因为在您调用 FindPath() 后,将会自动地清除约束。

关于 FindPath() 及其应用的更多信息,可以在导航网格物体技术指南页面找到。 同时,请确保阅读导航网格物体路径调试页面来获得关于如何调试您的路径搜索的更多信息。

目前的路径约束及它们的应用的总结


NavMeshPath_AlongLine

随着与指定方向的距离越来越远,这个约束项节点添加启发式消耗。

NavMeshPath_EnforceTwoWayEdges

它将过滤掉不可以双向行走的边。(防止出现 AI 进入到它们内部无法出来的情况)。

NavMeshPath_MinDistBetweenSpecsOfType

它将会沿着先前走过的链的每步返回,从而确保在某种类型的边之间有最小的距离。 (比如,确保在覆盖物之间有最小的距离)。

NavMeshPath_SameCoverLink

它只允许那些其中包含指定 CoverLink 中的掩体的多边形。

NavMeshPath_Toward

这将会根据到传入的目标点的距离来添加启发式消耗。 可以使用这个约束来使得正常的路径搜索偏向您的特定目标点。

NavMeshPath_WithinDistanceEnvelope

允许用户指定在路径的有效范围,它可以或者把节点抛出到这个范围之外或者随着它们远离范围而使节点处于不利位置。 (比如,我想查找在距离我的某个最大范围值之内的一个点)

NavMeshPath_WithinTraversalDist

将会否决那些超过指定值的路径

目前的路径目标点求值器及其应用的总结


NavMeshGoal_At

最简单最常用的目标点求值器。 当找到包含目标点的多边形时将会结束搜索。

NavMeshGoal_ClosestActorInList

它是其中的一个比较有趣的求值器。 这个求值器可以有效地找到距离要求的实体最近的 actor(以路径距离为单位)。 这可以通过执行反向的搜索来完成。 工作集将会由包含列表中要搜索的 actor 的所有多边形所填充,然后将会继续搜索,直到找到包含 searchstart(搜索开始节点)的多边形为止。 这时,路径是按照正向顺序进行保存的,因为我们已经进行了反向搜索。

NavMeshGoal_Filter

这是一个具有辅助作用的基类,它主要用于那些将要与 NavMeshGoal_GenericFilterContainer 结合使用的过滤器中。这些目标应该只会回答“这是一个有效的最终目标吗?这个问题,不会做其他任何事情。

NavMeshGoal_GenericFilterContainer

这个目标求职器在它没有离开路径之前不会停止,而且只会返回内存消耗最少的节点。

NavMeshGoal_Null

这个目标求值器将会简单地保持搜索,直到达到迭代次数的最大值或者搜索完所有节点为止。这对于找到某个区域的最佳节点是有用的。(比如,可以和 NavMeshPath_WithinDistanceEnvelope 约束结合使用,在给定的最大范围内查找最远的多边形)。

NavMeshGoal_PolyEncompassesAI

这个目标求值器将会扔掉无法完全适合这个实体搜索的多边形,这通常用于开放端路径搜索(例如,查找任何不在某个半径范围内的多边形),因为边可能会支持允许遍历进入到多边形的实体,但是这个实体可能不需要完全与这个多边形内部相适应,尽管他可以穿过它。

NavMeshGoal_Random

这个目标求职器在它没有离开路径之前不会停止,而且会返回一个随机遍历的节点。

NavMeshGoal_WithinDistanceEnvelope

这个目标求值器会扔掉节点(多边形),前提是它们位于指定的距离范围外。