本系列的教程文章基于 A*Pathfinding Project 4.2.8的官网教程翻译,每一章节的原文地址都会在教程最下方给出。

这篇会讲怎么处理不同类型的agent,比如尺寸方面的。

如果你的agents的尺寸不同,它们通常不会找到相同的路径。但是幸运的是,我们有办法解决。最简单的方法就是创建多个graphs,每种类型对应一个特定的graph。但是如果你有很多个不同类型的agent,那么你就需要创建非常多的graphs,这样不但会快速消耗内存,还会增加scanning的时间。在Seeker组件上,可以设置seeker使用哪个graph。

示例

假如我们有2个agents:

在AstarPath的面板上,我们可以创建两个不同的graphs,唯一的区别就是 character radius不一样。(其他的参数其实也可以不一样,根据你的需求决定)在这个示例里,我们使用了recast类型,但其实其他的类型也是一样简单的。

scan之后,我们会得到一个结果如下图所示。小的agents是蓝色的,大的是紫色的。

在Seeker组件,我们可以选择使用哪一个graph。

那么现在每一个agent都使用了适合他们自己大小的graph了。

视频封面

00:17

grid graph的其他方式

在grid的graph上,可以使用其他方式来替代。让graph根据障碍物不同的距离生成不同的tag,这只适用于single graph。

还有一种方法非常高效,但是不好的是计算的代价比较大。对于路径请求,可以提供一个ITraversalProvider对象,用来确定哪些节点不应该遍历。这在之前的文章里有提到过。

放牛的星星:Unity寻路插件(A* Pathfinding)进阶教程九:基于turn-based的游戏实战?

zhuanlan.zhihu.com
图标

使用这个的话,我们可以添加自定义代码,而不仅仅是检查它是否可以遍历。当然我们还可以检查它周围的所有节点,例如一个3X3或者5X5平方的范围。(默认是1X1)

下面的图片展示了使用3X3的情况。请注意,即使某个节点是可以通行的它也不会过去,因为它会检查周围3X3的范围。

ITraversalProvider的方法主要优点就是,它会处理所有导致节点不可遍历的事情。比如,你可以使用不同的标记来限制代理的移动。但是,如果你使用的是多个graph,不同的agent仍然是能够根据tag标识到达一个区域,即使该区域不能通行,当然前提是它的size要足够大。而这种方法它就没有办法到达,因为除了检查必要可通行的节点,它还检查每个节点周围NXN的区域。

ITraversalProvider的代码 大概可以这样写:

class GridShapeTraversalProvider : ITraversalProvider {
Int2[] shape;

public static GridShapeTraversalProvider SquareShape (int width) {
if ((width % 2) != 1) throw new System.ArgumentException("only odd widths are supported");
var shape = new GridShapeTraversalProvider();
shape.shape = new Int2[width*width];

// Create an array containing all integer points within a width*width square
int i = 0;
for (int x = -width/2; x <= width/2; x++) {
for (int z = -width/2; z <= width/2; z++) {
shape.shape[i] = new Int2(x, z);
i++;
}
}
return shape;
}

public bool CanTraverse (Path path, GraphNode node) {
GridNodeBase gridNode = node as GridNodeBase;

// Dont do anything special for non-grid nodes
if (gridNode == null) return DefaultITraversalProvider.CanTraverse(path, node);
int x0 = gridNode.XCoordinateInGrid;
int z0 = gridNode.ZCoordinateInGrid;
var grid = gridNode.Graph as GridGraph;

// Iterate through all the nodes in the shape around the current node
// and check if those nodes are also traversable.
for (int i = 0; i < shape.Length; i++) {
var inShapeNode = grid.GetNode(x0 + shape[i].x, z0 + shape[i].y);
if (inShapeNode == null || !DefaultITraversalProvider.CanTraverse(path, inShapeNode)) return false;
}
return true;
}

public uint GetTraversalCost (Path path, GraphNode node) {
// Use the default traversal cost.
// Optionally this could be modified to e.g taking the average of the costs inside the shape.
return DefaultITraversalProvider.GetTraversalCost(path, node);
}
}

记住这个扩展只能在grid使用,不能再layered grid graphs上使用。

那么使用的方式如下:

ABPath path = ABPath.Construct(currentPosition, destination, null);

path.traversalProvider = GridShapeTraversalProvider.SquareShape(3);

// If you are writing your own movement script
seeker.StartPath(path);

// If you are using an existing movement script (you may also want to set ai.canSearch to false)
// ai.SetPath(path);

注意,如果使用这两种grid的方法,还有一个弊端。当你要寻找的目标点完全无法到达的时候,那么寻路就会失败,而不是像通常一样找到一个离它最近的点。你可以在计算路径前使用CompuatePartial栏位为true来解决某些情况下产生的问题。

path.calculatePartial = true;

根据提供的ITraversalProvider,只要目标节点是一个可遍历的节点,它就能保证获得一个最近的节点。

原文地址:

Documentation?

arongranberg.com
图标

推荐阅读:
相关文章