1011shl 发表于 2020-11-25 13:46

Unity寻路插件(A* Pathfinding)入门教程十:使用节点(Using nodes)

本系列的教程文章基于 A*Pathfinding Project 4.2.8的官网教程翻译,每一章节的原文地址都会在教程最下方给出。
路径查找都是基于Graph的节点进行。每个节点都以一些特殊的方式彼此联系。比如栅格类的(grid)每个节点都和周围4方向或者8方向的节点相连。而对于nav/recast类型的graph则每个三小姐就是一个节点,并且它们之间是通过三角形中间的节点相联系。
本篇不会讲寻路算法是怎么实现的,但是可以参照一下这个动图来大致了解一下:
如果可以爬墙的同学可以看这里的维基百科:
扩展阅读:
寻找某个位置最近的节点

很多时候,你想要知道给定世界里的一个点,离它最近的节点是哪个。通常我们都是交给插件内置的移动脚本去处理,你根本不需要关心,但是如果你深入的去了解的话,你会发现这会对你了解插件有很大的帮助作用。
// Find the closest node to this GameObject's position
GraphNode node = AstarPath.active.GetNearest(transform.position).node;

if (node.Walkable) {
    // Yay, the node is walkable, we can place a tower here or something
}
在某些情况下,你可能还会关注一些特定的节点,比如最近的“可行走”节点。这就需要使用到NNConstraint类了。本篇文章就是使用默认的 NNConstraint。(之所以称它为默认的,是因为在没有特殊需求的情况下,它能满足正常的寻路调用。)
GraphNode node = AstarPath.active.GetNearest(transform.position, NNConstraint.Default).node;
当然你也可以自定义:
var constraint = NNConstraint.None;

// Constrain the search to walkable nodes only
constraint.constrainWalkability = true;
constraint.walkable = true;

// Constrain the search to only nodes with tag 3 or tag 5
// The 'tags' field is a bitmask
constraint.constrainTags = true;
constraint.tags = (1 << 3) | (1 << 5);

var info = <span class="n">AstarPath.active.GetNearest(transform.position, constraint);
var node = info.node;
var closestPoint = info.position;
这些搜索区域的约束范围不会是无限的,但是可以是相当巨大的。最大的寻路距离可以再A*的面板Inspector -> Settings -> Max Nearest Distance 中修改,或者使用AstarPath.maxNearestNodeDistance。如果最近的节点比设置的距离要大,就会返回null。
NNConstraint 还有其他的重要参数,可以看这里最近的节点

你可能注意到了,上面从GetNearest方法获取最近节点,我们必须得访问这些节点字段。这个方法会返回一个NNInfo的结构,并包含了两个字段。第一个字段是node指向了最近的那个node节点(如果没有可能为null)。第二个position则包含了距离查询点最近的那个点。在栅格类型的graph (grid graph)上,节点会被看做是一个方格。因此,position字段包含的是方格内与查询点最近的点。在基于导航网格的 graphs上(nav/recast)position记录的是指向最近三角形的最近点,路点类型的因为没有行走表面,所以会直接指向节点的位置。
var info = AstarPath.active.GetNearest(transform.position);
var node = info.node;
var closestPoint = info.position;
节点连接

每个节点都存储了它可以连接的其他节点。至于如何连接则取决于graph的类型。栅格类的,只需要一个byte就可以存储与他相邻的所有节点。和其他类型的graph相比较,他的存储内存可能要小80倍!
你可以通过GetConnerctions来访问所有的可连接节点。这个方法提供了一个代理(delegate)用来连接每个节点的调用,并抽象出底层表示。
GraphNode node = ...;
// Draw a line to all nodes that this node is connected to
node.GetConnections(otherNode => {
    Debug.DrawLine((Vector3)node.position, (Vector3)otherNode.position);
});
你可以增加或者删除自定义的连接节点,获取更多信息,查看这里:节点属性

有一些节点的属性,我们还是比较感兴趣的:
第一,每个节点都有一个Position字段,表示这个Node在世界空间的坐标。用一个Int3而不是一个Vector3存储,但是你其实可以很方便的在这两者之间进行转换:
GraphNode node = ...;
Vector3 v3position = (Vector3)node.position;
node.position = (Int3)v3position;
你还可以访问其他的属性,比如 这个节点是否可以行走,它的tag或者是penalty。
bool walkable = node.Walkable;
uint tag = node.Tag;
uint penalty = node.Penalty;
有一些辅助类的方法帮助寻找节点比如获取节点上随机一点:
Vector3 randomPoint = node.RandomPointOnSurface();
Node的表面区域:(路点类型为Null)
float surfaceArea = node.SurfaceArea();
三角面节点(TriangleMeshNode)

网格节点用于nav或者recast类型的graph。
你可以通过一个node得到一个最近的点,它可以是XZ平面上的,也可以是3D空间里的。
MeshNode node = ...;
Vector3 closestPoint = node.ClosestPointOnNode(somePoint);
Vector3 closestPoint = node.ClosestPointOnNodeXZ(somePoint);
你还可以检查一个点是否在一个node内。
if (node.ContainsPoint(somePoint)) {
    // The point is inside the node
}
你可以获取一个node所代表的三角形的顶点:
// Individually
Int3 v0 = node.GetVertex(0);
Int3 v1 = node.GetVertex(1);
Int3 v2 = node.GetVertex(2);
// Or combined (faster)
Int3 v0, v1, v2;
node.GetVertices(out v0, out v1, out v2);
可达性(Reachability)

通常,可达性用来检查一个角色是否可以到达一个特定的节点。要记住,一条路径的计算总是默认去找离目标点最近的那个点。所以,大部分时候你其实不太需要关心这个。路径的查找系统会根据connected组件来预计算这些节点点可以之间的连通性来哪些点能够到达。这在场景视窗里会用不同的颜色区分(当Graph Coloring的选项设置为 area的时候)。每个节点的 Area字段都被设置成一个连通组件的索引值。如果2个节点有相同的area,则表示他们之间有有效路径。你也可以使用代码检查它们:
var node1 = AstarPath.active.GetNearest(somePoint1);
var node2 = AstarPath.active.GetNearest(somePoint2);
if (PathUtilities.IsPathPossible(node1, node2)) {
    // There is a valid path between the nodes
}
在文档和代码的各个部分,计算这些区域或者连接组件的过程被称为:flood filling
你可以向IsPathPossible方法里填充一些mask标签或者几点列表。
你可以找出一个给定节点的所有可到达节点。
List<GraphNode> reachableNodes = PathUtilities.GetReachableNodes(node);
有好几种方法可以找到一个节点的所有可到达节点,更多的信息参见:


原文链接:
页: [1]
查看完整版本: Unity寻路插件(A* Pathfinding)入门教程十:使用节点(Using nodes)