本系列的教程文章基於 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
圖標

推薦閱讀:
相关文章