赞
踩
新版本的Unity导航系统。可以帮助我们轻松实现角色在地图上的导航移动效果。跟老版本相比感觉差不多,不过有些地方的改进的确不错。
新版本的导航系统(Navigation)是以包的形式存在的,所以需要提前去安装(菜单栏-Window-PackageManager-搜索包Navigation)。如图:
安装完成后可从编辑器上层的菜单页面查看Navigation页面,如图:
Navigation是新版本的页面,Navigation(Obsolete)是老版本的页面。
在新场景中创建一个地面Plane,然后为其添加一个空父物体Map,再在父物体上添加一个NavMeshSurface组件。这里添加一个Map父物体是为了方便管理,没有强制要求子父级关系,比如NavMeshSurface组件添加到Plane上也可以。
之后先不管其各种参数含义,直接点击Bake按钮,即可看到地图被烘焙了,导航网格被显示出来。
同时这个时候我们可以看到在Assets资源中,有一个与场景同名的文件被创建出来,文件中还会有一个.asset资源文件NavMesh-Map,这个文件主要记录我们刚才烘焙所得的各种信息。
这个时候,若我们不想要这个烘焙效果了,可直接点击Map的NavMeshSurface组件上的Clear按钮,清除烘焙。
清除后地面的烘焙效果消失,且刚刚的NavMesh-Map文件也被删除了。至此,一个简单的地图烘焙操作就完成了。
首先,创建一个Sphere,作为我们的玩家(Player),再创建一些Cube作为墙体。我们认为Cube是墙体,那么其应为地图的一部分,为了方便管理,我们将Cube设为Map的子物体。由于我们增加了墙体,所以地图有了新内容,需要我们重新烘焙一次,再次点击Bake即可。结果如图所示,可以看到墙体周围的地面已经是不可通过的区域了:
接下来给我们的Player增加一个Nav Mesh Agent组件,用来配合地图的烘焙,准备实现我们想要的效果。
同样,现在不用关心这些参数的含义,我们先以实现小案例为主。那么之后创建一个Player.cs脚本挂到Player身上,然后增加一些代码来实现Player的导航效果。
代码:
- using UnityEngine;
- using UnityEngine.AI;
-
- public class Player : MonoBehaviour
- {
- //定义NavMeshAgent组件对象
- private NavMeshAgent agent;
-
- private void Awake()
- {
- //获取NavMeshAgent组件对象
- agent = this.GetComponent<NavMeshAgent>();
- }
-
- private void Update()
- {
- //实现效果:让Player导航移动到鼠标左键点击的位置。
- //若按下鼠标左键
- if(Input.GetMouseButton(0))
- {
- //向鼠标所在位置发送射线
- Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
- //射线碰撞信息(用于接收信息)
- RaycastHit hit;
- //检测射线ray的碰撞,并将碰撞信息返回到hit中
- if (Physics.Raycast(ray, out hit))
- {
- //将射线碰撞点设置为Player导航的目的地
- //NavMeshAgent组件的应用
- agent.SetDestination(hit.point);
- }
- }
- }
- }
至此小案例就完成了,运行程序,点击地面就可以看到小球(Player)的导航移动了。
此组件主要挂载到Player身上,即谁要导航移动,就挂到谁身上。另外,挂载NavMeshAgent的对象是不会被烘焙的。
代理者类型,可以理解为一个组,只有在一个组内的Player、烘焙的导航网格等等才会相互有所影响。Humanoid是默认的预设,我们点开这个标签页。
可以看到选项里有一个设置选项,点击Open Agent Settings,可以打开设置面板。
我们可以修改Humanoid的相关参数,也可以通过+、-按钮可以添加新的Agent Type,去自定义相关参数。
PS:Agent Type匹配问题。
说明下自定义Agent Type的使用,若我们新增了Agent Types并进行替换,那么Player的NavMeshAgent组件和地图上的NavMeshSurface组件上的Agent Type都要替换掉,保持一致,且地图还要重新烘焙下。如图:
NavMeshAgent需要有一个或多个与之对应的NavMeshSurface才行,即相同的Agent Type。我们也可以创建多个Player和Map,然后为它们分组,赋予不同的Agent Type,那么他们之间是不会相互影响的。如图,想要复现的话注意我修改的Collect Objects参数,让地图仅烘焙自己及其子级对象,这个在后面会讲到。
Generated Links
生成链接。这里指跳跃链接。
以上两个参数基本是一起使用的,但具体怎么用呢?首先找到我们的Map身上的NavMeshSurface组件,将参数Generate Links勾选了,勾选后便可以在烘焙时自动借助以上两个参数生成跳跃链接。如图:
偏差值,用来设置偏差的,那么是谁的偏差?首先,我们来观察下上面小案例Scene窗口中的Player。
可以看到小球的外侧有一个圆柱体将其包裹其中,这个圆柱体才是我们导航系统中真正操作的对象。换句话说我是在给小球导航吗?我是在给这个圆柱体导航,小球只是附带物罢了。那么我们的这个偏差值即表示“圆柱体”与“小球”的高度偏差,注意是高度。
PS:圆柱体本来是有些陷到地面的,我这里把Player整体拉上来了一些,不过不影响,无论是陷到地下,还是浮空,还是跑出地图范围了,只要距离不太远,一旦运行都会落到烘焙的地面上或是拉到地图边缘。
那么演示操作一下:
Player控制设置。
障碍物避让设置。参数所调整的便是我们上文所说的“圆柱体”,其还起到避障作用。
PS:同优先级Player间避让问题。
若两个Player向同一目标点移动,则会出现避让(将上面的案例中的Player直接复制一个,就可以实现此效果,这时候也可以去试试上面的优先级测试)。那么目标点与两个同优先级的Player的位置关系是什么?答:是以圆柱体边缘为基点,平均靠近目标点。表述可能不太清晰,直接看下图吧,图中把一个Player的Radius增大了,然后两个Player都往目标点移动,从图中可以看到两个小球(Player)都没有到目标点的位置,基本上是两个小球的圆柱体在目标点处相切(虽然有些偏差)。
需要注意,不要同时显示两条路径,这样即使隐藏另一条,即使没勾选AutoRepath,Player也会去找另一条;还要在Player达到边缘停止前完成路线1的隐藏和路线2的显示,否则即使勾选,也不会去找路线2。通过这些测试我们也大概知道这个RePath是个什么逻辑了。
此组件主要挂载到地图上,说是地图,实际上随便一个游戏物体基本都可以,一般是挂载到一个空物体上,然后子级放构建地图的游戏对象。
这个和Nav Mesh Agent的一样,就不说啦。
区域。一个类似于层级的东西。如下:
默认给我们提供了三个,可走、不可走、跳跃。当然我们也可以点击Open Area Settings自己来自定义。
又看到老朋友Agent了。在User里填写名字可以添加我们自己的区域。Cost表示消耗,可以理解为Player在面临多个区域时,选择走哪个区域的优先级判断。大家都是懒人,消耗越大的区域,自然不想走,这里也一样,Cost越大的区域优先级就越低,越小优先级越大。
若勾选此选项,则被收集的对象会依赖于Agent Type中设置的Agent的Generated Links下的Drop Height参数和Jump Distance参数来生成“跳跃链接(Link)”。换人话就是,勾选后,在烘焙时,会借助上述两个参数自动生成“跳跃链接”。
另外,此参数是可以借助NavMeshModifier重写覆盖的,等介绍到这个组件的时候再讲吧。
再另外....生成的跳跃链接是属于默认提供的Jump Area的!
使用几何。即我们使用怎样的几何体来进行烘焙,有两个选项如下:
对象收集。被收集的对象在Bake时才能被烘焙到。
Collect Objects:收集对象的方式。
PS:需要注意,挂载NavMeshAgent或NavMeshObstacle组件的对象是不会被收集烘焙的。
Include Layers:收集对象时的层级筛选。在满足Collect Objects收集方式的前提下,对收集对象进行层级筛选,只有属于包含层级的对象才能被收集。
高级设置。
什么是Voxel Size?立体像素的大小。其影响着我们借助借助几何形状烘焙生成的导航网格的准确性。立体像素比较合适的大小是每个Agent Radius 2-4尺寸,立体像素尺寸越小,导航网格生成的时间越长。
什么是Tile Size?瓷砖尺寸(?),好吧就是区块之类的东西的尺寸。这个东西大概.....就是说我们重新烘焙时,变化的区域块,即变化是按块划分的,一块一块的变化,这个值越小的话就可以获得更细节变化,即效果更好,当然带来的缺点是会生成更多的数据存储起来。
就是烘焙后产生的数据文件,前面有提过的。Clear后就没了,Bake就有。
此组件是用来创建“跳跃链接”的,实现Player在不同地形间跳跃。一般是将其挂载到一个空物体上,就会形成一个桥梁一样的东西。如图:
和之前两个组件的一样,这里就不多介绍了。不过和之前一样,这里Agent Type的选择需要与Player、Map保持一致,否则不会生效。
路径宽度。越宽,可走范围就越大。如图:
Cost修改(覆盖/重写)。首先,前面说过Area这个东西,如同层级一样,是有优先级的,即Cost。这里,若为正值,则此“跳跃链接”的Cost为我们输入的值,否则使用其所属Area的Cost。
官网解释:如果启用此属性,当端点移动时,网格外链接将重新连接到导航网格。如果禁用,即使移动了端点,链接也将保持在其起始位置。
感觉这个是给老组件Off-Mesh Link用的,老组件在运行时若修改两个端点的位置,只有在勾选此项时,“跳跃链接”才会跟着端点调整。现在这个新Link组件,无论勾不勾上,都可以随时跟着端点调整,似乎用不到啊。
双向。即无论从起始点还是终点,都可以跳跃!
区域类型。就是设置Area的,前面都讲过了。
导航网格障碍物组件。谁是障碍物就挂到谁身上。Player的移动是会避开障碍物的。另外,挂载NavMeshObstacle组件的对象是不会被烘焙的。
障碍物几何体的形状。分为Box(方体)和Capsule(胶囊)两种。无论我们挂载的对象是什么样子,这个选项才是真正的起障碍物作用的几何形状,类似碰撞体。
几何形状的中心位置。
几何形状尺寸,在选择Box和Capsule时,可调参数是不同的,这个很简单就不说了。
雕刻。勾选此选项后,导航网格障碍物会在导航网格中创建一个孔,即在烘焙的网格中挖出一个不可经过的区域。同时会出现三个参数,这三个参数是来调整导航网格障碍物移动时如何重新雕刻孔的。
开启与关闭的区别图示:
更详细的官方解释:
障碍与雕刻
导航网格障碍物可通过两种方式影响导航网格代理(即Player,官方文档称其为代理)在游戏中的导航:
未启用 Carve 时,导航网格障碍物的默认行为类似于碰撞体的行为。导航网格代理会尝试避免与导航网格障碍物的碰撞,当靠近时,它们会与导航网格障碍物碰撞。障碍躲避行为是非常基本的,具有一条短半径。因此,导航网格代理可能无法在导航网格障碍物很混乱的环境中找到方向。此模式最适合用于障碍物不断移动的情况(例如,车辆或玩家角色)。
启用 Carve 时,障碍物处于静止状态时将在导航网格中雕刻一个孔。移动时,障碍物即为障碍物。在导航网格中雕刻一个孔后,寻路器 (pathfinder) 能够让导航网格代理绕过雕有障碍物的位置周围,或者如果当前路径被障碍物阻挡,则寻找另一条路线。对于通常会阻碍导航但可被玩家或其他游戏事件(如爆炸)移动的导航网格障碍物(例如板条箱或木桶),最好为其开启雕刻功能。
(PS:若以障碍物方式,就会在这种情况下被卡住。说明还是通过在导航网格上雕刻来寻路更为智能一些。)
移动的导航网格障碍物的逻辑
当导航网格障碍物的移动距离超过 Carve > Move Threshold 设置的值时,Unity 会将其视为移动状态。当导航网格障碍物移动时,雕刻的孔也会移动。但是,为了减少 CPU 开销,只在必要时才重新计算该孔。此计算的结果将在下一帧更新中可用。重新计算逻辑有两个选项:
这是默认行为。要启用此选项,请勾选导航网格障碍物组件的 Carve Only Stationary 复选框。在此模式下,当导航网格障碍物移动时,雕刻的孔将被移除。当导航网格障碍物已停止移动并且静止时间超过 Carving Time To Stationary 设置的值时,障碍物将被视为静止状态,并再次更新雕刻的孔。当导航网格障碍物为移动状态时,导航网格代理会使用碰撞躲避功能避开它,但不会在它周围规划路径。
Carve Only Stationary 通常是性能方面的最佳选择,当与导航网格障碍物相关联的游戏对象由物理系统控制时,是很适合的选项。
要启用此模式,请取消勾选导航网格障碍物组件的 Carve Only Stationary 复选框。取消勾选此复选框的情况下,当障碍物移动的距离超过 Carving Move Threshold 设置的值时,雕刻的孔会更新。此模式适用于大型缓慢移动的障碍物(例如,步兵避开的坦克)。
注意:使用导航网格查询方法时,应考虑到更改导航网格障碍物与该更改对导航网格生效之间存在一帧延迟。
导航网格修改器。可调整特定游戏对象在导航网格烘焙期间的行为方式。就好比是对我们的Map进行局部修改一样。修改器会影响挂载到的对象以及其子级对象。
如下图,Map是所有地图的父物体,Other是原先的地图内容,这里我增加了两块地面,并给其增加了一份父级空对象ModifierTest,再其父级对象上挂载了一个Nav Mesh Modifier组件。这样以来我的地图Map中就有两块地面是被一个导航网格修改器可修改的。后续将演示使用。
修改形式。分为如下两种:
那么,如何使用呢?
Add or Modify object
当选择Add or Modify object时,下方会多出两个参数Override Area(重写区域)、Override Generate Links(重写跳跃链接生成)。这两个重写的内容本文之前都介绍过,分别勾选如下:
可以看到,勾选后,我们可以重新选择设置的Area,以及是否自动生成Links。
首先演示跳跃链接的生成吧,要先把Map的NavMeshSurface组件上的自动生成给关掉,然后我们重写那两块地面可以生成。如下:
可以看到只有那两块地板有跳跃链接的生成。PS:虽然跳跃链接链接到了左侧的大地面,但设置是只有那两块修改器控制的地面能生成,所以Player能从两块地面上来到大地面,但不能从大地面移动到小地面上。
然后演示所属Area。首先我们先关闭修改器的Override Area烘焙下,默认烘焙是Walkable,然后通过修改器将两个地面的Area修改为Jump,再烘焙。如下:
可以看到那两块地面颜色变化了,Area被重写了。
Remove object
这个就很简单了。选择这个后就表示将修改器影响的对象完全剔除烘焙。
影响的Agents。就是Agent Type里的那个,比如NavMeshSurface设置的Type是A,修改器是B,那么修改器就无法起到作用,除非改为A才能其作用。演示:
是否应用到孩子。前面说了修改器影响的是挂载的对象及其子物体,这里就是决定是否影响子物体的。
子父级上都挂有Modifier组件时,他们的优先级关系是什么?答案是覆盖关系,子级覆盖父级。但要注意这里覆盖的是什么,覆盖的是修改器吗?不是,覆盖的是我们的游戏对象,即不是简单的修改器替换,而是要根据修改器的参数才能确定多层修改器最终的修改结果是什么。理解了这个后面就好理解了。
给ModifierTest加一个空物体父级F,挂载一个Modifier组件,然后....看图吧,F和ModifierTest上的组件设置是这样。
那么烘焙结果是什么?答案是两地面不烘焙。那么若把ModifierTest的Apply To Children给去掉勾选呢?答案是两地面烘焙。看演示吧:
因为我们去掉勾选后,子级修改器不会作用于孩子,所以修改不到两个地面,但这个时候要注意修改不到不代表就是默认烘焙,我们上层还有一个修改器呢,子级修改不到两个地面,那么父级对两个地面修改就不会被子级修改给覆盖掉,所以结果是父级修改器的修改结果。
导航网格修改器体积。用来使用一个Box标定区域的,即将某块区域设置为某个Area(类似层级那个)。后面有演示。
Box大小,也可点击Edit按钮直接编辑,与碰撞体类似。
Box中心位置。
要修改标识为的Area。演示:
注意Box的高度,要碰撞到烘焙的地面才行,最后我拉高Box后导致无法标识地面区域。
影响的Agent,即Agent Type设置的那个。
明明没多少东西,一做笔记就花费了好长时间....总之还好,至少后面就不怕忘了。导航系统差不多也就这些内容了,那么这次就到这里结束吧。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。