寻路是游戏开发中不可忽视的功能之一,NavMesh 导航网格寻路则被广泛用于在复杂的 3D 游戏世界中实现动态物体自动寻路。开发者 iwae 创作了插件 Easy NavMesh,在 Cocos Creator 3.x 中实现了完善的 3D 自动寻路功能。

导航网格(Navigation Mesh,简称 NavMesh)能够存储可行走区域的网格信息,用以在复杂的 3D 空间中实现导航寻路等功能。

导航网格是由多个多边形网格(Poly Mesh,以下简称为 Poly)组成的,即上图中的黑色描边的淡蓝色色块部分。导航网格中的寻路以 Poly 为单位,同一个 Poly 中的两点,在忽略地形高度的情况下,是可以直线到达的;如果两个点位于不同的 Poly,那么就会利用寻路算法(比如 A* 算法)算出需要经过的 Poly,再算出具体路径。

NavMesh 与路径点

为什么选择使用 NavMesh,而不用路径点进行寻路呢?

在早期 3D 游戏中,路径点是一种常用的寻路方式,但路径点不光生成和配置比较麻烦,还有一个更严重的缺点。以下图《魔兽世界》中的暴风城一角为例,当使用路径点时,AI 的行为是有限的,只能在路径点之间的连线上移动。

而 Navmesh 则可以通过计算人物的信息(如体积、行走高度、可上坡角度等),用以烘焙出 NavMesh 信息,下图淡蓝色 Poly 即代表了地图上的可以行走的区域。

通过 Navmesh,人物的移动区域将更加灵活,可以实现更复杂的 AI 行为。如下图所示,人物可以在河边任何位置摸鱼,也可以实现出城和躲避到树林当中。

再举一个更有说服力的例子,在《魔兽世界》怀旧服纳格兰的哈兰岛上,使用 NavMesh 可以涵盖整个区域,AI 的行为和表现也会比路径点更加智能,基本可以涵盖哈兰岛的各个角落(当然有些特殊的情况,比如跳跃等,我们还是可以通过路径点实现)。

NavMesh 同时还可以通过高度参数和角度参数实现角色的斜坡移动,进行上桥、下桥等行为。以《魔兽》沙塔斯城为例,参考下图 Poly 部分。

当有了这些 Poly 网格,就可以借助 A* 算法来实现寻路功能。这里使用了 bgrins 大佬的 A* 开源库[1],并翻译和修改了数学库,转换了成了 TS 版本。

传统 A* 的搜寻路线的方式是基于网格和网格中的障碍点网格,进行寻路算法,如下图所示。

在 NavMesh 生成的 Poly 块中,数据已经实现了基于地图的网格化,我们需要考虑的问题是:如何寻找最优的落地点,而不是让人物沿着下图 Poly 的黄线移动,或者直接从起点走到终点?

Easy NavMesh

Easy NavMesh 是一个用于 Cocos Creator 3.x 的轻量级网格导航库,采用了 A*+ 漏斗算法,整个库只有 40KB 不到,可以满足 H5/小游戏平台对包体大小和性能消耗的需求;同时预烘焙 NavMesh 网格信息保存为了 Json 格式,来确保加载和运行效率。

漏斗算法+A*

Easy NavMesh 采用了基于 PatrolJS[2] 实现的轻量级 TS 库,放弃了使用 recast.js+wasm 的组合,将库的大小控制在 40KB 以下。PatrolJs 采用了比较简单的漏斗算法(Simple Stupid Funnel Method)+A* 的组合,通过漏斗算法正好可以解决上一章我们遇到的 Poly 移动问题。

漏斗算法放弃了找寻 Poly 的边缘的中点(虽然这方法可以让路径变得平滑,在《生或死4》中有采取过类似方案),而是通过循环算法,不断缩小三角形漏斗的范围(可以理解为三角形的角度),这个方法计算频率高,但是更简单而有效。

下面我们通过几个实例图,演示漏斗算法通过算小漏斗角度,定位出路径点的过程。

  • 检查 A 图的第一个 Poly 左右2个点,是否在红线和蓝线组成的三角形漏斗范围内,再依次走 B 图到 D 图的3个 Poly,我们看到,三角形漏斗的角度不断变小,漏斗的范围变得越来越小。

  • 接着我们遍历 E 图和 F 图新增的2个 Poly,我们发现E图右边的端点(标记 x 的一边,为了方便理解,这里的位置都是图片上实际的位置,不是 Poly 顶点的位置)在之前 D 图生成的三角形漏斗之外,而且 D 图的红线能到达 E 图的第5个 Poly,这里漏斗就停止更新,不缩小角度。

  • E 图没有更新,接着 F 图新增的蓝线又在漏斗外,D 图的红线无法到达第6个 poly,这时候我们结束漏斗算法,把直接 A 到 D 生成的红线作为第一个路径,从 G 图开始,重新生成了一个新的三角形漏斗,开始新的漏斗算法。

有了漏斗算法之后,我们就可以使用 A* 配合漏斗,获取正确的路线。

static findPath(startPosition:Vec3, targetPosition:Vec3, zoneID:string, groupID:number) {const allNodes = this.zone[zoneID].groups[groupID];const vertices = this.zone[zoneID].vertices;let closestNode = null;let distance = Infinity;let measuredDistancefor (let i = 0, len = allNodes.length; i < len; i++) {const node = allNodes[i];measuredDistance = Vec3.squaredDistance(node.centroid, startPosition);if (measuredDistance < distance) {closestNode = node;distance = measuredDistance;}}let farthestNode = null;distance = Infinity;for (let i = 0, len = allNodes.length; i < len; i++) {const node = allNodes[i];measuredDistance = Vec3.squaredDistance(node.centroid, targetPosition);if (measuredDistance < distance &&nUtils.isVectorInPolygon(targetPosition, node, vertices)) {farthestNode = node;distance = measuredDistance;}}// If we can't find any node, just go straight to the targetif (!closestNode || !farthestNode) {return null;}//A*寻路算法const paths = Astar.search(allNodes, closestNode, farthestNode);const getPortalFromTo = function (a, b) {for (let i = 0; i < a.neighbours.length; i++) {if (a.neighbours[i] === b.id) {return a.portals[i];}}};//使用漏斗算法,结算出最佳路线// Got the corridor,pull the ropeconst channel = new Channel();channel.push(startPosition);for (let i = 0; i < paths.length; i++) {const polygon = paths[i];const nextPolygon = paths[i + 1];if (nextPolygon) {const portals = getPortalFromTo(polygon, nextPolygon);channel.push(vertices[portals[0]], vertices[portals[1]]);}}channel.push(targetPosition);channel.stringPull();// Return the path, omitting first position (which is already known).return channel.path;
}

预烘焙方案

为了减少数据开销,Easy NavMesh 使用预烘焙方案,提前把 Navmesh 数据以 Json 格式导出,方便跨平台支持。

Easy NavMesh 提供了多种导出工具,支持 recast.js 动态导出 Json 数据、Blender Navmesh 导出 OBJ 等,开发者可以根据需求来选择:

  • 支持 recast.js 导出 Json 数据。可以使用 Cocos Creator 提供的 NavMesh 导出面板,导出 NavMesh 信息为 Mesh Json 格式;也可以通过面板把整个场景导出为 OBJ,用 Blender 等工具进行 NavMesh 信息处理。

  • 支持 Blender Navmesh 导出 OBJ 格式,再使用 Python 脚本进行转换(该脚本基于 OBJ to three.js Json Mesh 进行了修改,剔除了无用的 normal、UV 等信息,原作者:AlteredQualia[3])。

    引擎解析这些 Json 后,我们就可以使用 Json 中的定点信息和面的信息,构建导航网格的数据。为了灵活适配,这些网格信息也可以放在服务器或者通过脚本新建对象来储存。

ClampSteped

支持基于 ClampSteped 的移动。借助 ClampSteped,可以使用 NavMesh 烘焙好的网格信息,无需使用 Cocos 自带的 Cannon 或者 Ammo 物理引擎,人物也无需添加刚体,是基于 ThreeJs 的 Plane 和 Triangle 库的数学方法实现的(Easy NavMesh 移植了 ThreeJS 的 Plane 和Triangle 类,重新使用 Cocos 的 Vec3 Mat4 数学库进行了编译)。

ClampSteped 目前还是一个不太成熟的方案,现在只提供了基础 Demo,后续的优化需要依靠各位大佬,使用 ClampSteped 替代物体,需要注意设置好物体半径,太小容易导致穿模,太大容易穿墙。

其他优化

Easy NavMesh 剔除了第三方 Vector3 的数学库,使用 Cocos 的 Vec3 替代,避免了 Cocos 的 Vec3 类和其他 V3 坐标类之间的2次转换。

原库使用了 OBJ 格式,或者Three.js 的Json Model,这里一步到位,可以直接通过 Cocos 导出 Json 信息,信息提前做了小数点精度保留,减少了 Json 体积和处理 OBJ 信息的运算量,Size Does Matter~

常见问题 QA

Q1: 为什么有时候路径不是视觉上最近的?

A:在 NavMesh 中 A* 算法是以 Poly 作为网格的,在复杂的网格地形中,到最近的距离点的 Poly 组数反而可能比较多。

Q2: 为什么 ClampStep 容易穿墙?

A:目前数学库都是从 Three.js 翻译的,这个 ClampStep 在 Three 的项目中使用也较少,可以参考的 Demo 不多,需要进一步适配 Cocos,后续版本会不停调优。

Q3:后续有优化计划吗?

A:Easy NavMesh 会持续更新维护。首先我计划加入更多基于 NavMesh 的游戏 Demo,比如捉迷藏;此外优化 ClampSteped,实现基于 ClampSteped 的 FPS NavMesh Demo(下次一定!)。

资源链接

Easy NavMesh 现已发布,限时优惠5折还剩23小时,点击文末【阅读原文】至 Cocos Store 即可获取,大佬们多多支持~更多详细说明见论坛,也欢迎大家到帖子里交流反馈!

Easy NavMesh 下载-Store

https://store.cocos.com/app/detail/3641

论坛专贴

https://forum.cocos.org/t/topic/132913

参考资料

[1] A* 开源库丨bgrins

https://github.com/bgrins/javascript-astar

[2] PatrolJS

https://github.com/nickjanssen/PatrolJS

[3] AlteredQualia

https://alteredqualia.com/

  • Creator H5游戏开发PDF免费下载(800+页)

  • 2021年他开发19款插件工具!款款口碑爆棚

  • 为什么能持续成交,我究竟是做对了什么?

  • 炫酷枪火打击视频+图文+源码!

  • 这样学Shader可以少走几年弯路,看完我信了……

屌炸天,既要小又要快!完美支持小游戏包体,这款神级插件怎么做到的?相关推荐

  1. 游戏包体大小的那些事

    手游行业正在快速发展,一些老的观点也正在随着行业经验的积累,逐渐变得更加完善和成熟.比如手游包体必须小才好,这个观点,其实并非是一个完整的观点.今天我们就来聊聊关于包体大小那些事儿. 制作人更关心品质 ...

  2. unity第三人称射击游戏_在游戏上第3部分完美的信息游戏

    unity第三人称射击游戏 Previous article 上一篇文章 The economics literature distinguishes the quality of a game's ...

  3. 在游戏上第3部分完美的信息游戏

    Previous article 上一篇文章 The economics literature distinguishes the quality of a game's information (p ...

  4. Towxml 3.0来了,让小程序完美支持Markdown

    Towxml 3.0来了,让小程序完美支持Markdown 查看全文 http://www.taodudu.cc/news/show-805307.html 相关文章: 数据接口的登录态校验以及JWT ...

  5. 让小程序完美支持Markdown,最详细教程来了

    最近在做需要展示文章详情的功能,详情打算用Markdown展示,发现微信小程序在支持Markdown方面不是很友好,小蛋我无意中发现一个好用的组件,Towxml,完美支持Markdown,下面就带大家 ...

  6. 宝藏级UI组件库:FirstUI,微信小程序版+uniapp版双版至V1.5.0,完美支持vue3

    First UI(https://www.firstui.cn/)是一套超高性能.超高颜值的移动端UI综合解决方案,包含业内顶尖的组件库.强大的功能库.丰富精美的模板库,提供uni-app(完美支持n ...

  7. uni-app中使用Towxml 3.0,小程序完美支持Markdown

    简介   Towxml 是一个可将HTML.Markdown转为微信小程序WXML(WeiXin Markup Language)的渲染库.用于解决在微信小程序中Markdown.HTML不能直接渲染 ...

  8. PHP生成PDF完美支持中文,解决TCPDF乱码

    PHP生成PDF完美支持中文,解决TCPDF乱码 2011-09-26 09:04 418人阅读 评论(0) 收藏 举报 phpfontsheaderttfxhtml文档 PHP生成PDF完美支持中文 ...

  9. 差之毫厘:etcd 3 完美支持 HTTP 访问

    etcd 升级到 3.x 版本后,其对外 API 的协议从普通的 HTTP1 切换到了 gRPC.为了兼顾那些不能使用 gRPC 的特殊群体,etcd 通过 gRPC-gateway 的方式代理 HT ...

最新文章

  1. 宫崎骏动画里的15对情侣,你最喜欢哪一对?
  2. ndroid网络(4):HttpClient必经之路----使用线程安全的单例模式HttpClient,及HttpClient和Application的融合...
  3. POJ1785(笛卡尔树的构造)
  4. [html] 如何解决input在Firefox和Chrome中高度不一致的问题?
  5. php3.2.3 升级,thinkphp3.2.3 升级到3.2.4时出错问题
  6. 对于一个html元素,有几种方法修改样式方法的优先级,HTMLCSS常见面试题及疑难解答...
  7. C++ 运算符重载的原理
  8. 【披着递推皮的动态规划】 山区建小学 题解
  9. 【个人笔记】OpenCV4 C++ 快速入门 17课
  10. php环境搭建(php5.5.8+apache2.4)
  11. CCNA实验:实验一:思科设备基本配置
  12. M1芯片制霸苹果生态?2021 年 Apple 春季新品发布会全记录
  13. linux-LNMP一键安装Error: MySQL install failed. Error: PHP install failed
  14. cs起源 css武器大合集,cs起源幻彩武器包mod
  15. bitbake的原理介绍, 使用方法, 编译脚本的编写方法
  16. Hexo 搭建个人博客学习笔记(4):个性化主题Next个性化配置
  17. cf-645D. Robot Rapping Results Report(判断是否是惟一的拓扑序列)
  18. 导出excel file-saver XLSX
  19. 青岛大学计算机考研好考么,青岛大学考研难吗?一般要什么水平才可以进入?...
  20. rn react native PanResponder手势动画 实现窗口拖动 滑动动画 Animated

热门文章

  1. vue cli 3x降到2x
  2. 情境化——让活动页面击中用户
  3. 产品和商品的区别 sku spu的区别 什么事关键属性?什么事商品属性?
  4. Excel宏(VBA)密码破解
  5. GITHUB上传文件方法教程
  6. 国内cPanel空间试用手记
  7. PHP 那个版本更好、更稳定?
  8. 基于Sobel算法的边沿检测设计与实现
  9. zy是什么意思网络_网络资讯:ps 是什么意思
  10. “三国时代”:公募销售保有规模前100排名出炉