1. 静态导航烘焙基础从零搭建寻路网格刚接触Unity导航系统时我总喜欢把NavMesh想象成游戏世界里的隐形高速公路网。就像现实中的GPS需要地图数据一样NavMesh就是AI角色的导航地图。先来看最基础的静态烘焙流程场景准备创建一个带地面和障碍物的简单场景。比如用Cube搭建四面墙中间放几个立柱作为障碍。记住所有需要参与导航计算的静态物体地面、墙壁、柱子都要标记为Navigation Static就像给它们贴上我是固定路障的标签。烘焙参数设置在Navigation窗口的Bake页签你会看到几个关键参数Agent Radius相当于角色的体型宽度决定路径能通过多窄的通道Max Slope角色能爬的最大坡度Step Height可跨越的台阶高度// 最简单的导航脚本示例 using UnityEngine; using UnityEngine.AI; public class BasicNavigation : MonoBehaviour { public Transform target; private NavMeshAgent agent; void Start() { agent GetComponentNavMeshAgent(); agent.destination target.position; } }常见问题排查第一次烘焙时经常遇到路径不通的情况。我的经验是先用Debug模式查看NavMesh可视化蓝色区域表示可行走区域白色边缘是障碍物边界如果该连通的区域没有连通检查障碍物间距是否小于Agent Radius的两倍有个实用技巧在复杂场景中可以先用简单几何体代替高模进行烘焙测试确认路径可行后再用精细模型替换。我在一个中世纪城堡项目中先用Box代替所有装饰立柱测试寻路节省了大量调试时间。2. 动态目标点与点击移动实现静态烘焙只是开始真正的游戏世界需要动态响应。想象一下RTS游戏中鼠标点击移动的效果实现起来比想象中简单射线检测转换坐标核心是通过摄像机射线把屏幕点击转换成世界坐标。这里有个坑要注意射线碰撞的必须是NavMesh所在的层否则会点到空中楼阁。void Update() { if (Input.GetMouseButtonDown(0)) { Ray ray Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if (Physics.Raycast(ray, out hit, Mathf.Infinity, LayerMask.GetMask(Ground))) { agent.SetDestination(hit.point); } } }移动优化技巧添加StopDistance防止角色紧贴目标使用PathPending检查路径是否计算完成通过remainingDistance实现精准停靠实测发现当同时控制多个单位移动时每帧为所有单位计算路径会导致卡顿。我的解决方案是分帧处理用队列轮流更新不同单位的路径。3. 区域掩码与多路径选择就像现实中的高架桥和地下通道游戏里也常有并行的多条路径。Area Mask就是控制角色能走哪条路的开关区域划分实战在Navigation窗口的Areas页签定义不同区域类型如Walk、Jump、Bridge烘焙时不同区域会显示不同颜色通过NavMeshAgent的areaMask属性控制可通行区域// 只允许行走在Walk和Bridge区域 agent.areaMask (1 NavMesh.GetAreaFromName(Walk)) | (1 NavMesh.GetAreaFromName(Bridge));动态切换案例在一个塔防项目中我实现了这样的逻辑普通敌人走地面路径飞行敌人可以穿越禁行区BOSS单位能破坏特定路障关键代码是动态修改areaMaskvoid SwitchToFlyingMode() { agent.areaMask | (1 NavMesh.GetAreaFromName(Air)); agent.CalculatePath(transform.position, target.position, new NavMeshPath()); }4. 动态障碍物与实时导航更新终于来到最激动人心的部分——动态障碍物处理。就像《魔兽争霸》里可升降的城门这类效果需要NavMeshObstacle组件配合基础实现步骤给动态障碍物添加NavMeshObstacle组件设置合适的Shape类型Box/Capsule通过代码控制carving属性实时更新导航网格public class DynamicBridge : MonoBehaviour { private NavMeshObstacle obstacle; void Start() { obstacle GetComponentNavMeshObstacle(); obstacle.carveOnlyStationary false; // 允许移动时雕刻NavMesh } public void ToggleBridge(bool isOpen) { obstacle.enabled !isOpen; obstacle.carving isOpen; // 关键参数 } }性能优化经验大量动态障碍物时设置carveOnlyStationarytrue提升性能移动障碍物建议使用Capsule形状避免Box旋转时的计算误差频繁开关的障碍物可以缓存NavMeshData实例踩过的一个坑当障碍物移动速度过快时NavMesh更新可能跟不上。后来我改用NavMeshAgent的AvoidancePredictionTime参数解决了这个问题。5. 高级技巧与实战问题排查在真实项目中总会遇到各种妖魔鬼怪般的问题。分享几个救命技巧跳跃与空中路径通过OffMeshLink实现两点间的特殊连接设置autoTraverseOffMeshLinkfalse可以手动控制跳跃动画记得调整jumpDistance匹配角色动画长度斜坡与台阶处理陡坡上角色可能打滑调整NavMeshAgent.baseOffset楼梯行走不自然试试分层烘焙不同高度的NavMesh人群避让方案设置不同的priority值让重要角色优先通行通过avoidancePriority实现动态避让大量AI移动时分帧更新路径计算减轻性能压力// 人群避让优化示例 void Update() { if (Time.frameCount % 10 agentID % 10) { // 分帧更新 agent.SetDestination(target.position); } }最近在一个MMO项目中我们实现了200AI同时在动态变化的地图上寻路。关键是把动态障碍物按更新频率分组高频更新的障碍物如玩家召唤物用简化碰撞体低频更新的如战场机关用精确碰撞。