最近在游戏开发中要做寻路。首选果断就是Unity3D自带的寻路啦。方便稳定,基本功能都能满足。我们的需求也不复杂,就是一个英雄在不同的地图中探索。但是介于一个比较恶心的问题,果断放弃了它。所以,说A* Pathfinding Project之前,让我先吐槽几百字……
这个问题就是NavMesh不能动态地加载。Unity3D中每一个场景的NavMesh,如果有的话,都会对应一个名叫NavMesh.asset的文件。一一对应。假如你使用Resources.Load()方法载入它,会发现它其实就是一个NavMesh类的实例。好方便啊~~。但是Unity3D却没有提供任何方法动态替换一个场景的NavMesh。NavMesh类都是静态方法哦~。你就是载入进来,也拿它没辙,哦吼吼~。那好吧,接受这个残酷的现实。但哥不嫌麻烦,咱每一张地图都建一个场景,对应一个NavMesh,可以了吧?理论上可以。但实际不行。介于场景众多,你在操作它们的时候(移动、重命名等等),你不知什么时候,场景和它的NavMesh的关联关系就会丢失。总之,再次给Unity3D的做了一半的功能跪了。
通过百度、谷歌,以及各种机缘巧合,知道了有A* Pathfinding Project这个寻路插件。看它高大上的官网(http://arongranberg.com/astar/),介绍的各种功能也是很强大的。A*,NavMesh和好多其它种哥也不懂的寻路算法都实现。 而且可以Save和Load预先做好的NavMesh和设置。于是哥决定尝试一下……
具体的使用,这里就不详细说了。看它自带的ExampleScenes以及官方教程(http://arongranberg.com/astar/docs/index.php)就可以了。下面说一些遇到的坑,希望对遇到的朋友有帮助。
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 11 UpdateShortcuts(); 12 }
不论怎样,作为一个第三方的寻路插件,A* Pathfinding Project功能已经很给力了,而且貌似还是一个人搞的(orz...)。所以有点小BUG也无可厚非了,就当锻炼自己Debug的能力了。最后,还是给A* Pathfinding Project点32个赞~~