3.10 导航网格 (Navigation Mesh) 3.10 导航网格 (Navigation Mesh) 在 Unity3D 中,导航网格 (Navigation Mesh, 简称 NavMesh) 是实现智能角色移动和寻路功能的核心模块。它允许游戏中的非玩家角色 (NPCs) 或其他 AI 代理在复杂的游戏环境中自主、智能地移动,避开障碍物,找到到达目标的最优路径。导航网格极大地简化了 AI 寻路功能的开发,提升了游戏世界的真实感和互动性。 3.10.1 导航网格的概念与优势 什么是导航网格? 导航网格是一种用于路径规划的数据结构。它将游戏场景中可行走区域表示为一个由凸多边形连接而成的网格。
在 Unity3D 中,导航网格 (Navigation Mesh, 简称 NavMesh) 是实现智能角色移动和寻路功能的核心模块。它允许游戏中的非玩家角色 (NPCs) 或其他 AI 代理在复杂的游戏环境中自主、智能地移动,避开障碍物,找到到达目标的最优路径。导航网格极大地简化了 AI 寻路功能的开发,提升了游戏世界的真实感和互动性。
什么是导航网格?
导航网格是一种用于路径规划的数据结构。它将游戏场景中可行走区域表示为一个由凸多边形连接而成的网格。可以将其想象成一张“可行走区域地图”,AI 角色只需要在这张地图上寻找路径,而无需考虑复杂的几何形状和障碍物。
与传统的基于路点 (Waypoint) 或栅格 (Grid-based) 的寻路系统相比,导航网格具有以下显著优势:
高效性: 导航网格能够快速生成和查询路径,即使在复杂场景中也能保持较高的寻路效率。
自然性: 基于凸多边形的网格能更精确地描述可行走区域的形状,使得 AI 角色的移动更加自然流畅,避免了栅格系统中的“锯齿”感。
灵活性: 导航网格可以方便地处理不同高度、斜坡等复杂地形,并能适应动态环境中的障碍物变化。
易用性: Unity3D 提供了强大的内置导航网格系统,开发者可以轻松地生成、配置和使用导航网格,无需从零开始编写复杂的寻路算法。
导航网格的应用场景
导航网格广泛应用于各种类型的游戏,特别是在需要大量 AI 角色进行复杂移动的游戏中,例如:
角色扮演游戏 (RPG): NPC 的城镇巡逻、任务指引、战斗走位。
即时战略游戏 (RTS): 单位的集群移动、资源采集、战场迂回。
第一人称射击游戏 (FPS): 敌人的追击、躲避、战术包抄。
模拟经营游戏: 市民的自主活动、交通规划、资源运输。
简而言之,任何需要 AI 角色在游戏世界中智能移动的场景,导航网格都是一个理想的选择。
Unity3D 的导航网格系统主要由以下几个核心组件和概念组成:
导航网格烘焙 (Navigation Mesh Baking): 这是生成导航网格的过程。Unity 会分析场景中的静态几何体,识别可行走区域,并自动生成导航网格数据。
导航网格代理 (NavMesh Agent): 这是附加在游戏角色上的组件,负责控制角色的移动和寻路行为。代理会根据导航网格数据计算路径,并驱动角色沿着路径移动。
导航区域 (Navigation Area): 用于定义不同类型的可行走区域,并可以为不同区域设置不同的移动成本。例如,草地、泥地、水面等可以设置为不同的区域类型,影响 AI 角色的移动速度或偏好。
脱网格链接 (Off-Mesh Link): 用于处理导航网格无法直接连接的区域之间的移动,例如跳跃、攀爬、传送门等。
导航网格障碍物 (NavMesh Obstacle): 用于在运行时动态地添加障碍物,让 AI 角色能够避开这些动态障碍。
导航网格工作流程图解 (Mermaid Graph TD)
流程说明:
场景准备: 在场景中放置静态几何体,这些物体将用于生成导航网格。
导航网格烘焙: 通过 Unity 编辑器的 Navigation 窗口进行烘焙,生成导航网格数据。
角色配置: 为需要寻路的角色添加 NavMesh Agent 组件。
设置目标: 通过脚本或动画等方式设置 NavMesh Agent 的目标位置。
路径规划: NavMesh Agent 根据导航网格数据和目标位置,自动计算出到达目标的路径。
角色移动: NavMesh Agent 驱动角色沿着计算出的路径移动。
可选功能: 可以使用导航区域、脱网格链接和导航网格障碍物等组件来增强导航网格的功能和适应更复杂的场景。
接下来,我们将通过一个简单的示例项目,详细讲解如何在 Unity3D 中实践导航网格。
示例项目:简单的 AI 角色寻路
1. 创建 Unity 项目和场景
创建一个新的 Unity 3D 项目。
在场景中创建一些简单的静态几何体作为障碍物,例如 Cube 和 Plane。
创建一个 Plane 作为地面。
2. 标记静态物体为 Navigation Static
选中场景中的所有静态几何体(障碍物和地面)。
在 Inspector 面板中,将它们的 Navigation Static 属性勾选上。
3. 烘焙导航网格
打开 Navigation 窗口 (Window -> AI -> Navigation)。
在 Navigation 窗口中,切换到 Object 标签页,确认需要烘焙的对象都已勾选 Navigation Static。
切换到 Bake 标签页。
点击 Bake 按钮。
Unity 将会自动分析场景,并在 Scene 视图中以蓝色半透明网格的形式显示生成的导航网格。
你可以在 Bake 标签页中调整烘焙参数,例如:
Agent Radius: AI 角色的半径,影响角色能否通过狭窄区域。
Agent Height: AI 角色的高度,影响角色能否通过低矮区域。
Max Slope: 角色能够行走的坡度最大值。
Step Height: 角色能够跨越的最大台阶高度。
Min Region Area: 最小可行走区域面积,用于过滤掉过小的区域。
4. 创建 AI 角色并添加 NavMesh Agent 组件
在场景中创建一个新的 3D 对象作为 AI 角色,例如 Capsule 或 Sphere。
选中 AI 角色对象。
在 Inspector 面板中,点击 Add Component 按钮,搜索并添加 Nav Mesh Agent 组件。
NavMesh Agent 组件负责控制角色的移动和寻路。
你可以在 Inspector 面板中调整 NavMesh Agent 的属性,例如:
Speed: 角色的移动速度。
Acceleration: 角色的加速度。
Angular Speed: 角色的旋转速度。
Stopping Distance: 角色到达目标位置后停止移动的距离。
Radius: 代理的半径,与烘焙参数中的 Agent Radius 对应。
Height: 代理的高度,与烘焙参数中的 Agent Height 对应。
5. 编写脚本控制 AI 角色移动
创建一个新的 C# 脚本,命名为 AIController (或其他你喜欢的名字)。
将 AIController 脚本附加到 AI 角色对象上。
打开 AIController 脚本,并输入以下代码:
using UnityEngine; using UnityEngine.AI; public class AIController : MonoBehaviour { public Transform target; // 目标位置 private NavMeshAgent navMeshAgent; void Start() { navMeshAgent = GetComponent<NavMeshAgent>(); } void Update() { if (target != null && navMeshAgent != null) { navMeshAgent.SetDestination(target.position); // 设置导航目标位置 } } }
在 Unity 编辑器中,创建一个空的 GameObject 作为目标位置 (Target)。
将 Target 对象拖拽到 AI 角色 AIController 组件的 Target 属性槽中。
移动 Target 对象在场景中的位置,你会看到 AI 角色会自动寻路并移动到 Target 对象的位置。
代码解释:
using UnityEngine.AI;: 导入 Unity 的 AI 命名空间,包含 NavMeshAgent 类。
public Transform target;: 声明一个公共的 Transform 变量 target,用于在 Inspector 面板中指定目标位置。
private NavMeshAgent navMeshAgent;: 声明一个私有的 NavMeshAgent 变量 navMeshAgent,用于存储 AI 角色的 NavMeshAgent 组件。
navMeshAgent = GetComponent<NavMeshAgent>();: 在 Start() 函数中获取 AI 角色对象上的 NavMeshAgent 组件。
navMeshAgent.SetDestination(target.position);: 在 Update() 函数中,每帧调用 navMeshAgent.SetDestination() 方法,将目标位置设置为 target 对象的位置。NavMeshAgent 会自动计算路径并驱动角色移动。
6. 运行游戏并观察效果
运行 Unity 游戏。
在 Scene 视图或 Game 视图中,拖动 Target 对象,观察 AI 角色是否能够正确地避开障碍物,寻路并移动到 Target 对象的位置。
恭喜你! 你已经成功地创建了一个简单的基于导航网格的 AI 角色寻路示例。
导航区域允许你定义不同类型的可行走区域,并为不同区域设置不同的移动成本。这可以用于模拟不同地形对角色移动的影响,例如:
草地: 正常移动速度。
泥地: 移动速度减慢。
水面: 移动速度更慢,或者只有特定角色才能进入。
使用导航区域的步骤:
定义导航区域类型:
在 Navigation 窗口的 Areas 标签页中,可以定义新的导航区域类型,并为每个区域类型设置 Cost 值。
标记场景对象的导航区域:
选中场景中需要设置特定导航区域的静态几何体。
在 Inspector 面板中,找到 Navigation 属性组。
在 Navigation Area 下拉菜单中,选择你定义的导航区域类型。
在 NavMesh Agent 中设置区域掩码 (Area Mask):
选中 AI 角色对象。
在 NavMesh Agent 组件的 Inspector 面板中,找到 Area Mask 属性。
Area Mask 属性是一个位掩码,用于指定 AI 角色可以进入哪些导航区域。
默认情况下,所有区域都被选中 (Layer 0)。
你可以取消选中某些区域,限制 AI 角色只能在特定的区域类型中移动。
示例:设置泥地减速区域
在 Navigation 窗口的 Areas 标签页中,创建一个新的导航区域类型,命名为 "Mud",并将 Cost 值设置为 2 (高于默认区域的 Cost 1)。
在场景中创建一个 Plane 对象,覆盖一部分地面区域,作为泥地。
选中泥地 Plane 对象,勾选 Navigation Static 属性,并在 Navigation Area 下拉菜单中选择 "Mud"。
烘焙导航网格。
运行游戏,观察 AI 角色在泥地区域的移动速度是否变慢。
脱网格链接用于处理导航网格无法直接连接的区域之间的移动,例如跳跃、攀爬、传送门等。
创建脱网格链接的步骤:
创建 Off-Mesh Link 对象:
在场景中创建一个空的 GameObject。
点击 Add Component 按钮,搜索并添加 Off Mesh Link 组件。
配置 Off-Mesh Link 组件:
在 Off Mesh Link 组件的 Inspector 面板中,设置以下属性:
Start Transform: 链接的起点 Transform。
End Transform: 链接的终点 Transform。
Link Type: 链接类型,可以是 Manual (手动控制) 或 Auto Generated (自动生成)。
Cost Modifier: 链接的移动成本,可以调整 AI 角色对使用该链接的偏好。
Bi Directional: 是否双向链接。
Activated: 链接是否激活。
调整链接起点和终点位置:
Off Mesh Link 对象的子对象 Start 和 End 调整到链接的起点和终点位置。代码控制 Off-Mesh Link 穿越:
当 AI 角色到达脱网格链接的起点时,NavMeshAgent 会触发 OnTriggerEnter 回调函数 (如果链接的起点带有 Collider 组件)。你可以在脚本中检测是否进入了脱网格链接区域,并手动控制角色进行跳跃、攀爬等动作。
示例:创建跳跃链接
在场景中创建两个平台,平台之间留有一定间隙。
在两个平台之间创建一个 Off-Mesh Link 对象,调整起点和终点位置,使其分别位于两个平台上。
将 Link Type 设置为 Manual。
编写脚本,检测 AI 角色是否到达链接起点,并在脚本中控制角色进行跳跃动画和移动到链接终点。
导航网格障碍物用于在运行时动态地添加障碍物,让 AI 角色能够避开这些动态障碍。例如,移动的箱子、掉落的树木、开关门等。
使用导航网格障碍物的步骤:
为动态障碍物添加 NavMesh Obstacle 组件:
选中动态障碍物对象。
点击 Add Component 按钮,搜索并添加 Nav Mesh Obstacle 组件。
配置 NavMesh Obstacle 组件:
在 NavMesh Obstacle 组件的 Inspector 面板中,设置以下属性:
Shape: 障碍物的形状,可以是 Capsule 或 Box。
Radius/Size: 障碍物的半径或尺寸。
Height: 障碍物的高度。
Center: 障碍物的中心偏移量。
Carving: 是否进行雕刻 (Carve)。
启用 Carving 后,导航网格会实时更新,在障碍物周围生成新的可行走区域。
禁用 Carving 后,障碍物只会被视为静态障碍物,不会实时更新导航网格。
Carve Only Stationary: 仅在障碍物静止时进行雕刻。
Move Threshold: 障碍物移动超过此阈值时才进行雕刻更新。
Time To Stationary: 障碍物停止移动多久后开始雕刻。
示例:动态箱子障碍物
在场景中创建一个 Cube 对象作为箱子。
为箱子添加 NavMesh Obstacle 组件,并启用 Carving 属性。
编写脚本,控制箱子在场景中移动。
运行游戏,观察 AI 角色是否能够避开移动的箱子。
在复杂的场景中,导航网格的性能优化至关重要。以下是一些常见的优化技巧:
合理设置烘焙参数: 调整 Agent Radius、Agent Height、Max Slope 等烘焙参数,避免生成过于精细的导航网格。
减少导航网格数据量: 尽量简化场景几何体,减少不必要的细节,可以有效减小导航网格数据量。
分层烘焙导航网格: 对于大型场景,可以将场景划分为多个区域,分别烘焙导航网格,并使用场景加载或卸载技术,只加载当前区域的导航网格数据。
限制 NavMesh Agent 数量: 场景中 AI 角色数量过多时,会增加寻路计算的负担。可以考虑使用对象池技术,或者优化 AI 逻辑,减少不必要的寻路计算。
使用异步寻路: 将寻路计算放在后台线程中进行,避免阻塞主线程,提高游戏帧率。 (Unity 的 NavMeshAgent 默认已在后台线程进行部分计算)
定期重新烘焙导航网格 (Runtime Baking): 对于动态变化较大的场景,可以定期重新烘焙导航网格,以适应场景变化。但需要注意 runtime baking 的性能开销。
导航网格是 Unity3D 中实现智能角色寻路的关键技术。它具有高效、自然、灵活和易用等优点,被广泛应用于各种类型的游戏中。
本章节详细介绍了导航网格的概念、核心组件、工作流程和实践操作,并深入讲解了导航区域、脱网格链接和导航网格障碍物等高级功能。同时,也提供了一些导航网格性能优化的建议。
掌握导航网格技术,你将能够为你的游戏角色赋予更智能的行为,创造更生动、更具沉浸感的游戏世界。随着游戏技术的不断发展,导航网格技术也在不断演进,未来将会出现更多更强大的寻路算法和工具,为游戏 AI 带来更广阔的应用前景。
希望本章节内容能够帮助你深入理解和掌握 Unity3D 的导航网格技术,并在你的游戏开发项目中灵活运用。