想象你第一次走进一座超大型商场:头顶挂着“前方电梯”的指示牌,但真正让你不迷路的,是脚下那一格格颜色略深的地砖——它们悄悄标记了所有可行走的区域,而货架、柜台、花坛的位置则被“画”成了不可跨越的禁区。商场管理员早在开门营业前,就拿着建筑蓝图把整层楼摸了个遍,把“哪块地砖能踩、哪块不能踩”整理成一张总图纸。之后每一位顾客(或者是会自己送货的机器人),只需要回答三个问题:我在哪、要去哪、下一脚踩哪块砖?这就是 Unity AI Navigation 的整套逻辑:它把一个复杂的三维世界,变成一张“踩不踩得上去”的图纸,再让角色像一个熟门熟路的老顾客一样,沿着地砖走出流畅的路线。 在 Unity 中,这张图纸叫 NavMesh(导航网格)。它不是实时扫描的,而是事先“烘焙”出来的多边形拼图,配合 Agent、Obstacle、Off-Mesh Link 等组件,构成了游戏世界中一切智能体移动的基础设施。无论你将来做开放世界 NPC、VR 虚拟导览,还是数字孪生工厂里的巡检机器人,理解这套“地砖系统”,就等于握住了让角色自己认路、绕路、抄近路的万能钥匙。

岗位关联

对 AI 从业者而言,NavMesh 是连接“算法世界”与“可交互 3D 场景”的桥梁。掌握它,你就能在虚拟仿真、机器人路径规划、游戏 AI 等方向,把抽象的寻路算法快速落地成用户看得见、摸得着的智能行为。

目录
  1. 可视化演示
  2. 地砖图纸:NavMesh 的烘焙原理
  3. 智能顾客:NavMesh Agent 组件
  4. 找路大脑:A* 与路径多边形
  5. 灵活绕行:动态障碍与避障
  6. 空中桥梁:Off-Mesh Link 跳跃
  7. 多层商场:分层与性能优化
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示系统处理流水线、实时性能指标曲线及多方案对比分析,包含数据流转、吞吐延迟监控与方案评估。

本节配有交互动效(进入视口后自动加载,约 3KB)。

系统可视化 处理流程:输入→处理→模型→输出 性能监控:实时指标 P=L×T

地砖图纸:NavMesh 的烘焙原理

商场开业前,总经理不会让每位顾客自己拿尺子去量货架的缝隙。他会请来一位“地砖规划师”,拿着建筑蓝图把整层楼走一遍:哪块地砖平整能踩,哪片货架区必须避开,哪段台阶高得让人望而却步。规划师把这些信息画成一张“可踩地砖总图”,贴在每个入口处。之后,任何顾客都不必再思考“这里能不能走”,只需对照总图找路。Unity 里这张总图,就是 NavMesh(导航网格)——把世界地图提前扫描、计算、归档成一张多边形拼图。

不过,画这张图没那么简单,因为角色是有“体积”的。一个贴墙走的壮汉和一个小孩子,需要的过道宽度完全不同。所以 Unity 在烘焙(Baking)时,会先把所有障碍物的边缘向外“膨胀”一个 Agent Radius(代理半径),相当于在地砖图上给每堵墙圈出一圈“安全警戒线”。半径越大,能走的路就越窄,这就是为什么同一个场景,换一个更胖的角色就要重新烘焙。

在 Unity 中执行烘焙时,引擎会把标记为 Navigation Static 的物体视为障碍物,把地面、斜坡、台阶按照 Max Slope(最大坡度)和 Step Height(最大台阶高度)筛选出真正的“可踩地砖”:

烘焙完成之后,场景视图里会出现一层半透明网格,这就是商场门口贴出的“总图”。请注意,烘焙是离线预计算:营业期间货架不动,地砖图就不用重画;一旦货架挪了位,就得重新请规划师进场测绘一次。

A={p∈S ∣ d(p,O)>ragent}A=\{p\in S\ \mid\ d(p,O)>r_{\text{agent}}\}
这个集合公式的意思是:可行走区域由所有“离障碍物足够远”的表面点组成,距离要大于角色半径,才能保证不擦墙、不卡缝。
  1. 表面集合 S表示场景中所有被当成“地面”的几何表面,也就是商场里所有铺了地砖的楼层表面。
    S=场景内所有候选地面三角面片S=\text{场景内所有候选地面三角面片}
  2. 距离函数 d(p,O)计算表面上的某个点 p 到最近障碍物 O 的直线距离,好比顾客站在这块地砖中心,量一量离货架还有多远。
    d(p,O)=min⁡o∈O∥p−o∥d(p,O)=\min_{o\in O}\|p-o\|
  3. 代理半径 r_agent角色身体占用的空间近似半径(通常取胶囊体宽度的一半),对应商场比喻里“顾客肩膀宽度”。
    ragent=Agent Radius 配置值r_{\text{agent}}=\text{Agent Radius 配置值}
  4. 严格大于号 >距离必须严格大于半径,否则角色走到那里会与障碍物发生擦碰,Unity 会直接放弃这块地砖。
    d(p,O)>ragentd(p,O)>r_{\text{agent}}
  5. 可行走集合 A把所有满足条件的地砖点收集起来,最终拼成一张绿色导航网格,这正是我们可视化演示里那一格格的“绿色地砖”。
    A=所有安全点的集合A=\text{所有安全点的集合}

智能顾客:NavMesh Agent 组件

图纸贴好了,可顾客不会读图怎么办?没关系,商场给每位“特殊顾客”发了一个会自动认路的智能小推车——它内置了传感器、转向轮和刹车,你只要告诉它“去三楼收银台”,剩下的全自动。在 Unity 中,这个小推车就是 NavMesh Agent 组件。把一个角色挂上 Agent,它便拥有了“看懂地砖图”的能力:无论你把它丢到绿色网格的哪个角落,它都能沿着网格移动,绝不会走到灰色货架上去。

使用方式极其简单:先给角色 GameObject 添加 NavMeshAgent 组件,然后在代码里调用 SetDestination(Vector3),Agent 就会自动计算路径并移动。真正让“推车”手感像真人的,是它的运动属性:Speed 决定最快步行速度,Acceleration 决定起步和刹车的平顺感,Angular Speed 决定转弯是否灵活。你不需要自己写逐帧位移逻辑,引擎已帮你封装好。

Agent 的移动不是一个瞬移过程,而是一个速度随时间平滑变化的物理过程。每帧它先按加速度提速,再被最大速度限制住:

有了这套机制,角色从静止到匀速再到刹停,动作就自然得像真人逛街。你还会注意到 Agent 有一个 Stopping Distance(停止距离)参数:它代表当推车距离目标点小于这个值时开始减速停车,避免了“冲到目标脚下再来个急刹车”的尴尬。

vnew=min⁡(vmax⁡, vold+a×Δt)v_{\text{new}}=\min\left(v_{\max},\ v_{\text{old}}+a\times\Delta t\right)
公式表示“新手开车”:每一帧想加速,但车速不能超过设定的最高限速,踩油门再猛也只能慢慢逼近极速。
  1. 旧速度 v_old上一帧结束时角色当前的移动速度,也就是推车此刻已经跑多快。
    vold=上一帧实际速度v_{\text{old}}=\text{上一帧实际速度}
  2. 加速度 a角色每秒钟能增加的速度量,数值越大起步越迅猛,好比推车电机的功率。
    a=Acceleration 配置值a=\text{Acceleration 配置值}
  3. 帧间隔 Δt两帧之间的时间差,Unity 中通常取 Time.deltaTime,用于把加速度换算成这一帧的速度增量。
    Δt=tnew−told\Delta t=t_{\text{new}}-t_{\text{old}}
  4. 速度上限 v_maxSpeed 属性设定的最大移动速度,任何情况下都不能超越,相当于商场的限速线。
    vmax⁡=Speed 配置值v_{\max}=\text{Speed 配置值}
  5. 取最小值 min从“加速后的速度”和“最高限速”中选小者,确保推车只会在安全范围内越跑越快。
    vnew=min⁡(加速结果, 限速)v_{\text{new}}=\min(\text{加速结果},\ \text{限速})

找路大脑:A* 与路径多边形

总图有了,车也发动了,可“怎么走”才是真正的智慧。想象你在商场里要去一家网红奶茶店,你心里其实在快速盘算:已经走过的路(不能回头),加上估计还要走多远,两条加起来最小的路线就是最优路线。A*(A-Star)算法干的正是这件事——它是 Unity 寻路的“大脑”,在 NavMesh 的多边形网格上寻找最短路径。

NavMesh 把地砖图抽象成一个个相邻的多边形,角色从一个多边形中心走到另一个多边形中心的代价是已知的,A* 在这个“多边形地图”上展开搜索。它维护一个优先级队列,每次取出“当前总代价最小”的节点向外探索,直到抵达目标。这里的核心是估价函数:

A* 搜索完成后,引擎会得到一条经过多个多边形走廊的路径,再经过拉直和拐角平滑处理,形成 Agent 实际跟随的折线轨迹。到达判定也很简单:当 Agent 的 remainingDistance(剩余距离)小于等于 stoppingDistance 时,它就知道“到了”,自动刹车停止。整个过程中,玩家甚至不需要知道算法细节,就像顾客不需要懂地图学也能逛完整个商场。

f(n)=g(n)+h(n)f(n)=g(n)+h(n)
总代价 f 等于“已经付出的代价 g”加上“未来还要付出的估计代价 h”,A* 永远优先尝试总代价最小的路,像极了精明的购物者。
  1. 当前节点 n正在被考察的地砖多边形,可理解为推车当前停在哪个导航区域。
    n∈NavMesh 多边形节点n\in\text{NavMesh 多边形节点}
  2. 已走代价 g(n)从起点走到当前节点已经消耗的路径长度,这是确定值,每走一步都会累加。
    g(n)=起点到 n 的实际最短距离g(n)=\text{起点到 n 的实际最短距离}
  3. 启发代价 h(n)从当前节点到终点的“直线估计距离”,通常用欧氏距离,它未必精确但绝不能高估。
    h(n)=∥n−goal∥h(n)=\|n-\text{goal}\|
  4. 总估价 f(n)两者相加,作为优先级排序的依据,A* 每轮都从开放列表中挑 f 最小的节点扩展。
    f(n)=已走实账+剩余预估f(n)=\text{已走实账}+\text{剩余预估}

灵活绕行:动态障碍与避障

商场难免有意外:保洁阿姨推着清洁车堵住了过道,或者一辆补货小车临时停在电梯口。如果每次移动一个物体都要重新烘焙整张总图,那商场管理员得累死。所以,静态货架和动态障碍物必须分开处理。烘焙完成的 NavMesh 只对“永远不动”的物体负责;而那些会临时出现、移动的物体,Unity 提供了 NavMesh Obstacle 组件。

当一个动态物体挂上 Obstacle 组件并开启 Carve(雕刻)选项后,它就像一把“地砖切割刀”——运行时在导航网格上实时挖掉一块区域,迫使 Agent 绕行。等这个物体移开,被挖掉的地砖又会自动补回来,完全不需要重新烘焙。这正是我们在可视化里看到的情景:灰色货架挡住了路,蓝色主角沿着绿色地砖绕了一个弧线继续前进。

除了躲避静态挖洞,多个 Agent 之间还会互相让路,这叫局部避障(Local Avoidance)。每个 Agent 根据自己和邻居的速度、方向,计算一个不会相撞的安全速度:

需要注意的是,Carve 的实时计算有一定开销,所以不要把成百上千个 Obstacle 都开启 Carve。实际项目中,通常只对少数几个关键可移动物体开启雕刻,其余的交给物理碰撞或局部避障解决,这样商场的“地砖维护系统”才能既灵活又高效。

Awalk(t)=Abase∖Acarve(t)A_{\text{walk}}(t)=A_{\text{base}}\setminus A_{\text{carve}}(t)
可走区域等于“基础地砖”减去“当前时刻被动态物体挖掉的部分”,随着时间变化,挖洞的位置也跟着变。
  1. 基础网格 A_base最初烘焙生成的完整可行走区域,也就是商场原始的绿色地砖总图。
    Abase=初始 NavMesh 网格集合A_{\text{base}}=\text{初始 NavMesh 网格集合}
  2. 雕刻区域 A_carve(t)在 t 时刻,所有激活了 Carve 的动态障碍物在网格上占据的区域,类似清洁车此刻挡住的那几块地砖。
    Acarve(t)=⋃动态障碍物占用的多边形A_{\text{carve}}(t)=\bigcup\text{动态障碍物占用的多边形}
  3. 集合差运算符 \setminus表示从基础网格中减去雕刻区域,得到此刻真正能走的路。
    Abase∖Acarve(t)A_{\text{base}}\setminus A_{\text{carve}}(t)
  4. 动态可走区域 A_walk(t)最终结果,它会随时间变化,Agent 每帧读取它调整路径,实现“现走现算”的灵活绕行。
    Awalk(t)=当前帧可用的导航网格A_{\text{walk}}(t)=\text{当前帧可用的导航网格}

空中桥梁:Off-Mesh Link 跳跃

商场里并不是所有区域都连着同一层地砖:二楼到三楼要坐扶梯,室内和室外天台之间还有一座天桥。对 NavMesh 来说,这些“断头路”天生无法连通,系统不可能自动知道扶梯在哪里。这时就需要人工搭建一座“空中桥梁”——Off-Mesh Link 组件。

Off-Mesh Link 的工作方式很直观:在场景中放置两个空物体,一个放在桥梁起点,一个放在终点,然后把它俩拖进 Off-Mesh Link 的 Start 和 End 字段。烘焙时,Unity 会在这两个点之间生成一条隐形的“传送走廊”。当 Agent 走到起点附近,它会自动被“吸”进这条走廊,从起点平滑移动到终点——就像顾客踏上自动扶梯,不用自己爬楼梯就完成了楼层切换。

Agent 跨越 Link 时的移动是一个标准的插值过渡:

默认情况下 Off-Mesh Link 是双向的:顾客既能从起点到终点,也能从终点返回起点。如果你只想让它支持单向跳跃,取消勾选 Bidirectional 即可。值得注意的是,Link 只负责“位置转移”,跳跃动画、音效还得自己写触发器来处理——桥梁搭好了,怎么走过去仍是角色自己的事。

P(t)=Pstart+(Pend−Pstart)×t,0≤t≤1P(t)=P_{\text{start}}+(P_{\text{end}}-P_{\text{start}})\times t,\quad 0\leq t\leq 1
位置在起点和终点之间按比例 t 线性过渡:t=0 时在起点,t=1 时在终点,中途恰好走完整个扶梯。
  1. 起点坐标 P_startOff-Mesh Link 的 Start 物体在世界空间的位置,即扶梯的入口地砖处。
    Pstart=(xs,ys,zs)P_{\text{start}}=(x_s,y_s,z_s)
  2. 终点坐标 P_endLink 的 End 物体的位置,即扶梯出口对接的那块地砖。
    Pend=(xe,ye,ze)P_{\text{end}}=(x_e,y_e,z_e)
  3. 位移向量 P_end-P_start从入口指向出口的方向和距离,告诉 Agent 要往哪个方向、移动多远。
    ΔP=Pend−Pstart\Delta P=P_{\text{end}}-P_{\text{start}}
  4. 插值进度 t0 到 1 之间的时间比例,由 Link 的移动耗时换算而来,t=0.5 表示已经走完一半扶梯。
    0≤t≤10\leq t\leq 1
  5. 当前坐标 P(t)t 时刻 Agent 所在的位置,当 t 从 0 增长到 1,角色就从入口平滑抵达出口。
    P(t)=起点按比例向终点滑动P(t)=\text{起点按比例向终点滑动}

多层商场:分层与性能优化

真正的大商场绝不只有一层,楼层之间通过扶梯相连。如果规划师把整栋楼画成一张巨大的总图,不仅烘焙耗时极长,而且只要三楼挪了一个货架,整栋楼的图纸都要重画。聪明的做法是按楼层分别烘焙,再通过 Off-Mesh Link 连接——这就是 Unity 中的分层导航(Area/Layer)思想。

NavMesh 的 Area 可以让不同区域拥有不同“通行代价”:例如“走廊”区域代价低,“泥地”区域代价高,寻路时自动优先走造价低的道路。而 NavMesh Surface 组件则把烘焙从“整场景一次性”变成“按需局部生成”。你可以给每一块室内地板挂一个 Surface,运行时只更新角色附近的小块网格,就像商场哪块地砖松了就只修哪块,不必砸掉整层楼的地面。

多块区域组合起来,就是整个商场的完整路网:

这种分层设计带来了立竿见影的渲染与计算效率提升:大世界不再需要一次性烘焙全部地形,而是随玩家移动流式加载;修改局部场景时,只需重建受影响的那一小块 Surface,其他区域的 Agent 路径完全不受干扰。在优化实践中,还可以通过减少动态 Carve 数量、控制 Agent 数量、使用多线程寻路等手段,让商场里上千名“顾客”同时逛店也不卡顿。

Swalkable=⋃i=1nSiS_{\text{walkable}}=\bigcup_{i=1}^{n}S_i
总可行走区域等于各个楼层/区域地砖的并集,区域之间靠 Off-Mesh Link 粘合,合并后构成一张完整的全局导航图。
  1. 楼层网格 S_i第 i 层地砖(即某一块 NavMesh Surface 生成的局部网格),只覆盖商场的一部分。
    Si=第 i 块局部导航网格S_i=\text{第 }i\text{ 块局部导航网格}
  2. 并集运算符 ∪把所有局部地砖拼在一起,允许相邻边界无缝衔接,形成连贯的寻路空间。
    A∪B=两块区域的所有可走点A\cup B=\text{两块区域的所有可走点}
  3. 楼层数量 n场景中划分的 Surface 或 Area 总数,分得越细,更新单个区域的开销就越小。
    n=局部区域数量n=\text{局部区域数量}
  4. 总可行走集合 S_walkable全商场最终可寻路的完整网格,Unity 在此之上执行全局 A* 路径搜索。
    Swalkable=全局 NavMesh 路网S_{\text{walkable}}=\text{全局 NavMesh 路网}

AI 应用场景

开放世界游戏 NPC 智能导航

阶段:导航网格生成与动态寻路

解决的问题:开放世界地图面积大、房屋树木多,传统手工 Waypoint(路径点)既繁琐又容易因场景微调而失效。NavMesh 自动烘焙出可行走区域,NPC 在追逐玩家或执行巡逻任务时,能实时算出绕过建筑和野怪的合理路线。

具体案例:在一款 RPG 中,村庄的守卫需要从村口巡逻到铁匠铺。场景里有多个房屋、水井和随机刷新的野怪。开发者将地面标记为 Navigation Static 并烘焙 NavMesh,再给守卫挂上 NavMesh Agent;玩家引怪堵路时,守卫通过动态避障自动绕路,无需设计师手动铺设每一条巡逻线。

数字孪生工厂 AGV 巡检

阶段:局部快速重规划与动态障碍避让

解决的问题:智能工厂中货架位置经常调整,AGV 小车若使用静态预设路线,一旦遇到临时堆放的物料就会死锁。NavMesh Surface 支持局部重烘焙,AGV 只需调用 SetDestination,即可在新地图上实时算出安全路径。

具体案例:某汽车工厂的 AGV 需要从仓库送零件到装配线,途中有移动的叉车和临时堆料区。工程师为每块厂房地面配置 NavMesh Surface,并在叉车上挂 NavMesh Obstacle 且开启 Carve。叉车一移动,导航网格动态挖洞,AGV 随即绕行,全程无需停机重刷地图。

Python 演示

python70 行
import heapq # 1 表示可走地砖, 0 表示障碍物(货架)GRID = [    [1, 1, 1, 1, 1, 1, 1, 1],    [1, 1, 0, 1, 1, 1, 1, 1],    [1, 1, 1, 1, 1, 0, 1, 1],    [1, 0, 1, 1, 1, 1, 1, 1],    [1, 1, 1, 1, 1, 1, 1, 1],] START = (0, 4)  # 起点(左下角)GOAL = (7, 0)   # 终点(右上角)  def neighbors(node):    r, c = node    for dr, dc in ((1, 0), (-1, 0), (0, 1), (0, -1)):        nr, nc = r + dr, c + dc        if 0 <= nr < len(GRID) and 0 <= nc < len(GRID[0]) and GRID[nr][nc] == 1:            yield nr, nc  def manhattan(a, b):    return abs(a[0] - b[0]) + abs(a[1] - b[1])  def astar(start, goal):    frontier = [(0, start)]    came_from = {}    cost_so_far = {start: 0}    while frontier:        _, current = heapq.heappop(frontier)        if current == goal:            break        for nxt in neighbors(current):            new_cost = cost_so_far[current] + 1            if new_cost < cost_so_far.get(nxt, float("inf")):                came_from[nxt] = current                cost_so_far[nxt] = new_cost                heapq.heappush(frontier, (new_cost + manhattan(nxt, goal), nxt))    path = []    node = goal    while node in came_from:        path.append(node)        node = came_from[node]    path.append(start)    return path[::-1]  def show_map(path):    for r, row in enumerate(GRID):        line = ""        for c, cell in enumerate(row):            if (r, c) == path[0]:                line += "S"            elif (r, c) == path[-1]:                line += "G"            elif (r, c) in path:                line += "*"            elif cell == 0:                line += "#"            else:                line += "."        print(line)    print("path:", path)  if __name__ == "__main__":    show_map(astar(START, GOAL))
目的:用 2D 地砖网格模拟 NavMesh 的可行走区域,以 A* 算法实现“找路大脑”,最终打印出从起点 S 到终点 G 的最优路径。
  1. GRID迷你版“地砖图纸”,1 代表绿色可走地砖,0 代表灰色货架障碍物。
  2. neighbors(node)返回上下左右四个相邻地砖,相当于检查角色下一步能踩哪几块砖。
  3. manhattan(a, b)曼哈顿距离充当 A* 的启发函数 h,它永远不高于实际代价。
  4. frontier = [(0, start)]优先级队列,存放待考察的节点,A* 每次取出 f 值最小的节点扩展。
  5. new_cost + manhattan(nxt, goal)这就是 f=g+h,把已走代价和未来估计加在一起。
  6. came_from[node]记录每个节点是从哪里走来的,最后倒推还原整条路径。
  7. show_map(path)把地砖图纸画出来:S 是起点,G 是终点,* 是选中的绿色路径,# 是障碍物。

常见误区

误区我以为只要给角色挂上 NavMesh Agent,它就会像雷达一样实时扫描周围地形,自己生成可走区域。场景里随便放个箱子,Agent 都应该马上绕开它。

正解NavMesh 本身是离线烘焙的产物,就像商场地砖是开业前画好的;运行时 Agent 只是在这张已经画好的网格上查询路径。烘焙后新增的静态物体,Agent 根本看不见——除非它带有动态障碍组件,或者你重新烘焙/局部更新 Surface。

为何常见:因为 Agent 的实时避障表现太自然了,看起来像是在“思考”,很容易让人误以为网格也是实时生成的。

误区我挂上 NavMesh Agent 后,让它朝一个 Rigidbody 箱子走过去,以为 Agent 会自动把箱子推开继续前进,结果角色卡在箱子前面不停抖。

正解NavMesh Agent 只负责“走路”,不负责“推东西”。它沿网格移动时默认避开障碍,如果路径被动态障碍堵死,它会停下来等待或重试。想让角色推动箱子,需要给箱子加 Rigidbody 并编写推挤逻辑,或者使用物理材质和力来实现。

为何常见:在 RPG 里我们经常看到 NPC 推门、推箱子,容易以为这是导航组件自带的功能,实际是开发者特意写的物理交互。

误区我搭好 Off-Mesh Link,角色走到起点后确实瞬移到了终点,但没有任何跳跃动作,看起来像是被 bug 传送过去的。

正解Off-Mesh Link 只负责“路径转移”——把 Agent 从起点插值移动到终点。跳跃、攀爬、翻墙的动画需要你在 OnLinkTraversal 或动画状态机里自行触发。Unity 可以感知 Link 的开始和结束,但演出效果完全由你编排。

为何常见:因为 Link 在编辑器中表现为一条连接线,很多人把它当成“自动播放动作的传送门”,忽略了引擎的职责边界。

误区我在烘焙完成后移动了一面墙,发现 Agent 直接穿墙而过。我以为 NavMesh 会像 NavMesh Obstacle 一样自动适应变化。

正解被标记为 Navigation Static 的静态物体一变,原网格就“过期”了,必须重新烘焙,或改用 NavMesh Surface 做局部重建。只有当移动的物体挂的是 NavMesh Obstacle 时,运行时才会动态挖洞。穿墙说明你移动了静态物体而没重建网格。

为何常见:动态障碍的实时雕刻效果让人产生错觉,以为所有场景变化都能被导航系统自动感知,混淆了静态网格和动态 Obstacle 的分工。

面试题

Q1. NavMesh 在 Unity 中的本质是什么?

单选题 · easy

  • A.实时计算的最短路径树
  • B.预先生成的可行走网格← 正确答案
  • C.深度学习训练的路径模型
  • D.逐帧生成的碰撞检测框

答案:B。NavMesh 是离线烘焙得到的“可行走多边形网格”,像商场地砖图一样在运行前就画好。A 选项把寻路结果和网格混为一谈,C 是误用 AI 概念,D 是物理组件而非导航网格。

Q2. 烘焙 NavMesh 时设置 Agent Radius 的作用是什么?

单选题 · easy

  • A.控制角色行走动画的快慢节奏
  • B.判断障碍物外扩的通过半径← 正确答案
  • C.调节相机裁剪范围的缩放半径
  • D.设置光源照射半径调节明暗

答案:B。Agent Radius 决定障碍物边缘向外膨胀多少,防止角色擦墙或卡缝。它影响地砖“安全警戒线”的宽窄,与动画、相机、光照均无关。

Q3. NavMesh Agent 的 Stopping Distance 参数表示什么?

单选题 · mid

  • A.角色可移动的最大总里程数
  • B.抵达目标附近触发停下的距离← 正确答案
  • C.从最高速刹到零的滑行路程
  • D.允许脱离网格的偏离容差

答案:B。Stopping Distance 是角色离目标点多近时开始停车,相当于推车快到收银台前提前减速的距离。它不限制总里程、不描述刹停过程,也不允许角色偏离网格。

Q4. Off-Mesh Link 在游戏中最典型的用途是什么?

单选题 · mid

  • A.连接高低差平台让角色跳跃← 正确答案
  • B.渲染一座三维桥梁的实体模型
  • C.压缩纹理贴图提高手机帧率
  • D.同步在线玩家的动画播放状态

答案:A。Off-Mesh Link 用于连接两段不相连的 NavMesh,比如悬崖两端的跳跃点或楼层间的扶梯,本质是“空中桥梁”。它不负责渲染模型、压缩纹理或同步动画。

Q5. NavMesh Obstacle 开启 Carve 选项后会发生什么?

单选题 · mid

  • A.让障碍物在网格上动态挖洞← 正确答案
  • B.永久雕刻出一片新的战场地形
  • C.把角色皮肤雕刻出肌肉纹理
  • D.优化动态阴影的裁剪计算开销

答案:A。Carve 会在运行时从 NavMesh 上实时挖掉动态障碍物占据的区域,等效于“清洁车挡住几块地砖,Agent 自动绕行”。它是临时挖洞,不是永久改地形,也与渲染无关。

Q6. 在 A* 算法中,启发函数 h 满足什么条件才能保证找到最短路径?

单选题 · hard

  • A.启发值大于真实剩余代价
  • B.启发值小于等于真实剩余代价← 正确答案
  • C.启发值取任意常数也能保证最优
  • D.启发值必须严格等于零才可行

答案:B。只有 h(n) 不大于真实剩余代价(可采纳性)时,A* 才能保证最优解。若 h 高估实际代价,算法可能错过真正的最短路径;h 等于零会退化成 Dijkstra,仍可最优但搜索范围大、效率低。

Q7. 下列哪个是 NavMesh Agent 的移动控制属性?

单选题 · easy

  • A.控制移动快慢的 Speed← 正确答案
  • B.物理碰撞计算中的 Mass
  • C.材质表面摩擦力的 Friction
  • D.细节层次渲染的 LOD

答案:A。Speed 是 Agent 自身的最大移动速度,直接决定角色走多快。Mass、Friction 属于 Rigidbody 物理系统,LOD 属于渲染性能优化,均与 Agent 移动控制无关。

Q8. 下列哪些情况需要重新烘焙或局部更新 NavMesh?

多选题 · mid

  • A.移动了静态货架障碍物的位置← 正确答案
  • B.修改了地面坡度可行走角度← 正确答案
  • C.换了一套角色皮肤和配音音效
  • D.把相机分辨率从 720 提升到 4K

答案:AB。静态障碍移动和坡度调整都会改变“地砖图纸”,必须重烘焙或使用 Surface 局部更新。换皮肤、调分辨率只影响渲染表现,不改变场景的可行走几何。

Q9. 关于动态障碍物和 Agent,下列说法正确的是?

多选题 · hard

  • A.移动角色应挂载 Agent 组件← 正确答案
  • B.可推拉的箱子用 Obstacle 组件← 正确答案
  • C.Agent 寻路中能实时修正路线← 正确答案
  • D.动态障碍物不会影响导航网格

答案:ABC。角色用 Agent 让“小推车”自动移动;可移动道具用 Obstacle 并开启 Carve 让网格动态挖洞。Agent 在遭遇障碍时会实时修正路径。D 错误——开启 Carve 的障碍物确实会临时修改导航网格。

Q10. 下列哪些方法能提升大世界 NavMesh 的运行性能?

多选题 · hard

  • A.分块烘焙降低单帧计算量← 正确答案
  • B.关闭所有 Agent 的碰撞检测
  • C.用 Surface 做局部增量更新← 正确答案
  • D.无限提高网格精度换取美观

答案:AC。分块烘焙和 Surface 局部增量更新都能避免一次性重建整张地图,是官方推荐方案。关闭碰撞检测会让角色穿模,无限提高网格精度则会大幅增加烘焙和寻路开销,均不可取。