|
本系列的教程文章基于 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);
- }
复制代码
原文链接: |
|