如果使用 NavTag 标记包含自定义数据的地形区域,则可以确定哪些 NavTag 被认为是可供每个角色行走的有效地形,即使这些角色使用相同的数据库(因此使用同一组 NavData)时也是如此。您也可以自定义不同的人物优先选择或避免选择穿越不同 NavTag 的范围。
若要在您自己的路径中禁止、首选或避免 NavTag,您需要编写自定义 TraverseLogic 类。此类用于下列情况:
当您针对 NavMesh 运行其他种类的查询时,也可以使用同样的 TraverseLogic 类测试 NavTag。例如,如果运行 RayCastQuery 来检测沿两点之间的直线路径上是否存在任何障碍,可以使用该类实例设置查询,它将使用此实例确定哪些不同的 NavTag 可以穿越。
以下为三个 TraverseLogic 接口。它们的默认实现具有三个不同的复杂性级别,您可以选择符合您要求的级别。
如果 TraverseLogic 之一作为模板参数传递给 NavigationProfile 或查询,则会自动生成相应的代码。请参见步骤 3. 设置 TraverseLogic。
在 TraverseLogicWithCostPerTriangle 中,在单个 PathFinderQuery 期间多次请求三角形的成本倍增。若要避免因实现欠缺而出现性能问题,TriangleCostMap 类用于计算并存储查询可以访问的一些三角形的成本倍增。您需要编写从 ITriangleCostMap 派生的 TriangleCostMap 类。它仅具有一个纯虚拟的 Recompute() 函数。
使用之前,必须通过 BindToDatabase() 函数将 ITriangleCostMap 绑定到数据库。如果数据的三角形化中存在某些更改,如动态 NavMesh,则数据库可内部调用 Recompute() 函数。客户可调用它来更新它的成本倍增,但这不能频繁执行。您可调用以下辅助函数:Recompute() 中的 ITriangleCostMap::InitAndBrowseAllTrianglesInBox3f() 和 ITriangleCostMap::InitAndPropagateInTrianglesFromPosInVolume(),以自动初始化并设置三角形的成本倍增。有关详细信息,请参见 itrianglecostmap.h。
您可以以自己的方式编写三角形成本的设置,但必须确保调用 InitCostMapForBox3f() 函数之后,再针对三角形调用 SetCostMultiplier() 函数,以更改成本倍增(默认成本倍增为 1.f)。
当某些人物针对三角形进行寻径时,您不能更改三角形的成本。建议取消使用 TriangleCostMap 的人物的异步路径计算。您不能频繁更新 TriangleCostMap,否则人物可能无法在两次更新之间有足够的时间来查找路径。以下代码示例显示在 Recompute() 函数中可以执行以取消路径计算的操作:
for (KyUInt32 i = 0; i < gameWorld->GetBots().GetCount(); ++i) { GameBot* gamebot = gameWorld->GetBots()[i]; Kaim::Bot* navBot = gamebot->GetBot(); if (navBot->IsComputingNewPath()) navBot->CancelAsyncPathComputation(); }
另请参见 DefaultTraverseLogic 类,在您没有设置自己的 TraverseLogic 时默认使用该类。如果直接从 DefaultTraverseLogic 或 SimpleTraverseLogic 派生自定义类,只需要实现用于完成所需行为的替代方法。
默认情况下,DefaultTraverseLogic 或 SimpleTraverseLogic 类可防止所有移动穿过禁止的 NavTag。
但是,在某些特殊情况下,您可能要仅在一个方向上禁止导航穿过某一 NavTag,或仅从某些其他 NavTag 穿过。在这种方法中,您允许在 NavTag 所覆盖的区域中移动,但禁止跨越 NavTag 边界的移动。
class MyTraverseLogicClass : public Kaim::SimpleTraverseLogic<Kaim::LogicDoUseCanEnterNavTag> { ... };
TraverseLogic 接口中的所有方法都是静态的;因此,它们不与任何特定对象或状态绑定。这样不需要在运行时为要使用的人物和查询创建任何类实例,从而节省运行时内存。
但是,为了使 TraverseLogic 做出良好决定,以确定是否需要禁止 NavTag,或在不同环境中应该指定给不同 NavTag 的成本倍增,您可能需要使用绑定到对象的特定实例的某些种类的数据。出于此原因,所有 TraverseLogic 方法都接受无效指针。设置了模板以使用 TraverseLogic 的每个类(最明显的是 Bot 类,以及诸如 AStarQuery 和 RayCastQuery 等查询类)提供取值函数,可以用于设置和检索 Bot 或 Query 在需要调用 TraverseLogic 接口中的方法时传递的用户数据。例如:
class MyNavigationProfile : public Kaim::NavigationProfile<MyTraverseLogicClass> { };
Kaim::RayCastQuery<MyTraverseLogicClass> query;
每条线路分段的成本将由 RayCanGoQuery(由 AStarQuery 或路径跟随系统在内部运行)单独计算,以测试路径是否存在障碍物。每次由 RayCanGoQuery 测试的线路分段穿过 NavTag 边界,查询将边界点垂直投影到该线路分段。然后将沿其线路分段的每个子分段的距离乘以指定给相应的 NavTag 的成本倍增。
例如,在以下情景中,RayCanGoQuery 测试的线路分段在通过 NavMesh 三角形传播时跨越了多个不同的 NavTag:
( Distance(a) * CostMultiplier(blue) ) + ( Distance(b) * CostMultiplier(grey) ) + ( Distance(c) * CostMultiplier(blue) ) + ( Distance(d) * CostMultiplier(purple) ) + ( Distance(e) * CostMultiplier(blue) ) + ( Distance(f) * CostMultiplier(grey) ) + ( Distance(g) * CostMultiplier(blue) )
在确定完整路径的成本时,以这种方式对每个路径分段进行测试,总成本是所有分段的费用之和。
请注意,出于性能方面的原因,这一测量成本的方法不使用路径通过 NavMesh 的三角形所实际覆盖的精确距离,也不是角色沿该路径移动的精确距离。但是,这种近似值应足够接近,以允许您找到一组成本倍增,从而能够产生您的游戏玩法所需要的效果。
如果直接在自己的代码中运行 RayCanGoQuery,您可以对其进行配置,以计算测试的线路的成本。调用其 RayCanGoQuery::SetComputeCostMode() 方法,并传递 QUERY_COMPUTE_COST_ALONG_3DAXIS,确保使用应该用于确定 NavTag 的适航性和成本倍增的 TraverseLogic 来设置查询。运行查询之后,您可以通过调用 RayCanGoQuery::GetComputedCost() 检索成本。
有关演示自定义 TraverseLogic 类的使用以影响由 A* 查询生成的路径以及用作谓词以在路径跟随期间禁止特定 NavTag 的工作代码示例,请参见 Tutorial_NavTag.cpp 文件。