Unity学习之寻路导航系统AI Navigation
在 RPG 游戏中,AI 导航(寻路)是核心功能之一,它决定了 NPC(非玩家角色)如何在复杂场景中移动 —— 例如敌人追击玩家、商人往返于城镇、同伴跟随主角等。Unity 提供了一套成熟的 AI Navigation 系统(旧称 NavMesh 系统),通过烘焙场景导航网格(NavMesh)和控制导航代理(NavMeshAgent),实现高效、灵活的寻路逻辑。
一、核心概念:理解导航系统的基础组件
Unity 导航系统的核心是 “导航网格(NavMesh)” 和 “导航代理(NavMeshAgent)” 的配合,辅以其他组件实现复杂行为。
导航网格(NavMesh)场景中 “可行走区域” 的多边形网格,是寻路的 “地图基础”。通过烘焙生成,标记哪些区域允许 AI 移动。
- 可行走层(Walkable):默认可移动区域
- 不可行走层(Non-Walkable):障碍物(如墙壁、岩石)
- 特殊层(Jump、Climb):支持跳跃 / 攀爬等动态行为
导航代理(NavMeshAgent)附加在 AI 物体上的组件,负责 “根据 NavMesh 计算路径并控制移动”,是 AI 寻路的 “执行者”。
- Speed:移动速度
- Angular Speed:转向速度(控制转弯平滑度)
- Stopping Distance:到达目标点的停止距离
- Radius:代理半径(避免与障碍物碰撞)
- Base Offset:代理与物体中心点的垂直偏移(适配模型高度)
导航障碍(NavMeshObstacle)标记动态障碍物(如可移动的箱子、临时关闭的门),让 NavMeshAgent 实时规避。
- Carve:是否 “切割” NavMesh(动态修改可行走区域)
- Move Threshold:障碍物移动多少距离后更新导航
导航区域(NavMeshArea)对 NavMesh 进行 “区域分类”(如草地、山地、水域),可设置不同区域的移动成本(Cost),让 AI 优先选择低成本路径。
- Area Type:区域类型(自定义或默认)
- Cost:移动成本(值越高,AI 越倾向于避开)
导航链接(NavMeshLink)连接两个不相邻的 NavMesh 区域(如桥梁、传送门),让 AI 可以 “跨区域移动”,支持单向 / 双向通行。
- Start/End Point:链接的起点和终点
- Bidirectional:是否双向通行
- Cost Modifier:通过链接的成本系数
二、基础工作流程:从场景烘焙到 AI 移动
要在 RPG 中实现基础寻路,需遵循 “场景准备 → 烘焙 NavMesh → 配置 NavMeshAgent → 编写寻路逻辑” 四步流程,以下是详细操作:
1. 场景准备:标记可行走区域与障碍物
设置静态物体层级:
选中场景中的 “地面、道路、平台” 等可行走静态物体,在 Inspector 勾选 Static → 选择 Navigation Static(标记为导航静态物体,用于烘焙 NavMesh)。
选中 “墙壁、柱子、岩石” 等静态障碍物,同样勾选 Static → Navigation Static(烘焙时会自动标记为 Non-Walkable)。
处理动态障碍物:
对可移动物体(如 NPC 推的箱子、开关控制的门),添加 NavMeshObstacle 组件,勾选 Carve(动态切割 NavMesh,确保 AI 不会穿过)。
2. 烘焙 NavMesh:生成导航网格
添加NavMeshSurface
组件,在NavMeshSurface
组件中,点击Bake完成烘焙。此时可以看到场景中出现了蓝色区域,这些区域就是允许导航的区域。
- 配置烘焙参数:
- Radius:烘焙时的采样半径(控制 NavMesh 与障碍物的距离,建议与 NavMeshAgent 的 Radius 一致)。
- Height:烘焙时的采样高度(控制 AI 能通过的最小高度,如矮门需调小)。
- Max Slope:AI 能爬上的最大坡度(RPG 中建议 30°~45°,避免 NPC 爬过陡的坡)。
- Step Height:AI 能跨越的台阶高度(如 0.3 米,适配角色模型的台阶通过能力)。
- 点击 Bake 按钮,Unity 会自动生成 NavMesh(场景中显示为蓝色网格,蓝色区域即 AI 可行走区域)。
3. 障碍物设置
如果在场景中需要添加阻碍角色移动的物体,你可以为其添加NavMeshObstacle
组件
4.配置 NavMeshAgent:让 AI 拥有寻路能力
给 RPG 中的 NPC(如敌人、商人)添加 NavMeshAgent 组件,适配角色特性:
- Speed:根据角色类型设置(如玩家同伴速度 3m/s,敌人速度 4m/s,商人速度 2m/s)。
- Radius:根据角色碰撞体半径设置(如人形角色设为 0.3m,避免与障碍物贴边)。
- Base Offset:如果角色模型的中心点不在脚底(如模型中心点在腰部),需设置偏移(如 0.9m,让 NavMeshAgent 的 “行走高度” 与模型匹配)。
- Stopping Distance:如果是 “追击玩家” 的敌人,设为 1m(靠近后攻击);如果是 “到达目标点” 的 NPC(如商人到摊位),设为 0.1m(精准停止)。
5. 编写基础寻路逻辑:控制 AI 移动到目标点
通过代码控制 NavMeshAgent 的目标点(SetDestination
方法),实现 AI 向指定位置 / 物体移动。以下是 RPG 中常见的基础案例:
案例1:NPC 跟随玩家(同伴跟随功能)
using UnityEngine;
using UnityEngine.AI;// 附加在同伴 NPC 上
public class FollowerAI : MonoBehaviour
{[Header("跟随配置")]public Transform player; // 玩家 Transformpublic float followDistance = 2f; // 跟随距离(保持在玩家身后 2 米)public float rotateSpeed = 5f; // 朝向玩家的旋转速度private NavMeshAgent agent;private Vector3 targetPosition; // 跟随目标位置void Start(){// 获取 NavMeshAgent 组件agent = GetComponent<NavMeshAgent>();// 禁用自动转向(手动控制朝向玩家,更自然)agent.updateRotation = false;}void Update(){if (player == null) return;// 计算“玩家身后 followDistance 米”的目标位置// 1. 获取玩家朝向的反方向(身后)Vector3 behindPlayer = -player.forward.normalized * followDistance;// 2. 目标位置 = 玩家位置 + 身后方向(保持 Y 轴与玩家一致,避免高度偏差)targetPosition = new Vector3(player.position.x + behindPlayer.x,player.position.y, // 与玩家同高度player.position.z + behindPlayer.z);// 让 NavMeshAgent 移动到目标位置agent.SetDestination(targetPosition);// 让 NPC 朝向玩家(平滑转向)if (agent.remainingDistance > agent.stoppingDistance){Quaternion targetRotation = Quaternion.LookRotation(player.position - transform.position);transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation, rotateSpeed * Time.deltaTime);}}
}
案例 2:敌人追击玩家(战斗寻路)
敌人需要 “检测到玩家后追击,超出范围后返回巡逻点”,逻辑如下:
using UnityEngine;
using UnityEngine.AI;public class EnemyChaseAI : MonoBehaviour
{[Header("寻路配置")]public Transform player; // 玩家目标public float chaseRange = 10f; // 追击范围public float attackRange = 1.5f; // 攻击范围public Vector3 patrolPoint; // 巡逻点(追击结束后返回)private NavMeshAgent agent;private bool isChasing = false; // 是否处于追击状态void Start(){agent = GetComponent<NavMeshAgent>();// 初始状态:移动到巡逻点agent.SetDestination(patrolPoint);}void Update(){if (player == null) return;// 计算与玩家的距离float distanceToPlayer = Vector3.Distance(transform.position, player.position);// 状态 1:检测到玩家,开始追击if (distanceToPlayer <= chaseRange && !isChasing){isChasing = true;agent.SetDestination(player.position); // 目标设为玩家Debug.Log("敌人开始追击!");}// 状态 2:追击到攻击范围,停止移动(准备攻击)if (isChasing && distanceToPlayer <= attackRange){agent.ResetPath(); // 停止寻路Debug.Log("敌人准备攻击!");// 这里可添加攻击动画、伤害判定逻辑}// 状态 3:超出追击范围,返回巡逻点if (isChasing && distanceToPlayer > chaseRange){isChasing = false;agent.SetDestination(patrolPoint); // 目标设为巡逻点Debug.Log("敌人返回巡逻点!");}// 状态 4:返回巡逻点后,停止移动if (!isChasing && agent.remainingDistance <= agent.stoppingDistance){Debug.Log("敌人到达巡逻点!");}}// Gizmos 绘制追击范围和巡逻点(场景视图调试用)void OnDrawGizmosSelected(){// 绘制追击范围(红色圆圈)Gizmos.color = Color.red;Gizmos.DrawWireSphere(transform.position, chaseRange);// 绘制攻击范围(橙色圆圈)Gizmos.color = Color.orange;Gizmos.DrawWireSphere(transform.position, attackRange);// 绘制巡逻点(蓝色立方体)Gizmos.color = Color.blue;Gizmos.DrawWireCube(patrolPoint, Vector3.one * 0.5f);}
}
三、RPG 进阶功能:扩展导航系统的能力
基础寻路只能满足简单移动,RPG 中还需要 “区域成本控制”“动态障碍物规避”“传送门 / 楼梯处理”“群体寻路” 等进阶功能:
1. 区域成本控制:让 AI 优先选择合理路径
在 RPG 中,AI 可能需要 “优先走平坦的道路,避开泥泞的草地” 或 “绕开危险的战斗区域”,这需要通过 NavMeshArea 配置区域成本 实现:
- 创建自定义区域类型:
- 打开 Navigation 面板 → Areas 标签页 → 点击 Add,创建自定义区域(如 “Grass”“Road”“Danger”)。
- 为每个区域设置 Cost(成本):Road 设为 1(最低成本,优先走),Grass 设为 5(较高成本,尽量避开),Danger 设为 100(极高成本,除非必经之路)。
- 给场景物体分配区域:
- 选中 “草地” 物体,在 Inspector 的 Navigation 面板中,将 Area 设为 “Grass”。
- 选中 “道路” 物体,将 Area 设为 “Road”。
- AI 优先选择低成本路径:NavMeshAgent 会自动根据区域成本计算 “总成本最低” 的路径,无需额外代码。例如:敌人追击玩家时,会优先走成本 1 的道路,而不是成本 5 的草地。
// 获取草地区域的 ID(Area Type 对应的索引,可在 Areas 标签页查看)
int grassAreaID = NavMesh.GetAreaFromName("Grass");
// 动态修改草地的移动成本
NavMesh.SetAreaCost(grassAreaID, 10f);
2. 动态障碍物处理:规避可移动物体
RPG 中常有 “可推动的箱子”“临时关闭的门” 等动态障碍物,需要用 NavMeshObstacle 让 AI 实时规避:
- 给动态物体添加 NavMeshObstacle:
- 选中可移动箱子,添加 NavMeshObstacle 组件,设置 Radius(与箱子碰撞体匹配)、Height(箱子高度)。
- 勾选 Carve(关键!让障碍物移动时 “切割” NavMesh,动态删除该区域的可行走路径)。
- 测试动态规避:
- 当箱子被玩家推动时,NavMeshObstacle 会实时更新 NavMesh,NavMeshAgent(如敌人)会自动绕开箱子,无需额外代码
public NavMeshObstacle doorObstacle;// 开门:禁用障碍物,允许 AI 通过
public void OpenDoor()
{doorObstacle.enabled = false;
}// 关门:启用障碍物,阻止 AI 通过
public void CloseDoor()
{doorObstacle.enabled = true;
}
3. 导航链接:处理传送门、楼梯、桥梁
RPG 中常需要 AI 通过 “传送门(跨场景 / 跨楼层)” 或 “楼梯(上下层)”,这些场景中 NavMesh 不相邻,需用 NavMeshLink 连接:
案例:传送门链接(跨区域移动)
- 在传送门入口和出口分别创建空物体,作为链接的 “起点(Start Point)” 和 “终点(End Point)”。
- 给起点空物体添加 NavMeshLink 组件:
- 设置 End Point 为出口空物体的 Transform。
- 勾选 Bidirectional(允许双向传送,如玩家和 NPC 都能通过)。
- 调整 Radius 确保链接覆盖可行走区域。
- 编写传送逻辑(当 AI 到达起点时,瞬间移动到终点):
using UnityEngine;
using UnityEngine.AI;public class PortalLink : MonoBehaviour
{public Transform targetPortal; // 目标传送门位置private NavMeshAgent agent;void OnTriggerEnter(Collider other){// 检测是否有 NavMeshAgent 的物体进入传送门agent = other.GetComponent<NavMeshAgent>();if (agent != null){// 暂停寻路,移动到目标位置agent.ResetPath();other.transform.position = targetPortal.position;other.transform.rotation = targetPortal.rotation;// 恢复寻路(如果有后续目标)agent.SetDestination(agent.destination);}}
}