NavMesh,关于导航网格的创建

在《人工智能游戏编程真言》中,有详细的叙述如何建立导航网格,但是却没有一丁点的代码,而且在创建过程中,还有许多细节问题没有解决,在百度文库中有一篇关于AS3建立导航网格的文章,里面也讲的很好,但是那里使用三角形作为节点,这显然对于多边形较多的3D世界不太合适。不过里面的A*寻路算法的相关取值是值得观看的。进行了几个礼拜的编写,导航网格稍微能看我先给几张截图,让大家有个底!可以生成多边形的网格节点,而且可以自动生成相邻连接,支持鼠标直接点选节点并且显示(渲染)其和其有连接的节点,以及加入物体后分裂受影响的节点,以及裁去小面积节点,但是没有3-2合并,在后面会完善。我经过三张地图的测试,感觉还不错

这张图是原网格模型,这里用的是OBJ文件格式模型,可以用3DMAX直接导出,obj文件加载器要自己些,推荐书籍有《DirectX+游戏开发终极指南》,《focus on 3d model》,场景自己用3DMAX制作,这里的场景比较粗陋,但是可以用来测试。

最初直接把全部三角形当作节点加入NavMesh中,这里面就是画出的节点,注意这里不是用线框模式渲染出来的,而是渲染的NavMesh,整个NavMesh包含一个导航节点链表(每个节点包括一个顶点索引数组,指向后面顶点数组的位置,就是这个节点所有顶点的索引集合。还有一个法线索引,主要是为了减小存储空间,就像《真言》里说的),顶点数组(包含顶点位置,还有指向的导航节点的指针的链表,这里可以知道哪些节点包含了此顶点,这个在合并的时候很有用哦,而且真言里也讲了极大的缩小合并时间),法线数组(法线值,因为游戏中,能走的面片法线很多相同的,基本都是指向Y轴)。现在是把你的OBJ模型里面可走(表示面法线值不会很离谱)的三角形,用你的OBJ加载器把纯三角形(只包含位置,DX中要使顶点序为顺时针)一个一个的加到NavMesh中,在加之前判断法线是否过陡或向下的那种,假如法线测试通过,那么在法线数组中查找是否有相近的法线值,并设置法线索引,假如没有那么在法线数组中假如当前法线,并且设置当前节点的法线索引。然后在设置节点的顶点索引,对当前三角形的每个顶点检查是否在顶点数组中(这里使用的是两点的最小距离小于某个值我们就把它们看作相同的顶点),假如在那个当前节点加入一个顶点索引(注意顶点中的节点链表要加一个指向当前节点的指针),假如不在数组内,那么我们在顶点数组中加入当前点,并把索引加入节点的索引数组中。最后把当前节点的指针加入节点链表中。加入全部三角形之后我们渲染当前节点,得到上图的结果。全是三角形,注意这里不是线框显示的哦!

接下来是合并相邻且能构成凸体的节点,我们循环所有的顶点,由于我们的顶点保存了所有指向本顶点的节点,那么我们对顶点内的某两个节点进行查找,假如两个节点有两组相同的顶点索引那么这两个节点送入合并函数中,首先我们要看两个顶点合并后是否为凸体,这里用个最简单的方法,就是检查合并点处的两个内角相加是否大于平角(180度),假如两个合并点都没有大于平角,那么我们合并他们。这里不给出合并的具体过程,因为要点太多,所以可以留言问。

这里知道没有节点合并,那么表示合并结束,注意最外循环检查有没有节点合并。渲染后见上图,可以看见可以保证凸体(注意,千万不能有重叠三角形,否则会失败,所以要单层,或者是层高很能很小)。

上图是用鼠标拾取节点的效果,这里没有什么优化,一个一个的遍历节点,不过这里用AABB来排除了大量的节点,然后对射线与节点的所有三角形(扇形)进行检测,对有碰撞的节点进行排序,取最前的节点,返回碰撞点

这里是生成连接的结果,这里只谈如何生成有相邻边的生成,阶梯和跳跃的连接也用类似的方法实现,只是投影在某个面上进行分析而已。

关于直接相邻的方法是:每两个节点只有一个连接(这里凸体能很好的支持此特性,至于其它特殊连接,我们用手动设置方式来生成,后话),对所有节点遍历

然后检查是否已有连接,有的话直接跳过,假如没有的话计算两个节点的AABB(这里会在xz面放宽一点,你懂得)假如不相交,跳过。如果相交,检查两个节点的每条边(线段表示)相互关系,我们目的是要找出共线且重合的两个线段。假如满足那么两个节点相邻,相互加入连接。

上图是连接生成后,拾取节点(黑色)及其邻接节点(红色)。这里还需要保存邻接的两个顶点,在A*寻路时很有用。在这里你可以自己弄一下关于阶梯和跳越的连接,提示,把节点投影到xz检查节点的相邻性,不过这里对于两个节点高度差太大那么忽略,阶梯是可以直接抬脚走所有你要设定抬脚的最大高度和跳跃的最大高度。

下图是其它连接的加入:抬脚的连接、跳跃的连接

这里黑色是当前节点,红色是邻接节点,深蓝色是抬脚连接的节点

这里包含了跳跃和直接相邻连接,黑色是当前节点,红色是直接相邻的节点,蓝色是跳跃的连接节点

 

这是加入物体的原始情况

现在暂不说寻路,现在目地是在有新的物体加入世界怎么去获取分裂节点并分裂。我这里使用的是遍历每一个节点,计算节点AABB(适当拉伸)与新加物体AABB的碰撞情况,注意假如AABB高度相对于节点高度(节点内所有顶点高度的平均值)较高(例如可以蹲下走过去、可以站着走过去),那么不要分裂节点,假如AABB间有碰撞,那么分裂该节点,这里的方法可以是《真言》里的(这里会增加顶点数),也可以只取节点中点和每两个相邻顶点的三角形。我们用《真言》里的,当然《真言》里分裂节点时情况有点出入,这里我们要处理的情况是(1)节点面积太小而且节点和物体有碰撞,那么删掉节点(2)假如节点在物体AABB内,那么删掉(3)假如物体和在节点内或者节点和物体有相交,那么分裂。反复迭代,最后合并细碎节点

这是分裂节点后的图,这里没有合并细碎节点,合并的函数也是我们上面合并的函数,只要重新调用就可以合并很多小节点。

 

 

下面是几张AStar关于网格导航寻路的图

 

普通直接相邻连接寻路

 

这个是有阶梯的情况,当然由于一些节点的连接人工没有删掉所以有点问题(主要是自动生成连接时,没有考虑节点投影重合问题),但是删掉后就会有比较好的路径。

 

 

有跳跃连接的情况,注意这里的台阶都是很高的,他们的连接是跳跃的连接

你可能感兴趣的:(3D游戏菜鸟之路)