2、NavMesh基础:Unity NavMesh组件介绍、烘焙NavMesh、设置可行走区域

好,咱们正式开始聊寻路。说到Unity里的寻路,绕不开的就是NavMesh。我刚开始用Unity做AI时,第一反应是自己写A*算法,折腾了好几天,结果发现Unity自带的这套系统已经相当成熟了。说白了,NavMesh就是一张“能走的地图”,AI角色在这张图上移动,省心又高效。

2.1 Unity NavMesh组件介绍

Unity的寻路系统核心就三个组件:NavMesh AgentNavMesh ObstacleNavMesh Surface。嗯,这里要注意,新版Unity(2018.4之后)把烘焙功能从旧版的Window菜单移到了组件里,很多人刚升级时找不到,我当初也懵了一下。

2.1.1 NavMesh Agent

这个组件挂在需要移动的角色身上。它负责接收目标点,然后沿着NavMesh自动寻路。我个人习惯把Agent看作“自动驾驶模块”——你告诉它去哪,它自己算路径、避障、绕弯。

关键参数我列一下:

参数 作用 我的建议
Radius 角色半径,决定能通过多窄的通道 别设太大,否则过门框会卡住
Height 角色高度,决定能钻多矮的洞 和碰撞体高度保持一致
Speed 移动速度 单位/秒,我一般设3-6
Stopping Distance 离目标多近时停下 近战设0.5,远程设2-3
Auto Repath 路径失效时自动重新计算 建议开启,否则角色会傻站着
小技巧: 如果你发现角色走路像喝醉了似的左右晃,检查一下Radius是不是设得太小。我曾经有个项目,敌人总在走廊里撞墙,查了半天发现Radius设了0.1,改成0.5就正常了。

2.1.2 NavMesh Obstacle

这个组件挂在动态障碍物上,比如会移动的箱子、可开关的门。它和Agent的区别在于:Obstacle不自己走路,它只是“挡路”。

我记得有个坑——如果你把Obstacle的Carve选项勾上,它会在NavMesh上“挖洞”,让Agent绕开。但Carve的性能开销不小,场景里超过20个动态障碍物同时Carve,帧率会明显掉。我的经验是:静态障碍物直接烘焙进NavMesh,只有真正会移动的才用Obstacle。

2.1.3 NavMesh Surface

这是新版Unity用来烘焙NavMesh的组件。把它挂在地面上,点击Bake按钮,系统就会自动生成可行走区域。说白了,它就是“画地图”的工具。

参数里有个Agent Radius,这个值决定了NavMesh边缘离墙有多远。设大了,角色贴不了墙;设小了,角色会卡进墙角。我一般设0.3-0.5,具体看你角色的大小。

2.2 烘焙NavMesh

烘焙听起来高大上,其实就是让Unity计算哪些地方能走、哪些不能走。操作很简单,但细节决定成败。

2.2.1 烘焙步骤

  1. 选中地面或场景中的静态物体,在Inspector里勾上Navigation Static
  2. 给地面挂上NavMesh Surface组件。
  3. 点击Bake按钮,等几秒钟。
  4. Gizmos开关,查看蓝色区域——那就是能走的地方。

你想想看,如果地面没勾Navigation Static,Unity根本不会把它算进烘焙范围。我刚开始学时就犯过这错,烘焙了半天,NavMesh一片空白,还以为引擎坏了。

2.2.2 常见问题

  • 烘焙后地面有空洞: 检查地面是不是有缝隙,或者地面厚度不够。NavMesh要求地面有一定厚度(默认0.2米),薄了会漏。
  • 角色走到边缘就掉下去: 检查Voxel Size,设小一点可以提升边缘精度,但烘焙时间会变长。
  • 楼梯上不去: 楼梯的每个台阶都要勾Navigation Static,并且台阶高度不能超过Agent的Step Height(默认0.75)。
避坑指南: 我曾经有个项目,角色死活不走楼梯,非要绕远路。查了半天,发现楼梯的碰撞体是Mesh Collider,换成Box Collider就正常了。嗯,NavMesh对Mesh Collider的支持不太好,能用Box尽量用Box。

2.3 设置可行走区域

不是所有地面都能走。比如沼泽、岩浆、深水区,这些地方角色应该绕开。Unity通过Area系统来区分不同区域。

2.3.1 Area类型

打开Window -> AI -> Navigation,在Areas选项卡里可以看到默认的三种:

  • Walkable: 可行走,默认所有地面都是这个。
  • Not Walkable: 不可行走,比如墙壁、悬崖。
  • Jump: 可跳跃通过的区域,需要配合Off-Mesh Link使用。

你可以自己添加自定义Area,比如Danger(危险区)、Slow(减速区)。我个人习惯把沼泽设为Slow,然后让Agent经过时减速,而不是完全绕开——这样更真实。

2.3.2 设置方法

  1. 选中地面物体,在Inspector里找到Navigation Area下拉菜单。
  2. 选择你想要的Area类型,比如Not Walkable
  3. 重新烘焙NavMesh,Unity会自动排除这些区域。

举个例子:你有一个喷泉,想让角色绕开。把喷泉的水面设为Not Walkable,烘焙后NavMesh就会在喷泉周围自动生成一个“洞”。

2.3.3 代码控制区域代价

有时候你不想完全禁止行走,只是希望角色“尽量”避开。这时候可以用Area Cost(区域代价)。代价越高,角色越不愿意走。

// 设置沼泽区域的代价为10(默认是1)
NavMesh.SetAreaCost(NavMesh.GetAreaFromName("Swamp"), 10);

// 设置危险区域的代价为50
NavMesh.SetAreaCost(NavMesh.GetAreaFromName("Danger"), 50);

代价越大,路径规划时越倾向于绕路。但注意,如果代价设得太大(比如999),角色可能会绕很远的路,看起来反而不自然。我一般控制在3-10之间,既避开又不至于太夸张。

核心要点: NavMesh不是万能的。它适合地面平坦、障碍物固定的场景。如果你的游戏有大量动态障碍物(比如不断移动的敌人),建议结合RVO(Reciprocal Velocity Obstacles)或者自己写局部避障。嗯,这部分我们后面章节会详细讲。

好了,这一章的内容就这些。记住:烘焙前先检查静态标记,设Area时别忘重新烘焙,代价别设太极端。下一章我们聊Off-Mesh Link,让角色能跳、能爬、能翻墙——那才是寻路系统的进阶玩法。