3. NavMesh Agent:Agent组件参数详解、移动控制、避障与路径更新

好,咱们接着聊。上一节我们把NavMesh烘焙出来了,但光有路网还不够——你得有个“人”在上面走,对吧?这个“人”就是NavMesh Agent。说白了,Agent就是那个在导航网格上自动寻路的小家伙。

我个人习惯把Agent比作一辆自动驾驶的汽车。你告诉它目的地,它自己规划路线、自己踩油门、自己打方向盘,甚至还会避让路上的其他车。今天我们就来拆解这辆“车”的各个部件。

3.1 Agent组件参数详解

给一个GameObject添加NavMesh Agent组件很简单:选中物体,点Add Component,搜索NavMesh Agent。但加上之后,那一堆参数是什么意思?我刚开始用的时候也懵过。

参数 说明 我的建议值
Agent Radius 代理的半径,决定了它能通过多窄的通道 0.5(标准人形)
Agent Height 代理的高度,决定了它能钻过多低的障碍 2.0
Base Offset 代理底部与地面的偏移量 0.0(一般不动)
Speed 最大移动速度 3.5(看需求)
Angular Speed 转向速度,度/秒 120
Acceleration 最大加速度 8.0
Stopping Distance 距离目标多近时停止 0.5(近战单位可以更小)
Auto Braking 到达目标时是否自动刹车 勾选
Auto Repath 路径失效时是否自动重新寻路 勾选
Area Mask 代理能行走的区域类型 默认全选

核心要点:Radius和Height决定了Agent的“物理尺寸”。我在项目中遇到过一个问题:角色明明能走的路,Agent就是过不去。后来发现是Radius设大了,卡在门框上。你想想看,一个胖子非要走窄门,能不卡吗?

3.2 移动控制:让Agent动起来

参数调好了,怎么让它走?最核心的API就两个:SetDestinationMove

SetDestination 是最常用的。你给它一个Vector3坐标,它自己算路径、自己走。我建议你在Update里每帧调用,但别担心性能——Unity内部做了优化,路径不会每帧重算。

// 最简单的用法
NavMeshAgent agent = GetComponent<NavMeshAgent>();
agent.SetDestination(targetPosition);

// 判断是否到达
if (!agent.pathPending && agent.remainingDistance <= agent.stoppingDistance)
{
    // 到达目标,做点啥
    Debug.Log("到了!");
}

小技巧:判断到达时,别忘了检查 agent.pathPending。我曾经踩过这个坑——路径还没算完,remainingDistance 是0,结果一上来就判定“到达了”。加个判断就稳了。

Move 方法就简单粗暴了。它直接让Agent沿着某个方向移动,不涉及寻路。适合做击退、冲锋这类效果。

// 把Agent往后推
agent.Move(-transform.forward * 2f);

3.3 避障:多个Agent不打架

一个Agent走路没问题,两个Agent迎面走来怎么办?撞上?Unity内置了避障系统,默认就是开启的。

避障的核心参数在 Obstacle Avoidance 部分:

  • Quality:避障质量,从None到High。质量越高越精确,但性能开销也大。我一般用Medium,够用。
  • Priority:优先级,0-99。数值越小,优先级越高。高优先级的Agent会“命令”低优先级的让路。

为什么会这样?因为Unity的避障算法是基于速度障碍法(Velocity Obstacle)的。每个Agent会预测其他Agent的未来位置,然后调整自己的速度避免碰撞。说白了,就是大家互相“商量”着走。

注意:避障不是万能的。如果Agent数量太多(比如超过50个),或者通道太窄,避障可能会失效,出现“堵车”现象。我曾经在做一个RTS游戏时,100个单位挤一个城门,那场面...嗯,后来我加了排队逻辑才解决。

3.4 路径更新:动态环境下的应对

游戏世界不是一成不变的。门会打开、墙会倒塌、障碍物会移动。这时候Agent的路径需要更新。

Auto Repath 参数就是干这个的。勾选后,如果当前路径因为环境变化而失效,Agent会自动重新计算路径。我建议你永远勾上,除非你有特殊需求。

手动更新路径也很简单:

// 强制重新寻路
agent.ResetPath();
agent.SetDestination(newTarget);

还有一种情况:你移动了NavMesh Obstacle(导航网格障碍物)。这时候需要重新烘焙NavMesh,或者用 NavMeshSurface.UpdateNavMesh() 局部更新。嗯,这块内容我们后面章节会细讲,先留个印象。

避坑指南:我曾经在项目中遇到一个bug——角色走到一半,目标点移动了,但Agent还在往原来的位置走。排查了半天,发现是 SetDestination 只在开始时调用了一次。正确的做法是:如果目标点会移动,每帧都调用 SetDestination 更新目标。放心,性能开销很小。

3.5 实战小例子:巡逻AI

光说不练假把式。咱们写一个简单的巡逻AI,让Agent在几个点之间来回走。

public class PatrolAI : MonoBehaviour
{
    public Transform[] waypoints;
    private NavMeshAgent agent;
    private int currentIndex = 0;

    void Start()
    {
        agent = GetComponent<NavMeshAgent>();
        GoToNextPoint();
    }

    void Update()
    {
        // 到达当前巡逻点,去下一个
        if (!agent.pathPending && agent.remainingDistance < agent.stoppingDistance)
        {
            GoToNextPoint();
        }
    }

    void GoToNextPoint()
    {
        if (waypoints.Length == 0) return;
        agent.SetDestination(waypoints[currentIndex].position);
        currentIndex = (currentIndex + 1) % waypoints.Length;
    }
}

这个脚本很简单,但很实用。你可以在场景里放几个空物体作为巡逻点,把角色挂上这个脚本,它就能自己溜达了。

好了,关于NavMesh Agent的核心内容就这些。记住:Radius和Height是基础,SetDestination是核心,避障是锦上添花,路径更新是保底机制。下一节我们聊聊更高级的话题——如何用代码动态控制Agent的行为,让它更“智能”。