查看: 2606|回复: 0
打印 上一主题 下一主题

[经验分享] A* Pathfinding Project的一些使用心得

[复制链接]

5472

主题

6

听众

1万

积分

版主

Rank: 7Rank: 7Rank: 7

纳金币
76544
精华
23

活跃会员 荣誉管理 突出贡献 优秀版主 论坛元老

跳转到指定楼层
楼主
发表于 2018-11-24 15:54:28 |只看该作者 |倒序浏览
最近在游戏开发中要做寻路。首选果断就是unity3d自带的寻路啦。方便稳定,基本功能都能满足。我们的需求也不复杂,就是一个英雄在不同的地图中探索。但是介于一个比较恶心的问题,果断放弃了它。所以,说A* Pathfinding Project之前,让我先吐槽几百字……

这个问题就是NavMesh不能动态地加载。Unity3D中每一个场景的NavMesh,如果有的话,都会对应一个名叫NavMesh.asset的文件。一一对应。假如你使用Resources.Load()方法载入它,会发现它其实就是一个NavMesh类的实例。好方便啊~~。但是Unity3D却没有提供任何方法动态替换一个场景的NavMesh。NavMesh类都是静态方法哦~。你就是载入进来,也拿它没辙,哦吼吼~。那好吧,接受这个残酷的现实。但哥不嫌麻烦,咱每一张地图都建一个场景,对应一个NavMesh,可以了吧?理论上可以。但实际不行。介于场景众多,你在操作它们的时候(移动、重命名等等),你不知什么时候,场景和它的NavMesh的关联关系就会丢失。总之,再次给Unity3D的做了一半的功能跪了。

通过百度、谷歌,以及各种机缘巧合,知道了有A* Pathfinding Project这个寻路插件,介绍的各种功能也是很强大的。A*,NavMesh和好多其它种哥也不懂的寻路算法都实现。 而且可以Save和Load预先做好的NavMesh和设置。于是哥决定尝试一下……

具体的使用,这里就不详细说了。下面说一些遇到的坑,希望对遇到的朋友有帮助。

1.如果你想要使用代码来Scan,AstarPath.active.Scan()或者ScanLoop()是不靠谱的。它们只有在AstarPath这个组件被选中时才管用。建议使用AstarPathEditor.MenuScan()。这个是位于Edit菜单内的功能选项。是可以在编辑器内调用的。

2.建议使用RecastGraph替代NavMeshGraph。两者寻路效果上没有太大差别,只是NavMesh的生成方式不一样。NavMeshGraph需要你手动指定一个Mesh。而RecastGraph是根据图层,和摆在场景里的Mesh生成的NavMesh。甚至可以在运行时生成。相比指定一个Mesh要灵活多了。而且NavMeshGraph的UI代码有问题,不能拖拽,必须从整个工程里面选。。。

3.AIPath这个很常用的脚本是有BUG的。虽然作者自己也说了吧。但没想到问题那么明显。作者为了达到最大程度地兼容,让AIPath在GameObject上,依次去找CharacterController,Rigidbody,最后是Transform,来控制移动。如果使用Rigidbody的话,会使用AddForce()方法移动。但是,却没有判断isKinematic这个属性。如果为true,AddForce()肯定没用啊。所以,自己加上个判断呗……。
  1. public virtual void Update () {
  2.         
  3.         if (!canMove) { return; }
  4.         
  5.         Vector3 dir = CalculateVelocity (GetFeetPosition());
  6.         //Rotate towards targetDirection (filled in by CalculateVelocity)
  7.         RotateTowards (targetDirection);
  8.    
  9.         if (rvoController != null) {
  10.             rvoController.Move (dir);
  11.         } else
  12.         if (navController != null) {
  13. #if FALSE
  14.             navController.SimpleMove (GetFeetPosition(),dir);
  15. #endif
  16.         } else if (controller != null) {
  17.             controller.SimpleMove (dir);
  18.         } else if (rigid != null && !rigid.isKinematic) {
  19.             rigid.AddForce (dir);
  20.         } else {
  21.             transform.Translate (dir*Time.deltaTime, Space.World);
  22.         }
  23.     }
复制代码
4.目前(3.5.1版本)A* Pathfinding Project还不支持躲避(Local Avoidance)。在之前版本,躲避是使用RVO这个插件实现的。但貌似由于版权问题,RVO被从A* Pathfinding Project中剥离了。如果非要实现躲避或动态阻挡这类功能,可以使用NavmeshCut这个组件。它只支持RecastGraph。原理就是实时地更新NavMesh,从中挖一个洞。要实现这个效果,场景中还必须挂一个TileHandlerHelper的组件。它会定时更新NavMesh。不过,如果阻挡物过多,或者更新频率过于频繁,肯定是不靠谱的。毕竟要修改Mesh也是有一定开销的。那建议就选用别的寻路插件咯……。

5.另外,TileHandlerHelper会提前对graph进行判空检查。假如,你的RecastGraph是运行时加载的,那这里肯定会报空指针。这是因为在AstarData类的DeserializeGraphsPart()方法里,对数据反序列化后,没有更新AstarData.recastGraph这个字段。在DeserializeGraphsPart()最后手动调用一下UpdateShorcuts()就好了。
  1. public void DeserializeGraphsPart (Pathfinding.Serialization.AstarSerializer sr) {
  2.             ClearGraphs ();
  3.             graphs = sr.DeserializeGraphs ();
  4.             if ( graphs != null ) for ( int i = 0; i<graphs.Length;i++ ) if ( graphs[i] != null ) graphs[i].graphIndex = (uint)i;
  5.             
  6.             userConnections = sr.DeserializeUserConnections();
  7.             //sr.DeserializeNodes();
  8.             sr.DeserializeExtraInfo();
  9.             sr.PostDeserialization();

  10.             UpdateShortcuts();
  11.         }
复制代码
不论怎样,作为一个第三方的寻路插件,A* Pathfinding Project功能已经很给力了,而且貌似还是一个人搞的(orz...)。所以有点小BUG也无可厚非了,就当锻炼自己Debug的能力了。最后,还是给A* Pathfinding Project点32个赞~~


鸣谢作者:无节操代码生成器。。。

推荐下载:http://www.narkii.com/club/thread-419932-1.html
分享到: QQ好友和群QQ好友和群 腾讯微博腾讯微博 腾讯朋友腾讯朋友 微信微信
转播转播0 分享淘帖0 收藏收藏1 支持支持0 反对反对0
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

关闭

站长推荐上一条 /1 下一条

手机版|纳金网 ( 闽ICP备08008928号

GMT+8, 2024-5-4 23:32 , Processed in 0.086928 second(s), 33 queries .

Powered by Discuz!-创意设计 X2.5

© 2008-2019 Narkii Inc.

回顶部