1. 项目概述为什么Unity 2D游戏需要智能寻路在开发2D游戏尤其是俯视角、RPG、塔防或者策略类游戏时一个最核心也最让人头疼的问题就是如何让游戏里的角色自己找到路你肯定不想让玩家去手动点击屏幕上的每一个格子来指挥角色移动那太笨拙了。更不想看到你的NPC或者敌人像个没头苍蝇一样撞墙或者卡在某个角落出不来。Unity自带的NavMesh系统非常强大但它是为3D世界设计的。在3D里寻路是在一个三维的“地面”网格上计算的。但到了2D世界事情就变了。我们的游戏世界是一个平面角色在X和Y轴上移动Z轴可能只用来处理渲染层级。直接把3D的NavMesh用在2D场景上会遇到坐标轴不对、碰撞体不匹配、烘焙Bake出来的导航区域错位等一系列问题。这时候NavMeshPlus就登场了。它不是一个全新的寻路引擎而是一个基于Unity官方NavMeshComponents的扩展Extension系统。简单说它把3D的导航网格“拍扁”并适配到了2D坐标系里同时提供了专门用于收集2D场景元素如Tilemap瓦片地图、Sprite精灵、Collider2D碰撞体作为障碍物的工具。这样一来我们就能在2D游戏里享受到和3D游戏一样成熟、高效的A*寻路算法带来的便利——自动绕过障碍、寻找最短路径、动态更新导航区域。我最近在一个2D俯视角的生存建造游戏里深度使用了NavMeshPlus让村民可以自动去砍树、采矿、搬运资源回仓库整个过程完全无需玩家微操。踩过不少坑也总结出了一套从原理到落地的完整流程。这篇文章我就来详细拆解如何用NavMeshPlus实现2D智能寻路让你也能轻松搞定游戏里的“自动驾驶”。2. 核心原理与架构拆解NavMeshPlus是如何工作的在动手写代码和摆弄组件之前理解NavMeshPlus底层是怎么转起来的能帮你避开后面90%的坑。它并不是魔改Unity的物理引擎或者重写寻路算法而是巧妙地“桥接”和“转换”。2.1 Unity NavMesh系统基础回顾首先我们要明白Unity原生的NavMesh系统工作流烘焙Bake在编辑模式下系统会扫描场景中所有标记为“Navigation Static”的物体及其Mesh Renderer根据它们的形状和位置在“可行走”的表面通常是Y轴朝上的平面生成一个由三角形构成的导航网格NavMesh。这个网格定义了AI可以行走的区域。导航代理NavMeshAgent这是一个挂在游戏对象如你的角色上的组件。它包含了速度、加速度、转向、半径等参数。你给Agent一个目标位置它就会利用A*算法在导航网格上计算出一条路径并控制角色移动。障碍物NavMeshObstacle动态障碍物Agent在寻路时会实时避开它们。这个流程的核心是“3D网格”。而在2D中我们的“地面”是XY平面障碍物是Sprite或Tilemap它们没有3D Mesh只有2D Collider。2.2 NavMeshPlus的“二维化”魔法NavMeshPlus的核心思路是在3D NavMesh系统的框架内模拟一个2D的“世界”。坐标转换与表面旋转这是最关键的一步。NavMeshSurface组件来自NavMeshComponents默认认为可行走面是XZ平面Y轴向上。在2D游戏中我们的相机通常是正交投影Orthographic看向XY平面Z轴指向屏幕内。NavMeshPlus通过一个“Rotate Surface to XY”的操作本质上是将NavMeshSurface组件所在的GameObject旋转-90度绕X轴。这样原本的XZ平面就变成了XY平面Z轴变成了“高度”在纯2D中我们通常忽略它。后续所有的烘焙和寻路计算都基于这个旋转后的新坐标系。2D数据收集器NavMeshCollectSources2d这是NavMeshPlus的明星组件。Unity原生的烘焙器只认识3D Mesh。NavMeshCollectSources2d组件的工作就是遍历场景找到所有的Tilemap、带有SpriteRenderer和Collider2D的物体将这些2D几何信息“转换”成NavMesh系统能理解的3D Mesh数据Sources。对于Tilemap它会读取每个有碰撞体的瓦片Tile将其矩形轮廓转换为3D多边形。对于Sprite/Collider2D它会根据Collider2D的形状Box, Circle, Polygon等生成对应的3D碰撞体形状。 这些被转换后的“源数据”会被喂给Unity的NavMeshBuilder去生成真正的导航网格。世界边界与行走区域在3D中导航网格烘焙在场景几何体上。在2D中我们需要先定义一个“可行走的矩形区域”。NavMeshPlus会根据你场景中2D碰撞体的边界自动计算出一个包围盒World Bounds或者你也可以手动指定。烘焙时会先在这个矩形区域内生成一个完整的“行走面”网格然后再根据收集到的2D障碍物源在这个网格上“挖洞”形成不可行走区域。注意这里有一个非常重要的概念区分。NavMeshPlus生成的不是一个“点阵”或“网格图”它生成的依然是三角形网格。只是这个网格是铺在XY平面上的。Agent的寻路是在这个三角形网格上进行的因此路径是平滑的曲线而不是生硬的网格拐点。这对于追求自然移动感的游戏至关重要。2.3 核心组件一览了解这几个组件你就掌握了NavMeshPlus的命脉NavMeshSurface (来自Unity)导航网格表面的容器和烘焙执行者。挂载它并旋转后它定义了2D导航网格所在的平面。NavMeshCollectSources2d (NavMeshPlus)2D源收集器。必须和NavMeshSurface挂在同一GameObject上。负责搜集Tilemap、Sprite等并转换为导航数据。NavMeshModifier (来自Unity)区域修改器。挂在具体的障碍物物体上可以覆盖该障碍物所在区域的导航网格类型例如标记为“不可行走”、“沼泽减速”、“水域不可通行”等。NavMeshAgent (来自Unity)导航代理。挂在需要自动寻路的角色上是寻路的执行者。NavMeshLink (来自Unity)导航链接。用于连接两个被断开如沟壑、楼梯的导航网格区域实现“跳跃”或“传送”效果。在2D中可用于实现上下楼梯、跳平台等。3. 环境准备与项目集成三种安装方式详解理论懂了接下来就是动手。把NavMeshPlus弄进你的Unity项目有三种主流方法各有利弊。3.1 方式一使用Package ManagerGit URL—— 推荐这是最干净、最便于管理的方式尤其适合使用Git进行版本控制的项目。在Unity编辑器中打开Window Package Manager。点击左上角的“”按钮选择“Add package from git URL...”。在弹出的输入框中粘贴NavMeshPlus的Git仓库地址https://github.com/h8man/NavMeshPlus.git点击Add。Unity会自动从GitHub克隆仓库并作为包导入。优点集成在Package Manager中与项目Assets分离干净整洁。更新相对方便可指定分支或标签。缺点需要网络且依赖Git。如果公司内网限制Git可能无法使用。3.2 方式二修改manifest.json文件—— 灵活控制版本如果你需要锁定某个特定版本或者项目结构有特殊要求可以直接修改包清单文件。用文本编辑器打开你项目根目录下的Packages/manifest.json文件。在dependencies: { ... }部分添加一行com.h8man.2d.navmeshplus: https://github.com/h8man/NavMeshPlus.git#master如果你想使用某个稳定版本查看GitHub的Release或Tags可以将#master替换为#v1.0.0这样的标签名。保存文件回到Unity它会自动开始导入包。优点版本控制精确配置一次即可。适合团队协作确保所有人环境一致。缺点对不熟悉json和包管理的开发者稍有门槛。3.3 方式三直接下载源码到Assets—— 传统直接如果你不想碰Package Manager或者项目非常老这是最直接的方法。访问 NavMeshPlus的GitHub页面 。点击Code Download ZIP将源码下载到本地并解压。在你的Unity项目Assets文件夹下创建一个合适的子文件夹例如Plugins/NavMeshPlus。将解压后文件夹内的Assets/NavMeshComponents和Assets/Gizmos等内容复制到你刚刚创建的Plugins/NavMeshPlus文件夹内。优点无需网络和Git最直观。可以直接查看和修改源码但需注意许可证。缺点污染Assets目录更新麻烦需要手动替换文件。不利于包管理。实操心得对于新项目我强烈推荐方式一Package Manager。它代表了Unity现代的资源管理方式。如果方式一失败再退而求其次用方式二。方式三仅作为备用方案。集成成功后你可以在Package Manager的“My Registries”或“In Project”列表里看到com.h8man.2d.navmeshplus。4. 场景配置与导航网格烘焙一步步构建可行走世界安装好插件我们开始实战。假设我们有一个简单的2D俯视角场景地面由Tilemap铺成上面散落着一些树木Sprite和石头Sprite作为障碍物。4.1 基础场景设置创建地面使用Unity的Tilemap系统创建一个地面层。确保你的Tile Palette中的瓦片都设置了合适的Collider 2D通常是Tilemap Collider 2D组件并勾选Used By Composite然后添加Composite Collider 2D组件以优化碰撞体。地面瓦片本身不应该被标记为障碍物它只是背景。放置障碍物创建一些精灵Sprite作为树木和石头为它们添加Box Collider 2D或Polygon Collider 2D。这些将是寻路时需要绕开的物体。4.2 配置NavMeshSurface与2D收集器这是核心步骤。在场景层级Hierarchy的根目录下创建一个空的GameObject命名为Navigation。选中这个Navigation对象在检查器Inspector中点击Add Component。搜索并添加NavMesh Surface组件。这个组件来自Unity的AI Navigation包如果你还没导入Unity会提示你导入确认即可。继续在Navigation对象上点击Add Component搜索并添加NavMesh Collect Sources 2d组件。这是NavMeshPlus提供的。现在点击NavMesh Surface组件上的Rotate Surface to XY按钮。你会看到该GameObject的Rotation值变为(-90, 0, 0)。这一步至关重要它把导航表面从XZ平面旋转到了XY平面。4.3 设置障碍物区域类型我们不想让角色穿过树木和石头所以需要将它们标记为“不可行走”。选中场景中的一棵“树”Sprite物体。点击Add Component搜索并添加NavMesh Modifier组件。在该组件中勾选Override Area。在Area Type下拉菜单中选择Not Walkable。Unity内置了多种区域类型Not Walkable表示完全不可通行。确保Apply To Children也勾选如果你的树是一个有子物体的预制件。对所有的树木、石头重复此操作。你也可以为河流、沼泽等地形设置不同的区域类型如自定义的“Slow”慢速区域。4.4 烘焙导航网格一切就绪开始生成导航网格。选中之前创建的Navigation游戏对象。在其NavMesh Surface组件上找到Agent Type。通常使用默认的Humanoid即可你也可以在Window AI Navigation的Agents标签页自定义代理尺寸。点击Bake按钮。烘焙完成后切换到Scene视图在顶部工具栏的Gizmos下拉菜单中确保Navigation是开启状态。你就能看到场景中覆盖了一层蓝色的网格——这就是生成的2D导航网格蓝色的区域是可行走的障碍物所在的地方则是“镂空”的。烘焙参数详解Agent Radius代理的半径。路径会与此半径的障碍物保持距离。如果你的角色体积小可以设小一点如0.1以获得更贴近障碍物的路径。Agent Height在2D中通常不重要但保留一个较小值如0.5即可。Max Slope和Step Height在纯2D俯视角游戏中这两个参数基本无关因为不存在斜坡和台阶。可以忽略。Drop Height和Jump Distance用于生成Off-Mesh Links导航链接在2D平台跳跃游戏中可能有用俯视角游戏一般不用。踩坑记录第一次烘焙后如果看不到蓝色网格请检查1.NavigationGameObject的Rotation是否为(-90,0,0)。2. Scene视图的Gizmos是否开启了Navigation显示。3. 是否在Window AI Navigation的Bake标签页中将Generated World Size设置得足够大以覆盖你的场景。烘焙后如果角色仍然穿墙请检查障碍物的NavMesh Modifier是否应用正确以及障碍物的2D碰撞体是否确实存在且形状正确。5. 让角色动起来NavMeshAgent的配置与脚本控制导航网格有了现在需要让角色在上面智能移动。这通过NavMeshAgent组件和简单的脚本控制来实现。5.1 为角色添加NavMeshAgent选中你的角色预制件或场景中的角色对象。点击Add Component搜索并添加NavMesh Agent。配置Agent参数这些参数直接影响移动手感和性能Speed最大移动速度。Angular Speed转向速度。在2D中角色通常是瞬间转向或使用插值旋转这个值可以设高如500。Acceleration加速度。值越大提速越快。Stopping Distance在距离目标多远处开始减速停止。设为0会尝试精确到达目标点但可能导致在目标点附近抖动。Auto Braking是否自动刹车。如果勾选接近目标时会减速不勾选则会以恒定速度移动直到到达然后瞬间停止。根据游戏手感需求调整。Radius, Height, Base Offset这些是代理的物理尺寸用于避障和空间查询。确保Radius小于导航网格中通道的宽度。Height在2D中不重要Base Offset通常为0。5.2 编写寻路控制脚本为角色创建一个C#脚本例如PlayerController或AIController。using UnityEngine; using UnityEngine.AI; // 引入AI命名空间 public class AIController : MonoBehaviour { private NavMeshAgent agent; private Camera mainCamera; void Start() { // 获取组件引用 agent GetComponentNavMeshAgent(); mainCamera Camera.main; // 关键告诉Agent我们是在2D XY平面上工作 // 将代理的更新位置和旋转模式设置为2D兼容 agent.updatePosition true; agent.updateRotation false; // 关闭Agent的3D旋转控制我们自己控制2D朝向 agent.updateUpAxis false; // 在NavMeshPlus的特定配置下可能需要关注此参数 } void Update() { // 示例1鼠标点击移动适用于玩家控制或RTS if (Input.GetMouseButtonDown(0)) // 左键点击 { // 将鼠标屏幕坐标转换为世界坐标2D Vector2 mousePos mainCamera.ScreenToWorldPoint(Input.mousePosition); // 设置Agent的目的地 agent.SetDestination(mousePos); } // 示例2自动面向移动方向2D if (agent.velocity.sqrMagnitude Mathf.Epsilon) // 如果有速度 { // 计算朝向角度 Vector3 direction agent.velocity.normalized; float angle Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg; // 应用旋转假设角色朝右是0度 transform.rotation Quaternion.AngleAxis(angle, Vector3.forward); } } // 一个公共方法供其他脚本调用让AI移动到指定位置 public void MoveToPosition(Vector2 targetPosition) { if (agent ! null agent.isActiveAndEnabled) { agent.SetDestination(targetPosition); } } // 检查是否到达目的地 public bool HasReachedDestination() { return !agent.pathPending agent.remainingDistance agent.stoppingDistance (!agent.hasPath || agent.velocity.sqrMagnitude 0f); } }脚本关键点解析agent.updateRotation false;这是2D寻路的关键设置。NavMeshAgent默认会控制3D物体的Y轴旋转这在2D中会导致不可预料的行为物体可能倾斜。我们禁用它然后手动用代码控制2D平面Z轴的旋转。agent.SetDestination()核心方法告诉Agent目标点。Agent会自动在后台计算路径并开始移动。agent.velocity可以获取Agent当前的速度向量用于计算面向方向。remainingDistance和pathPending用于判断移动状态非常有用。5.3 处理动态障碍物如果场景中有会移动的障碍物比如其他NPC、可推动的箱子你需要用到NavMeshObstacle组件。为动态障碍物添加NavMesh Obstacle组件。设置其形状Carve和大小。关键参数是Carve当勾选时它会在导航网格上“挖”出一个动态的洞Agent会实时避开这个区域。对于形状复杂的物体可以勾选Carve Only Stationary这样只有当障碍物停止移动时它才会被“雕刻”进导航网格避免因障碍物频繁移动导致导航网格反复重新计算性能开销大。实操心得对于大量动态障碍物要谨慎使用Carve功能尤其是它们频繁移动时。一种优化策略是对于移动的敌人或NPC不将其作为NavMeshObstacle而是通过代码让AI Agent之间互相避让例如使用RVO或简单的排斥力。NavMeshObstacle更适合相对静止或缓慢移动的环境物体。6. 高级应用与性能优化应对复杂场景当你的游戏场景变大、角色变多时基础的寻路可能会遇到性能瓶颈或逻辑问题。下面分享一些进阶技巧。6.1 分层烘焙与区域代价不同的地形应该有不同的移动代价。比如在草地上行走正常在沼泽里速度减半。自定义区域打开Window AI Navigation切换到Areas标签页。你可以看到默认的WalkableNot Walkable等。点击号可以创建自定义区域例如命名为Swamp并设置其Cost为2代价为2意味着走过该区域1米相当于在普通区域走2米。应用区域在场景中选中沼泽地对应的Tilemap或Sprite为其添加NavMesh Modifier组件Override Area选择你刚创建的Swamp。重新烘焙。现在当Agent寻路时它会自动计算代价可能会选择绕开沼泽即使那条路更长但总“代价”更低。在Agent上设置区域掩码在NavMeshAgent组件上有一个Area Mask属性。它决定了Agent可以在哪些类型的区域上行走。如果你的Agent是船你可以只勾选Water区域如果是陆地单位则不要勾选Water。这可以防止单位走到不该去的地方。6.2 局部网格更新与异步烘焙如果你的游戏地图是动态生成的或者有可破坏的地形如炸毁一堵墙每次变化都全图重新烘焙Rebake是灾难性的性能开销。使用NavMeshSurface的AddData和RemoveDataNavMeshSurface组件提供了AddData和RemoveData方法可以动态地添加或移除一部分导航网格数据。但这需要你手动管理这些数据块逻辑较复杂。局部烘焙Partial BakeNavMeshPlus的NavMeshCollectSources2d组件支持指定收集源的范围。你可以创建多个小的NavMeshSurface每个负责场景的一个区域。当某个区域发生变化时只重新烘焙那个小Surface。这需要你设计好场景的分区逻辑。异步烘焙NavMeshSurface.BuildNavMesh()是一个同步方法在大型地图上会卡住主线程。你可以使用NavMeshBuilder.UpdateNavMeshDataAsync()这个静态方法进行异步更新。你需要先创建一份NavMeshData然后异步更新它最后赋值给NavMeshSurface.navMeshData。具体实现可以参考Unity官方文档关于异步烘焙的示例。这是处理大型动态地图的终极方案但实现难度较高。6.3 多Agent管理与避障当屏幕上同时有几十上百个单位在寻路时即使每个Agent的路径计算是高效的它们也可能互相卡住。RVOReciprocal Velocity ObstaclesUnity的High-Level AI系统包含了RVO避障功能但它在2D中的支持并不完美。NavMeshAgent本身有简单的彼此避让但对于高密度单位效果有限。自定义局部避障一种实用的方法是在Update中为每个Agent检测前方一定距离内Physics2D.OverlapCircle的其他Agent。如果检测到则给当前Agent一个垂直于连线方向的轻微侧向力或者临时微调其速度。这需要精细的调参来避免“抖动”。分层寻路Flow Field对于大规模群体移动如RTS中的军队NavMesh可能不是最高效的。业界常用Flow Field流场寻路。但这超出了NavMeshPlus的范围你需要自己实现或寻找其他插件。对于中小规模100个Agent的2D游戏优化好的NavMeshAgent完全够用。6.4 常见性能问题排查表问题现象可能原因解决方案烘焙时间极长场景中2D碰撞体过多、过于复杂Tilemap使用了大量多边形碰撞体。1. 简化碰撞体形状用Box代替复杂Polygon。2. 为Tilemap使用Composite Collider 2D合并碰撞体。3. 在NavMeshCollectSources2d组件中尝试调整Collect Mode可能不需要收集所有类型的源。游戏运行时卡顿每帧有大量Agent在同时计算路径动态障碍物频繁触发网格雕刻Carve。1. 限制同时进行路径请求的Agent数量使用队列或分帧处理。2. 对非紧急的移动降低寻路更新频率如每0.5秒寻路一次。3. 为动态NavMeshObstacle设置合理的Carve Only Stationary和Carving Move Threshold。Agent在狭窄处卡住Agent的Radius设置过大超过了导航网格通道的实际宽度。1. 减小Agent的Radius。2. 在烘焙时增加Agent Radius参数生成更宽的通道。3. 检查烘焙结果确保关键通道没有被错误地标记为不可行走。路径看起来“绕远”区域代价Area Cost设置不合理或者存在多个相似代价的路径Agent选择了第一条计算的。1. 检查并调整不同地形区域的Cost值。2. A*算法本身会找到最短“代价”路径而非绝对最短几何路径这是正常的。7. 实战案例构建一个2D塔防游戏的敌人寻路系统让我们用一个具体的例子来串联所有知识。假设我们要做一个经典的2D塔防游戏敌人从地图入口沿着固定路径走向终点。7.1 场景搭建与静态烘焙设计地图使用Tilemap绘制一条蜿蜒的路径路径两侧是障碍物如岩石、树木。路径本身的地面瓦片使用一种Tile障碍物使用另一种Tile并为其添加2D碰撞体。设置导航创建Navigation空物体添加NavMeshSurface和NavMeshCollectSources2d。点击Rotate Surface to XY。为所有作为障碍物的Tilemap瓦片注意是瓦片不是整个Tilemap添加NavMesh Modifier区域类型设为Not Walkable。这里有个技巧你可以创建一个只包含障碍物瓦片的独立Tilemap图层然后给这个Tilemap物体整体添加一个NavMesh Modifier并勾选Override Area为Not Walkable同时勾选Apply To Children。这样这个图层上的所有瓦片都会自动成为障碍物。烘焙导航网格。你应该看到一条蓝色的、与你的路径形状一致的导航网格生成。7.2 敌人AI脚本敌人预制件需要NavMeshAgent组件和一个控制脚本。using UnityEngine; using UnityEngine.AI; public class TDEnemy : MonoBehaviour { public Transform targetWaypoint; // 当前目标路点由路径管理器分配 private NavMeshAgent agent; private float originalSpeed; public int health 100; void Start() { agent GetComponentNavMeshAgent(); originalSpeed agent.speed; agent.updateRotation false; // 关键禁用3D旋转 if (targetWaypoint ! null) { MoveToWaypoint(targetWaypoint.position); } } void Update() { // 控制2D朝向面向移动方向 if (agent.velocity ! Vector3.zero) { Vector3 dir agent.velocity.normalized; float angle Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg; transform.rotation Quaternion.Euler(0, 0, angle); } // 检查是否到达当前路点 if (targetWaypoint ! null HasReachedDestination()) { // 通知路径管理器请求下一个路点 PathManager.Instance.EnemyReachedWaypoint(this); } } public void MoveToWaypoint(Vector2 position) { agent.SetDestination(position); } public void ApplySlowEffect(float slowFactor, float duration) { // 被塔攻击时减速 agent.speed originalSpeed * slowFactor; CancelInvoke(nameof(ResetSpeed)); // 取消之前的重置 Invoke(nameof(ResetSpeed), duration); // duration秒后重置速度 } private void ResetSpeed() { agent.speed originalSpeed; } private bool HasReachedDestination() { return !agent.pathPending agent.remainingDistance agent.stoppingDistance; } public void TakeDamage(int damage) { health - damage; if (health 0) { Die(); } } private void Die() { // 死亡逻辑例如播放动画、产生金币、从管理器列表中移除等 PathManager.Instance.RemoveEnemy(this); Destroy(gameObject); } }7.3 路径管理器Waypoint System虽然NavMesh可以自动寻路但塔防游戏通常需要敌人严格沿着设计好的路径走即使有更短的“野路子”也不行。我们可以用路点Waypoint来引导。在场景中创建一系列空物体作为路点Waypoint按顺序放置在路径上。创建一个PathManager单例脚本管理所有路点和敌人。using System.Collections.Generic; using UnityEngine; public class PathManager : MonoBehaviour { public static PathManager Instance; public ListTransform waypoints new ListTransform(); private DictionaryTDEnemy, int enemyWaypointIndex new DictionaryTDEnemy, int(); void Awake() { if (Instance null) Instance this; else Destroy(gameObject); } // 敌人出生时调用分配起始路点 public void RegisterEnemy(TDEnemy enemy) { enemyWaypointIndex[enemy] 0; // 从第一个路点开始 enemy.MoveToWaypoint(waypoints[0].position); } // 敌人到达当前路点后调用 public void EnemyReachedWaypoint(TDEnemy enemy) { if (enemyWaypointIndex.ContainsKey(enemy)) { int currentIndex enemyWaypointIndex[enemy]; int nextIndex currentIndex 1; if (nextIndex waypoints.Count) { // 分配下一个路点 enemyWaypointIndex[enemy] nextIndex; enemy.MoveToWaypoint(waypoints[nextIndex].position); } else { // 到达终点敌人漏了 EnemyReachedEnd(enemy); } } } private void EnemyReachedEnd(TDEnemy enemy) { // 扣玩家生命值等逻辑 GameManager.Instance.PlayerLoseLife(); RemoveEnemy(enemy); } public void RemoveEnemy(TDEnemy enemy) { if (enemyWaypointIndex.ContainsKey(enemy)) { enemyWaypointIndex.Remove(enemy); } // 从其他管理列表中移除... } }这个方案的精妙之处在于我们结合了NavMesh的智能避障和路点系统的路径约束。敌人通过NavMeshAgent.SetDestination()前往下一个路点但寻路过程是由NavMesh完成的所以如果两个路点之间临时出现一个障碍物比如玩家建造了一个临时路障敌人会自动绕行而不会傻傻地撞墙。到达路点后再获取下一个目标。这样既保证了敌人沿预定路线前进又拥有了动态避障的灵活性。7.4 塔的攻击与减速区域塔防游戏中减速塔Glue Tower的效果可以通过NavMesh的区域代价系统优雅实现。在Navigation窗口的Areas页创建一个名为Slow的区域设置Cost为3。在减速塔的攻击范围内放置一个带有Collider 2D如Circle Collider 2D的触发器物体。为其添加NavMesh Modifier组件Override Area为Slow。当敌人进入这个区域由于该区域移动代价变高它的寻路算法可能会尝试离开这个区域如果路径可选。同时我们也可以在敌人进入触发器时调用上面TDEnemy脚本中的ApplySlowEffect方法直接降低其agent.speed实现更直接、可视的减速效果。两种方式可以叠加使用Cost影响路径规划长远决策speed影响实时移动即时效果。通过这个完整的塔防案例你应该能体会到NavMeshPlus在2D游戏寻路中的强大与灵活。它不是一个死板的路径点系统而是一个真正的、基于环境的智能导航解决方案。从简单的点击移动到复杂的动态策略游戏它都能提供坚实的底层支持。关键在于理解其原理并根据你的游戏类型进行恰当的配置和扩展。