由于我们的项目采用的寻路解决方案是:客户端使用 unity 原生的寻路系统,服务器采用 RecastNavigation 系统,而服务器的寻路数据来自于从 unity 导出的,所以理论上两边的寻路结果应该是一样的,可事实上并非如此,unity 无论如何寻路,都能表现出比较自然的结果,但是服务器却有时会出现比较奇怪的结果。

  由以上三张截图可以看到,起始点和终点稍微有点变化,结果就会出现较大的差异,寻路结果往往会“拐弯”,这种结果势必是不能接受的,unity 里不会出现这种情况,经过多次试验并仔细查看和分析,发现是因为寻路网格里出现了过于“细长”的三角形导致的,那如何修改呢,我在官方论坛里向作者(Mikko Mononen)提问(原帖地址点这里,自行解决 FanQiang 问题):1.我觉得 unity 的寻路系统是基于原作者的 RecastNavigation 系统编写的;2.这种结果是否是由“细长”三角形导致的,该如何解决呢?

  很开心作者很快就恢复了:1.unity 的寻路系统确实是基于 RecastNavigation 编写的,但是 Detour 系统已经被深度改写了,(后来发现作者的 twitter 简介是 unity 的 ai 程序员);2.这个问题确实由于“细长”三角形引起的,NavMesh 系统当遇到特别宽或者三边长度比特别大的三角形时会出问题。而作者很久前就在自己的博客里叙述过这个问题,并给出了解决方案,博客地址:

 
解决方法是:改变A*寻路结果中节点的放置处理,NavMesh 系统里默认使用多边形边的中点来作为路径通过的点,如果想要好看的结果,应该使用距离最近的点,即你有 A, A 两个点在一条线段 L 的两边,那么这个点应该是线段 AB 和线段 L 的交点。
 
 if (neighbourNode->flags == )
{
float sa[], sb[];
getPortalPoints(bestRef, bestPoly, bestTile,
neighbourRef, neighbourPoly, neighbourTile,
sa, sb);
float t = 0.5f;
dtDistancePtSegSqr2D(endPos, sa,sb, t);
t = dtClamp(t, 0.1f, 0.9f);
dtVlerp(neighbourNode->pos, sa,sb, t);
}

这段代码在:

 
也可以使用 "bestNode->pos" 替换 "endPos",这样就可以使用“最近”的点来替换当前 A* 里的“终点”,同时作者指出,像我这种客户端和服务器的解决方案都只能不断的调节调试去优化视觉上的结果,并不存在一个百分百准确的方案,不过很开心的是,这样修改完一实验,果然结果“正常”了,现在客户端和服务器的寻路90%的情况都能保持一致,极个别的情况还是会有差异,不过结果不会再特别“奇怪”,所以已经很满足了,太感谢原作者 mikko 同学了。
 
  题外话,上面这小段代码原来使用的是 getEdgeMidPoint 来求中点,作者用的 dtDistancePtSegSqr2D 这个函数是用来计算点 endPos 到线段 sa 和 sb 的垂直距离直线的交点 t,t 的数值就是线段 sa-t 和 线段 t-sb 的长度比值。当然这个结果不错了,但是还不是最好的,作者的原博客使用的是 dtIntersectSegSeg2D(ap, aq, bp, bq, s, t),计算的是线段 ap-aq 和线段 bp-bq 交点 m 在两条线段上的比值 s = ||ap-m|| / ||m-aq||,t = ||bp-m|| / ||m-bq||,这样一来计算出来的才是真正的最近距离,两点之间直线最短嘛;这个函数非常有意思,内部使用了 PertDotProduct 这个数学公式,这个才是最有意思的,但不太容易理解,就是向量 A 和向量 B 的 PertDotProduct 结果为:A 的法向量和向量B 的点乘积,几何意义是两条线段组成的平行四边形的面积(||a||.||b||.sina),而两条线段的交点分别在每个线段上的比值,正好可以通过计算 PertDotProduct 的比值来获得,因为他们组成了一个共底边的平行四边形,所以面积比等于高之比,而高正好分别就是其中一条线段的起点到交点的距离和起点到终点的距离。我自己的解决方案是选用的最后一种 dtIntersectSegSeg2D,效果也是最好的。
  最近一直太忙,都没更新,其实积累了很多心得在我的印象笔记里,慢慢补充到这里吧。
 
 

关于 RecastNavigation 寻路结果异常的问题。的更多相关文章

  1. 从 NavMesh 网格寻路回归到 Grid 网格寻路。

    上一个项目的寻路方案是客户端和服务器都采用了 NavMesh 作为解决方案,当时的那几篇文章(一,二,三)是很多网友留言和后台发消息询问最多的,看来这个方案有着广泛的需求.但因为是商业项目,我无法贴出 ...

  2. 如何制作RTS游戏的寻路系统?

    Q1:我们在做一个RTS游戏,开始用的是Unity自带的NavMesh的寻路,但发现这个并不适合RTS多人寻路,因为总会出现阻挡和闪跳的问题.看Asset Store上的A* path插件评论说在碰撞 ...

  3. CritterAI与Recast Navigation寻路

    版权声明:本文为博主吴欣伟原创文章,未经博主允许不得转载. 前言 这篇文章写于去年,由于工作需要,故写出这个研究文档,发现网上有关此寻路库的中文资源十分稀少,故发布出来与诸位共享交流,如文中有不对之处 ...

  4. RecastNavigation(3D场景建模、网格导航)

    一.RecastNavigation详解 RecastNavigation定义: RecastNavigation是一个导航寻路工具集,使用邻接的凸多边形集合描述一个3D场景,A*寻路算法使3D场景的 ...

  5. Recastnavigation 创建 off-mesh link 的潜规则

    Recastnavigation 在创建off-mesh link 时,发现有的off-mesh link 无法寻路(虽然在地图上能看到off-mesh link 的连线   在Google Grou ...

  6. alias导致virtualenv异常的分析和解法

    title: alias导致virtualenv异常的分析和解法 toc: true comments: true date: 2016-06-27 23:40:56 tags: [OS X, ZSH ...

  7. ASP.NET Core应用的错误处理[2]:DeveloperExceptionPageMiddleware中间件如何呈现“开发者异常页面”

    在<ASP.NET Core应用的错误处理[1]:三种呈现错误页面的方式>中,我们通过几个简单的实例演示了如何呈现一个错误页面,这些错误页面的呈现分别由三个对应的中间件来完成,接下来我们将 ...

  8. 记一次tomcat线程创建异常调优:unable to create new native thread

    测试在进行一次性能测试的时候发现并发300个请求时出现了下面的异常: HTTP Status 500 - Handler processing failed; nested exception is ...

  9. 使用JSONObject.fromObject的时候出现“There is a cycle in the hierarchy”异常 的解决办法

    在使用JSONObject.fromObject的时候,出现“There is a cycle in the hierarchy”异常.   意思是出现了死循环,也就是Model之间有循环包含关系: ...

随机推荐

  1. Mysql笔记【1】-数据库的基本操作(创建/删除)

    1.创建数据库 创建数据库(如果存在,则报错) #创建名称为test的数据库 create database test 查询创建完的数据库 show databases 2.删除数据库 删除数据库(如 ...

  2. CentOS7设置IP地址

    root权限下cd到/etc/sysconfig/network-scripts, vi ifcig-em1 TYPE=Ethernet BOOTPROTP=static NAME=em1 UUID= ...

  3. AJAX 一些常用方法

    abort() 停止当前请求getAllResponseHeaders() 返回包含HTTP请求的所有响应头信息,其中响应头包括Content-Length,Date,URI等内容.getRespon ...

  4. SQL Proc(存储过程)/tran(事物)

    存储过程好比C#方法 1.事物写在过程里面,直接调用存储过程 1.1没有参数的过程 /*transaction事物,procedure存储过程*/ create proc CopyTable_1_10 ...

  5. Python冒泡排序

    冒泡排序,顾名思义,按照一定的规则,把数据一直排下去 直接上代码 import random def bubblesort(data): for i in range(len(data)-1,1,-1 ...

  6. linux vi 使用

    vi 有一般模式和编辑模式 如vi test.txt 是首先进入的一般模式,一般模式下只能进行复制.删除.粘贴文件数据, 在一般模式下按i .I.a.A.o.O 都能进入编辑模式,按下不同的键进入编辑 ...

  7. bzoj4038: 医疗援助

    Description 一只带着先进设备和药物的医疗团队来到了埃博拉病毒疫区的某个非洲国家.这个国家有n个村庄,均坐落在该国唯一的一条公路旁,n个村庄依次标号为1,2,…n.第i个村庄有a_i个埃博拉 ...

  8. html多行注释方法

    Html单行:<!-- -->多行:<!-- -->javascript单行://多行:/* */Vbscript单行:'多行:'ASP <% %>中: 单行:'  ...

  9. structDemo1

    structDemo1 # include <iostream.h> # include <malloc.h> enum EType{ One = ,Tow,Three }; ...

  10. 事件tou

    #define EV_TIMER_RESOLUTION 1 /* 1 msec */ #define EV_READ_EVENT EPOLLIN #define EV_WRITE_EVENT EPOL ...