123456811 发表于 2021-1-11 10:41

Unity寻路插件(A* Pathfinding)进阶教程二:局部回避(RVO)

本系列的教程文章基于 A*Pathfinding Project 4.2.8的官网教程翻译,每一章节的原文地址都会在教程最下方给出。
本篇讲述如何使用A*功能的 局部回避。
A*Pathfinding插件所提供的局部回避功能都是基于RVO的(Reciprocal
):反速度障碍(译注:本人喜欢叫 动态规避)。该方法用梯度降低(
)的方式寻找最优的速度。
它比较适用于一些游走的人类行为,而不太适合诸如赛车之类不能迅速改变速度的行为。
这是Pro版本专属功能,free版本里可能会找不到相关的类、方法或者变量。概览

RVO分为两部分。首先就是核心的仿真代码。这套框架完全独立于Unity系统,比如常用的GameObject或者MonoBehaviours。仅有的联系可能就是一些数学库,但是数学库是很容易被替换的。这套核心框架几乎包含了所有的RVO的模拟代理。
第二部分是Unity的接口。但这些接口大部分是对核心系统做了一次包装。比如:RVOSimulator就是PathFinding.RVO.Simulator类的包装。Unity的接口还包含了一些帮助类,用来更方便的做RVO的集成,其中最常用的可能就是RVOController,它支持了一些方法诸如Move,或者一些属性诸如velocity,让你获取和操作的时候更加简单和容易。
所有的Unity接口脚本都有一个共同的假设前提:场景里应该只存在一个RVOSimulator,其他脚本都会从它查询以获取一些核心的模拟实例。你只需要将它添加到任意一个GameObject上,编辑少量的参数,就可以工作。一个就好,千万不要多啦!所有脚本都是从FindObjectOfType接口查询它,如果你添加了多个,就没办法从这个接口区分你到底要的是哪个,安全起见,就一个!
示例

插件提供了3个场景示例。分别在
Assets/AstarPathfindingProject/ExampleScenes/Example11_RVO 和
Assets/AstarPathfindingProject/ExampleScenes/Example16_RVO 2D 目录下
整合

内置的AI脚本(AIPath, RichAI)支持RVOController的即开即用,只要简单添加RVOController搭配AI脚本所在的GameObject上就好。
3.x的不支持Rigidbody或者CharacterController同时绑定在一个代理(Agent)上。因为动态回避系统希望能够完全掌控agent的行动。但是4.X你可以把它们放在一起拉。不过要注意的是,动态回避系统并不适用colliders,所以也不会去试图规避它。AILerp 类型设计的用途就是对路径进行插值,并且精确的执行它。因此,如果它偏离的路径那就失去了脚本原来的意义,所以只能取消它对RVO的支持了。
如果你想自己写一个RVO的自定义移动脚本,可以参考后面的文章。
在4.0版本之前,RVOController控制移动的原理和Unity的CharacterController很像。所以这里的设计错误就会导致二者如果绑定在同一个对象上会出现移动冲突。物理层面的整合

有时候你可能确实需要处理colliders,比如子弹击中或者其他的一些情况。但是当你往对象上添加了一个colliders之后,在部分拥挤的情况下,局部回避系统的表现会急剧下降。这是因为拥挤情况下,agent会有一小部分的重叠情况,但是物理系统又不允许。这样就会导致整个运动方式看起来很糟糕。
所以当你实在需要colliders的话,我推荐你把agent之间的碰撞关掉来解决这个问题。把Agents放到一个独立的layer层级之下,然后把Unity的physics settings打开,设置不同层级之间的碰撞关系,值得注意的是,Unity的物理区分了2D和3D,所以你要看清楚自己修改的是哪一个。
确保Agent在导航网格上

当时用了局部回避系统之后,经常会出现一些agents把另外一些agents挤出了导航网格之外。这肯定不是想要的结果,不过幸运的是,我们有办法解决这个情况。
如果你使用的是RichAI脚本,它会自动解决这个问题,因为它移动的方式就是基于网格,所以必须优先保证这一点。如果是AIPath脚本,你需要使用constrainInsideGraph选项。
另外还有一个备胎选项,就是使用RVONavmesh组件。它会给所有的障碍物添加一个回避系统知道的边界范围。不过它会有一个轻量的计算消耗(如果你在运行时重计算一个比较大的Graph的时候),但是它可以让回避系统提前知道障碍在哪而不是等一个agent已经撞上了之后再去计算墙在哪里。
RVONavmesh支持navmesh、recast和grid 类型的graph.
如果你需要经常更新graph,你可能不想要使用它,因为每次graph重建它也必须重建,对性能有显著的影响。你可以通过勾选RVOSimulator组件上的 Show Obstacles选项框来查看障碍物的轮廓信息。
下面的图你可以看到,若干agents使用了局部回避之后,在某个角落里不开启constrainInsideGraph的话 很容易就被挤出障碍物里面去了。
障碍物

可以在simulation里面添加一些障碍物(Obstacles)。Agents会确保不会穿过他们,(除非以一个相对FPS极高的速度移动)并且可以使用局部回避来规避它们。
一些内置的colliders通过下面方式添加 Components -> Local Avoidance,如果你想自己写一个自定义的colliders,后面也会介绍。
一个内置的colliders可以随意的移动,然后他们会在合适的时机进行更新。但是这并不意味着它会主动推开Agents,实际上,在这点上它也是非常不擅长。当它移动的比较慢的时候,可能还可以,但是如果移动的比较快的时候,agents可能会穿插到里面去。
有一个公有的碰撞变量叫 Obstacle Mode field。这个会在后面写自定义RVO colliders的指南里详细说明,这里仅仅说明一下,它可以用来阻挡Agents移动到一个障碍里面。但是当它已经在里面了,它并不会将agents移出来,反而可能会跟着障碍物一起移动。
密集运算(Number Crunching)

那么你可能会质疑,这个系统的运行效率到底有多快。呃~简单的说,非常快!实际上一个局部回避的系统并不需要一个很快的FPS,太高了反而是浪费CPU,因为性能并不会提升。在一台i7的机器上,用一个较良好的fps运行了5000个agents。局部回避系统以10FPS的方式运行,游戏以50-100的FPS运行,最终的结果是最低帧在30-40。这个可视化是通过一个很简单的方式去执行,创建一个Mesh,然后给每一个agent创建一个quad。这样做的原因是因为:这么多的数量,如果给每一个agent都创建一个GameObject会非常的慢。测试的时候,光创建这些物体的时间就接近10秒。这些agents创建成一个圆形,并且都试图到对面的点,所以他们是要多挤有多挤了。
但是不管怎么说,你的游戏肯定用不到这么多agents的,不过呢,这些例子也是比较轻量级的展示,一个真正的游戏还会有很多来自其他方面的性能压力。
下面是刚才示例的截图,感受一下:


原文地址:

seedees3 发表于 2021-7-12 22:00

收藏下,感谢!!!
页: [1]
查看完整版本: Unity寻路插件(A* Pathfinding)进阶教程二:局部回避(RVO)