找回密码
 立即注册
查看: 643|回复: 0

Unity寻路插件(A* Pathfinding)入门教程九:寻找路径

[复制链接]
发表于 2021-1-7 09:41 | 显示全部楼层 |阅读模式
本系列的教程文章基于 A*Pathfinding Project 4.2.8的官网教程翻译,每一章节的原文地址都会在教程最下方给出。
这篇教程的目的是提供一些参考给哪些想要自己写移动脚本,或者重新计算路径的人的。如果你只是想用内置的移动脚本,那么你可以跳过这个,转而去看一下有关目标点的的相关属性。
传送门:
用Seeker寻找路径

有很多的方法可以实现路径查找,但是最简单就是给你的移动物体绑定一个Seeker脚本,然后调用它的Seeker.StartPath()。Seeker脚本还会自动帮你处理修饰符。
注意,一个Seeker一次只能执行一个寻路,如果你在上一次的寻路未完成的时候重新请求,那么上一次的就会被中断。
  1. using Pathfinding;
  2. public void Start () {
  3.     // Get the seeker component attached to this GameObject
  4.     var seeker = GetComponent<Seeker>();
  5.     // Start a new path request from the current position to a position 10 units forward.
  6.     // When the path has been calculated, it will be returned to the function OnPathComplete unless it was canceled by another path request
  7.     seeker.StartPath (transform.position, transform.position+transform.forward*10, OnPathComplete);
  8.     // Note that the path is NOT calculated at this stage
  9.     // It has just been queued for calculation
  10. }
  11. public void OnPathComplete (Path p) {
  12.     // We got our path back
  13.     if (p.error) {
  14.         // Nooo, a valid path couldn't be found
  15.     } else {
  16.         // Yay, now we can get a Vector3 representation of the path
  17.         // from p.vectorPath
  18.     }
  19. }
复制代码
一个通用的认知错误是,认为调用了StartPath之后结果就已经计算出来了。这是不正确的。StartPath只是把请求放进了一个队列里,这么做的原因是如果是同一时间有大量的单位需要进行寻路计算,它们将会被分布在若干帧里去计算,以减少游戏帧率的瞬时抖动。当然如果你开启了多线程参数,它们也会在其他线程进行计算。当然也有一个接口是可以立即得到计算结果的,如下:
  1. Path p = seeker.StartPath (transform.position, transform.position + Vector3.forward * 10);
  2. p.BlockUntilCalculated();
  3. // The path is calculated now
复制代码
或者你可以使用Unity的协程:
  1. IEnumerator Start () {
  2.     var path = seeker.StartPath (transform.position, transform.position+transform.forward*10, OnPathComplete);
  3.     // Wait... (may take some time depending on how complex the path is)
  4.     // The rest of the game will continue to run while waiting
  5.     yield return StartCoroutine (path.WaitForPath());
  6.     // The path is calculated now
  7. }
复制代码
再或者你可以使用你自己的寻路来取代Seeker提供的方法,如果这样的话,你就可以在寻路之前改变一些寻路的设定之后再进行计算。
  1. // Create a new path object, the last parameter is a callback function
  2. // but it will be used internally by the seeker, so we will set it to null here
  3. // Paths are created using the static Construct call because then it can use
  4. // pooled paths instead of creating a new path object all the time
  5. // which is a nice way to avoid frequent GC spikes.
  6. var p = ABPath.Construct (transform.position, transform.position+transform.forward*10, null);
  7. // By default, a search for the closest walkable nodes to the start and end nodes will be carried out
  8. // 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
  9. // was at an unwalkable node. Setting the NNConstraint to None will disable the nearest walkable node search
  10. p.nnConstraint = NNConstraint.None;
  11. // Start the path by sending it to the Seeker
  12. seeker.StartPath (p, OnPathComplete);
复制代码
看起来每次指定一个回调函数好无聊,但其实却是没必要。Seeker里有一个参数可以设置一个回调之后,每次寻路完成都去调用。当然这么做的话其实还有一个好处,就是省去了每次在堆里分配delegate的内存消耗。
但是这里有另外一个问题,就是销毁的时候记得反注册它。
  1. public void OnDisable () {
  2.     seeker.pathCallback -= OnPathComplete;
  3. }
复制代码
一个比较方便的事情是,代理函数并不会只局限于一个回调,你可以在不同的脚本里都有一个关注结果的回调函数,然后通过 delegate的+或者-进行注册或者反注册来关注或者取消关注结果。
Seeker组件的出发点是持有一个单独的角色的寻路请求,所以他一次也只会请求一个路径,如果你请求了一个新的路径而另外一个请求正在执行中的话,你会得到一个警告日志,并且前一次的请求会被中断。如果你确实想取消上一次的计算请求,可以只用这个接口:Seeker.CancelCurrentPathRequest() 它不会产生警告日志。
其他方式的寻路

除了标准寻路之外,还有其他的寻路方式,比如多目标寻路(MultiTargetPath),但这个是pro版本才有的功能。它的调用也很简单,只要使用Seeker的接口就好。
  1. // Start a multi target path, where endPoints is a Vector3[] array.
  2. // The last parameter specifies if a path to all end points should be searched for
  3. // or only to the closest one
  4. seeker.StartMultiTargetPath (transform.position, endPoints, true);
复制代码
路径将返回一个Path的实例,但是它可以被强转化为其他类型(如:MultiTargetPath)来得到数据。完整的MultiTargetPath示例可以查示例。(译注:原文档的链接已经404了)
MultiTargetPath是Pro版本的特性,所以如果你用free版本去调用,会得到一个错误。
通常,开始使用一个寻路类型很简单:
  1. Path p = MyPathType.Construct (...);    // Where MyPathType is for example MultiTargetPath
  2. seeker.StartPath (p, OnPathComplete);
复制代码
使用构造函数带取代选择器,会更容易实现路径池。(path pool)
直接使用 AstarPath类

当然,你有的时候可能希望操控更多路径。那么你可以直接调用AstarPath组件。主要的使用函数是AstarPath.StarPath.
这个适用于一次请求大量的寻路。Seeker只不过是一个代理,并且一次只能计算一个,如果你硬要让它计算很多个,它只会计算最后一个。
Astar.StartPath不会自动进行后处理,但是你可以调用Seeker.PostProcess来处理。
  1. // There must be an AstarPath instance in the scene
  2. if (AstarPath.active == null) return;
  3. // We can calculate multiple paths asynchronously
  4. for (int i = 0; i < 10; i++) {
  5.     // As there is not Seeker to keep track of the callbacks, we now need to specify the callback every time again
  6.     var p = ABPath.Construct(transform.position, transform.position+transform.forward*i*10, OnPathComplete);
  7.     // Start the path by calling the AstarPath component directly
  8.     // AstarPath.active is the active AstarPath instance in the scene
  9.     AstarPath.StartPath (p);
  10. }
复制代码

原文链接:
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-12-24 04:10 , Processed in 0.188565 second(s), 25 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表