Unity寻路插件(A* Pathfinding)入门教程九:寻找路径
本系列的教程文章基于 A*Pathfinding Project 4.2.8的官网教程翻译,每一章节的原文地址都会在教程最下方给出。这篇教程的目的是提供一些参考给哪些想要自己写移动脚本,或者重新计算路径的人的。如果你只是想用内置的移动脚本,那么你可以跳过这个,转而去看一下有关目标点的的相关属性。
传送门:
用Seeker寻找路径
有很多的方法可以实现路径查找,但是最简单就是给你的移动物体绑定一个Seeker脚本,然后调用它的Seeker.StartPath()。Seeker脚本还会自动帮你处理修饰符。
注意,一个Seeker一次只能执行一个寻路,如果你在上一次的寻路未完成的时候重新请求,那么上一次的就会被中断。using Pathfinding;
public void Start () {
// Get the seeker component attached to this GameObject
var seeker = GetComponent<Seeker>();
// Start a new path request from the current position to a position 10 units forward.
// When the path has been calculated, it will be returned to the function OnPathComplete unless it was canceled by another path request
seeker.StartPath (transform.position, transform.position+transform.forward*10, OnPathComplete);
// Note that the path is NOT calculated at this stage
// It has just been queued for calculation
}
public void OnPathComplete (Path p) {
// We got our path back
if (p.error) {
// Nooo, a valid path couldn&#39;t be found
} else {
// Yay, now we can get a Vector3 representation of the path
// from p.vectorPath
}
}一个通用的认知错误是,认为调用了StartPath之后结果就已经计算出来了。这是不正确的。StartPath只是把请求放进了一个队列里,这么做的原因是如果是同一时间有大量的单位需要进行寻路计算,它们将会被分布在若干帧里去计算,以减少游戏帧率的瞬时抖动。当然如果你开启了多线程参数,它们也会在其他线程进行计算。当然也有一个接口是可以立即得到计算结果的,如下:
Path p = seeker.StartPath (transform.position, transform.position + Vector3.forward * 10);
p.BlockUntilCalculated();
// The path is calculated now或者你可以使用Unity的协程:
IEnumerator Start () {
var path = seeker.StartPath (transform.position, transform.position+transform.forward*10, OnPathComplete);
// Wait... (may take some time depending on how complex the path is)
// The rest of the game will continue to run while waiting
yield return StartCoroutine (path.WaitForPath());
// The path is calculated now
}再或者你可以使用你自己的寻路来取代Seeker提供的方法,如果这样的话,你就可以在寻路之前改变一些寻路的设定之后再进行计算。
// Create a new path object, the last parameter is a callback function
// but it will be used internally by the seeker, so we will set it to null here
// Paths are created using the static Construct call because then it can use
// pooled paths instead of creating a new path object all the time
// which is a nice way to avoid frequent GC spikes.
var p = ABPath.Construct (transform.position, transform.position+transform.forward*10, null);
// By default, a search for the closest walkable nodes to the start and end nodes will be carried out
// but for example in a turn based game, you might not want it to search for the closest walkable node, but return an error if the target point
// was at an unwalkable node. Setting the NNConstraint to None will disable the nearest walkable node search
p.nnConstraint = NNConstraint.None;
// Start the path by sending it to the Seeker
seeker.StartPath (p, OnPathComplete);看起来每次指定一个回调函数好无聊,但其实却是没必要。Seeker里有一个参数可以设置一个回调之后,每次寻路完成都去调用。当然这么做的话其实还有一个好处,就是省去了每次在堆里分配delegate的内存消耗。
但是这里有另外一个问题,就是销毁的时候记得反注册它。public void OnDisable () {
seeker.pathCallback -= OnPathComplete;
}一个比较方便的事情是,代理函数并不会只局限于一个回调,你可以在不同的脚本里都有一个关注结果的回调函数,然后通过 delegate的+或者-进行注册或者反注册来关注或者取消关注结果。
Seeker组件的出发点是持有一个单独的角色的寻路请求,所以他一次也只会请求一个路径,如果你请求了一个新的路径而另外一个请求正在执行中的话,你会得到一个警告日志,并且前一次的请求会被中断。如果你确实想取消上一次的计算请求,可以只用这个接口:Seeker.CancelCurrentPathRequest() 它不会产生警告日志。
其他方式的寻路
除了标准寻路之外,还有其他的寻路方式,比如多目标寻路(MultiTargetPath),但这个是pro版本才有的功能。它的调用也很简单,只要使用Seeker的接口就好。
// Start a multi target path, where endPoints is a Vector3[] array.
// The last parameter specifies if a path to all end points should be searched for
// or only to the closest one
seeker.StartMultiTargetPath (transform.position, endPoints, true);路径将返回一个Path的实例,但是它可以被强转化为其他类型(如:MultiTargetPath)来得到数据。完整的MultiTargetPath示例可以查示例。(译注:原文档的链接已经404了)
MultiTargetPath是Pro版本的特性,所以如果你用free版本去调用,会得到一个错误。通常,开始使用一个寻路类型很简单:
Path p = MyPathType.Construct (...); // Where MyPathType is for example MultiTargetPath
seeker.StartPath (p, OnPathComplete);使用构造函数带取代选择器,会更容易实现路径池。(path pool)
直接使用 AstarPath类
当然,你有的时候可能希望操控更多路径。那么你可以直接调用AstarPath组件。主要的使用函数是AstarPath.StarPath.
这个适用于一次请求大量的寻路。Seeker只不过是一个代理,并且一次只能计算一个,如果你硬要让它计算很多个,它只会计算最后一个。
Astar.StartPath不会自动进行后处理,但是你可以调用Seeker.PostProcess来处理。// There must be an AstarPath instance in the scene
if (AstarPath.active == null) return;
// We can calculate multiple paths asynchronously
for (int i = 0; i < 10; i++) {
// As there is not Seeker to keep track of the callbacks, we now need to specify the callback every time again
var p = ABPath.Construct(transform.position, transform.position+transform.forward*i*10, OnPathComplete);
// Start the path by calling the AstarPath component directly
// AstarPath.active is the active AstarPath instance in the scene
AstarPath.StartPath (p);
}
原文链接:
页:
[1]