(为了让各位能更容易读懂此文,此文仍会继续补充。

现在我将所有源码都存放在了Github上,请各位跟随我到Github去取源码:https://github.com/luzexi/Unity3DNavMesh

转载请注明出处:http://www.luzexi.com)

国庆闲来没事把NavMesh巩固一下。以Unity3D引擎为例写一个底层c# NavMesh寻路。因为Unity3D中本身自带的NavMesh寻路不能很好的融入到游戏项目当中,所以重写一个NavMesh寻路是个必经之路。NavMesh在很多游戏中应用广泛,不同种类的框架下NavMesh寻路发挥的淋漓尽致。与传统的A星寻路相比,NavMesh不仅减少了内存空间占有量,加快了寻路速度,还可以加入寻路角色的宽高限制,以及动态物体寻路等功能,基本上适应了大部分项目变化多端的需求。

我把写NavMesh的过程分成好几个部分,一一进行描述:

一.首先要理解NavMesh核心算法。NavMesh的核心算法就是用三角形代替传统寻路的方格,用计算拐点优化寻路路径来代替合并路径直线。 如下图1NavMesh寻路:

以及如下图2传统的方格寻路:

看到两者的差别了吧,NavMesh已三角形为寻路块,而传统以方格为寻路块。其实两者都使用A*寻路,但就是其网格生成不一样,导致当有大范围寻路时,其效率和要求也不一样。

二.NavMesh寻路中的路径优化之拐点计算。其实NavMesh中比较常用的是光照射线法,但这里不做详细介绍,光照射浅法详细内容地址:http://www.cnblogs.com/neoragex2002/archive/2007/09/09/887556.html 拐点计算优化路径就是到达目的地需要经过的一堆三角形中计算出最简洁的移动方式。其核心算法就是从当前点到另一个三角形中的点之间的线段,与这条线段相交的线段全部是路径所穿越的线段,就是拐点,把所有的拐点找出来,并得到一条最长的拐点,那个拐点就是最佳的拐点位置。

三.NavMesh类设计详解(这里只设计2D的寻路,对于3D方向的寻路,其实是可以2D寻路代替的):

1.所有类都在同一的命名空间NavMesh内 namespace NavMesh Triangle 三角形基础类 NavTriangle 寻路三角形类 (继承Triangle) Line2D 线段类 Polygon 多边形类 Seeker 寻路主算法类

----------------------------------------- 让大家久等了 ------------------------------------

在寻路前,我们需要建立MESH三角形网格,这是NAV_MESH的重点之一。

1.首先我们先要画出一个范围来确定我们的可行走范围。

2.再在可行走范围中去添加不可行走的范围。

3.我们用多个多边形Polygon代替以上的范围,也就是说,一个大的可行走Polygon内包含了若干个小的不可行走的Polygon。

这是生成MESH前我们需要知道的生成范围,然后再由这些多边形的各个顶点来生成三角形网格,三角形网格的生成算法如下:

Step 1 : 用可行走Polygon的任意一条边作为起点,将其推入堆栈列表。到Step2.

Step 2: 从堆栈中推出一条边,在所有三角形中计算出边的DT点,构成约束Delaunay三角形,到Step3。如果没有DT点就重复做Step2,直到堆栈为空就结束整个程序。

Step 3: 将所构成的三角形,另两边做如下处理:检查堆栈中是否已存在,如果存在就删除该边,如果不存在就加入到堆栈中。

生成mesh后如图:(绿色的为多边形边框,蓝色的为寻路路径,红色的为编辑器选中的多边形)

核心源码为:

/// <summary>/// 创建导航网格/// </summary>/// 所有阻挡区域</param>/// 输出的导航网格</param>/// <returns></returns>public NavResCode CreateNavMesh(List<Polygon> polyAll , ref int id , int groupid , ref List<Triangle> triAll){triAll.Clear();List<Line2D> allLines = new List<Line2D>(); //线段堆栈//Step1 保存顶点和边NavResCode initRes = InitData(polyAll);if (initRes != NavResCode.Success)return initRes;int lastNeighborId = -1;Triangle lastTri = null;//Step2.遍历边界边作为起点{Line2D sEdge = startEdge;allLines.Add(sEdge);Line2D edge = null;do{//Step3.选出计算出边的DT点,构成约束Delaunay三角形edge = allLines[allLines.Count - 1];allLines.Remove(edge);Vector2 dtPoint;bool isFindDt = FindDT(edge, out dtPoint);if (!isFindDt)continue;Line2D lAD = new Line2D(edge.GetStartPoint(), dtPoint);Line2D lDB = new Line2D(dtPoint, edge.GetEndPoint());//创建三角形Triangle delaunayTri = new Triangle(edge.GetStartPoint(), edge.GetEndPoint(), dtPoint, id++ , groupid);// 保存邻居节点// if (lastNeighborId != -1)// {// delaunayTri.SetNeighbor(lastNeighborId);// if(lastTri != null)// lastTri.SetNeighbor(delaunayTri.ID);// }//save result triangletriAll.Add(delaunayTri);// 保存上一次的id和三角形lastNeighborId = delaunayTri.GetID();lastTri = delaunayTri;int lineIndex;//Step4.检测刚创建的的线段ad,db;如果如果它们不是约束边//并且在线段堆栈中,则将其删除,如果不在其中,那么将其放入if (!Line2D.CheckLineIn(allEdges, lAD, out lineIndex)){if (!Line2D.CheckLineIn(allLines, lAD, out lineIndex))allLines.Add(lAD);elseallLines.RemoveAt(lineIndex);}if (!Line2D.CheckLineIn(allEdges, lDB, out lineIndex)){if (!Line2D.CheckLineIn(allLines, lDB, out lineIndex))allLines.Add(lDB);elseallLines.RemoveAt(lineIndex);}//Step5.如果堆栈不为空,则转到第Step3.否则结束循环} while (allLines.Count > 0);}// 计算邻接边和每边中点距离for (int i = 0; i < triAll.Count; i++){Triangle tri = triAll[i];计算每个三角形每边中点距离//tri.calcWallDistance();// 计算邻居边for (int j = 0; j < triAll.Count; j++){Triangle triNext = triAll[j];if (tri.GetID() == triNext.GetID())continue;int result = tri.isNeighbor(triNext);if (result != -1){tri.SetNeighbor(result , triNext.GetID() );}}}return NavResCode.Success;}

这里对如何计算DT点进行一个说明:

Step1. 构造三角形的外接圆,以及外接圆的包围盒

Step2. 依次访问网格包围盒内的每个网格单元:

若某个网格单元中存在可见点 p, 并且 ∠p1pp2 > ∠p1p3p2,则令 p3=p,转Step1;

否则,转Step3.

Step3. 若当前网格包围盒内所有网格单元都已被处理完,也即C(p1,p2,p3)内无可见点,则 p3 为的 p1p2 的 DT 点.

核心源码为:

/// <summary>
/// 找到指定边的约束边DT
/// </summary>
/// <param name="line"></param>
/// <returns></returns>
private bool FindDT(Line2D line, out Vector2 dtPoint)
{dtPoint = new Vector2();if (line == null)return false;Vector2 ptA = line.GetStartPoint();Vector2 ptB = line.GetEndPoint();List<Vector2> visiblePnts = new List<Vector2>();foreach (Vector2 point in allPoints){if (IsPointVisibleOfLine(line, point))visiblePnts.Add(point);}if (visiblePnts.Count == 0)return false;bool bContinue = false;dtPoint = visiblePnts[0];do{bContinue = false;//Step1.构造三角形的外接圆,以及外接圆的包围盒Circle circle = NMath.CreateCircle(ptA, ptB, dtPoint);Rect boundBox = NMath.GetCircleBoundBox(circle);//Step2. 依次访问网格包围盒内的每个网格单元://若某个网格单元中存在可见点 p, 并且 &ang;p1pp2 > &ang;p1p3p2,则令 p3=p,转Step1;//否则,转Step3.float angOld = (float)Math.Abs(NMath.LineRadian(ptA, dtPoint, ptB));foreach (Vector2 pnt in visiblePnts){if (pnt == ptA || pnt == ptB || pnt == dtPoint)continue;if (!boundBox.Contains(pnt))continue;float angNew = (float)Math.Abs(NMath.LineRadian(ptA, pnt, ptB));if (angNew > angOld){dtPoint = pnt;bContinue = true;break;}}//false 转Step3} while (bContinue);//Step3. 若当前网格包围盒内所有网格单元都已被处理完,// 也即C(p1,p2,p3)内无可见点,则 p3 为的 p1p2 的 DT 点return true;
}

Unity3D架构设计NavMesh寻路相关推荐

  1. Unity3d 引擎原理详细介绍、Unity3D引擎架构设计

    体系结构 为了更好地理解游戏的软件架构和对象模型,它获得更好的外观仅有一名Unity3D的游戏引擎和编辑器是非常有用的,它的主要原则. Unity3D 引擎 Unity3D的是一个屡获殊荣的工具,用于 ...

  2. Unity3d 引擎原理详细介绍、Unity3D引擎架构设计 - zhibolife

    时间 2014-03-24 11:18:00  博客园-所有随笔区原文  http://www.cnblogs.com/zhibolife/p/3620440.html 体系结构 为了更好地理解游戏的 ...

  3. Unity3d 引擎原理详细介绍、Unity3D引擎架构设计- zhibolife(转载分享)

    体系结构 为了更好地理解游戏的软件架构和对象模型,它获得更好的外观仅有一名Unity3D的游戏引擎和编辑器是非常有用的,它的主要原则. Unity3D 引擎 Unity3D的是一个屡获殊荣的工具,用于 ...

  4. 视频教程-Unity网络游戏架构设计-Unity3D

    Unity网络游戏架构设计 网名:海洋,CSDN社区讲师,3D游戏引擎开发者,IT讲师,计算机图形学方向研究生,曾在浙江大学CAD&CG;国家重点实验室学习.从事IT行业15年,主导或参与了1 ...

  5. Unity3D快速实现UI架构设计二

    笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,国家专利发明人;已出版书籍:<手把手教你架构3D游戏引擎>电子工业出版社和<Unity3D ...

  6. 适用于独立游戏开发的Unity3D 网络游戏架构设计

    最近在GitChat上发布了一个网络游戏框架教程,目标是带领读者搭建一个商业游戏的网络架构设计,该架构设计是游戏的核心技术,将采用 Unity 2017.2 最新版本作为开发工具.内容分为 UI 架构 ...

  7. 架构设计思维篇之结构

    avatar 结构是相关元素之间的组织和安排,它是一个很普遍的概念,我们无时无刻不在谈论着各种对象的结构,如:细胞结构.人体结构.目录结构.分子结构.组织架构.文章结构等等数不尽的结构. 那么,当人们 ...

  8. 3D引擎架构设计篇-姜雪伟-专题视频课程

    3D引擎架构设计篇-169人已学习 课程介绍         本课程是针对3D引擎架构设计,涵盖引擎的基础模块,多线程基础框架,大场景加载,地形多纹理优化技术,GPU优化渲染,物理引擎,AI算法以及A ...

  9. 简单Unity时间架构设计(克洛诺斯之匙)

    好吧,这次的题目有点标题党之嫌,提出这个设计,是因为最近玩了鬼泣,其中有一个关卡叫做"为了自己的主人",任务中,需要利用克洛诺斯之匙将时间变慢,便于通过激光镇. 使用克洛诺斯之匙之 ...

  10. 《炉石传说》架构设计赏析(4):Asset管理

    话说,经过这段时间的学习和摸索,对于Unity3D的开发思路已经基本清晰了.唯独还剩下一个AssetBundle机制还没有搞透,这个涉及到前期项目的资源规划.资源管理代码的写法,以及自动更新机制的实现 ...

最新文章

  1. 趣谈网络协议笔记-一
  2. 一句话说清楚NodeJS中module.exports和exports的区别
  3. ajax requestbody传值,ajax POST方法调用后@RequestBody属性为空
  4. PHP Redis 集群封装类
  5. html怎么让js延迟3秒跳转,JS使用setInterval或setTimeout隔几秒后跳转页面
  6. Spring Boot项目无法找到getMediaTypeMappings()方法
  7. 使用 BOOST_PROTO_DEFINE_OPERATORS 使用非原型类型 std::vector<> 对表达式进行原型化的示例
  8. 《喜剧之王》- 24/7 - Cagnet
  9. elasticsearc之mapping的介绍
  10. Vagrant 构建 Linux 开发环境
  11. 为了实现自己的美好程序人生
  12. 大型网站架构系列:缓存在分布式系统中的应用(三)
  13. android自定义底部中间突出导航栏,Android选中突出背景效果的底部导航栏功能
  14. Sentinel流控规则_关联_分布式系统集群限流_线程数隔离_削峰填谷_流量控制_速率控制_服务熔断_服务降级---微服务升级_SpringCloud Alibaba工作笔记0035
  15. Arcgis Engine 添加一个Symbol符号样式步骤
  16. [转载] C++ std::vector指定位置插入
  17. 凸优化第五章对偶 5.9 广义不等式
  18. PHP 获取YouTube视频相关信息(Youtube Api v3)
  19. visio添加外键约束
  20. mysql 实例和数据库名_oracle 实例名和服务名以及数据库名区别

热门文章

  1. APS车间排产软件实现企业生产数据可视化
  2. 用Python写个自动批改作业系统~
  3. Linux释放内存的命令
  4. 用友U8 cloud释放新动能,加速城投行业数智化转型
  5. 山东大学项目实训审计系统(七)数据可视化
  6. ESX修复Linux虚拟机重启只读模式
  7. 图文并茂说明Linux启动流程
  8. Nodejs爬虫自动爬取百度图片
  9. 【直流无刷马达的调速方法
  10. java枚举处理工具