前言:
- 我是根据b站上的视频进行学习,并且总结写下笔记,然后做此分享。笔记非常详细以至于你可以通过查看文章进行快速学习并且制作出游戏出来。当然视频学习的链接我也放在下面了:
- 视频学习:www.bilibili.com/video/BV1Nd…
- 音频/美术资源包下载: pan.baidu.com/s/1mY05SbG3…
目标
使用 Godot 导航系统 实现:
- 敌人自动寻路追击玩家
- 避开平台障碍
- 到达目标自动停止
- 用计时器优化性能
一、添加导航组件
1️⃣ 敌人场景添加
添加:
NavigationAgent2D
作用:
控制敌人的寻路行为。
2️⃣ 主场景添加导航区域
在关卡节点下添加:
NavigationRegion2D
作用:
定义“敌人可以移动的区域”。
3️⃣ 设置障碍来源
在 NavigationRegion2D 中:
- 将
Source Geometry改为 Group Explicit
然后:
- 复制该组名
- 到
TileMapLayerPlatform - 添加同名分组
作用:
- 只把平台碰撞体当作障碍
- 不遍历所有节点
- 提升性能
4️⃣ 绘制导航区域
使用“创建点工具”:
- 画一个矩形
- 点击“Bake Navigation Polygon”
效果:
- 蓝色区域 = 可移动
- 被平台碰撞阻挡区域 = 不可移动
二、移动状态中实现寻路
1️⃣ 拖入节点
在 Move 状态脚本中:
- Ctrl + 拖入
NavigationAgent2D - 获取玩家世界坐标
2️⃣ 定义变量
暂时无法在飞书文档外展示此内容
3️⃣ 重写物理更新函数
暂时无法在飞书文档外展示此内容
逻辑:
- 获取路径下一个点
- 计算方向
- 平滑插值速度
- 移动角色
4️⃣ 开启路径调试
在 NavigationAgent2D:
- 勾选 Debug Visible Path
可以看到敌人的路径线。
三、到达玩家自动停止
1️⃣ 设置目标距离
在 NavigationAgent2D 中:
- 将
Target Desired Distance改为 40
含义:
- 距离 ≤ 40 认为已追到
2️⃣ 代码判断
暂时无法在飞书文档外展示此内容
四、使用计时器优化性能
问题:
- 每帧更新路径
- 敌人多时会卡顿
解决:
只每 0.5 秒更新一次目标位置。
1️⃣ 添加计时器
在敌人场景添加:
Timer- 重命名:
PlayerPosUpdateTimer - Wait Time = 0.5
- 取消 AutoStart
2️⃣ 拖入脚本
Ctrl + 拖入 Move 状态脚本。
3️⃣ 连接信号
连接:
暂时无法在飞书文档外展示此内容
4️⃣ 进入/退出状态控制
暂时无法在飞书文档外展示此内容
5️⃣ 在计时器信号中更新目标
暂时无法在飞书文档外展示此内容
并删除 physics_update 里设置目标的代码。
效果:
- 每秒只更新 2 次路径
- 降低性能消耗
五、创建敌人组
在主场景:
- 创建
Node2D - 命名:
EnemyGroup - 开启 Y Sort
- 放入多个 Enemy
作用:
- 统一管理
- 正确遮挡排序
六、隐藏导航区域
关闭 NavigationRegion2D 的可见性。
说明:
- 蓝色区域 = 追杀范围
- 玩家离开区域 → 敌人不会追击
最终效果
✔ 敌人自动绕开平台追玩家
✔ 到达目标自动停止
✔ 路径可视化
✔ 使用计时器优化性能
✔ 支持多个敌人
现在敌人已经具备完整“导航追击 AI”。
实现过程:
- 我们将使用Godot的导航系统来帮助敌人找到通往玩家的路径,使其能够自动移动到玩家位置
- 为了让导航系统正常工作,我们需要在敌人场景中添加一个NavigationAgent2D节点,NavigationAgent2D节点定义了敌人寻路过程的行为
- 在移动状态脚本中,我们按住ctrl键+拖动把NavigationAgent2D节点拖入敌人移动状态脚本中。并且先获取玩家角色的世界位置
- 然后我们还需要一个NavigationRegion2D节点,在我们的游戏场景中,NavigationRegion2D定义了敌人可以移动。所以我们切换到主场景的关卡节点下进行添加
- 然后我们需要一个多边形来定义这个区域的范围,默认情况下,NavigationRegion2D会遍历游戏中的所有节点。场景中,它会拾取碰撞形状并将其视为障碍物。
- 在我们的游戏中,唯一需要拾取的障碍物是平台的碰撞形状。我不希望它遍历所有节点,将sourgeometry的属性改为GroupExplicit。
- 这个NavigationRegion2D只会处理标记了这个组名的节点的名字,让我们复制这个组名,并将其添加到TileMapLayerPlatform节点上
- 然后我们切换到主场景的TileMapLayerPlatform节点上的分组然后添加新的分组,分组名称就使用我们刚才复制的组名
- 现在我们可以在屏幕上绘制多边形来表示NavigationRegion2D的区域,使用观察窗上方的创建点工具,然后绘制一个矩形,进行烘焙导航多边形。蓝色区域是敌人可以移动通过的路径,非蓝色部分被平台的碰撞形状阻挡
- 然后我们回到敌人移动状态脚本,首先要让敌人进行移动我们需要一个方向,我们新建一个变量var direction : Vector2
- 然后我们来使用重写UpdatePhysics函数来操作敌人的移动,先获取玩家位置然后获取前往这个位置的方向
- 尽管我们并没有真正移动敌人,但是我们可以开启调试进行观察,我们点击节点的右边检查器的debug按钮进行开启可见路径
- 接下来我们让敌人进行动起来,我们上面获取了前往玩家的方向,我们只需要根据这个方向来更新敌人的速度就好,
- 我们还需要一个速度变量来定义敌人的移动速度
- 我们要将敌人的当前速度平滑过渡到目标速度
- 然后进行调用函数进行移动
- 启动游戏之后我们进入到敌人检测玩家的半价,之后敌人就会在导航区域内一直追玩家,不过当追到玩家之后不会停止,所以我们这里来写一下停止
- 我们首先回到2d导航节点,我们将这个目标距离期望改为40,当距离40的时候就认为追到玩家了
- 然后我们来到代码这里增加一下判断
- 然后我们来优化一下这个敌人移动,因为现在是根据每帧进行移动,如果敌人一多进行移动然后就会导致卡顿,所以我们来降低一下更新目标的频率。我们使用计时器来进行更新
- 我们在敌人场景添加新的节点,计时器节点,重命名为PlayerPosUpdateTimer
- 在检查器中将等待时间设置为0.5秒,这意味着这个计时器每秒只会触发两次,并且设置为自动启动
- 然后让我们把计时器的信号事件进行连接脚本
- 然后我们就可以在脚本中看到这个信号事件了
- 我们只希望敌人在移动状态时计时器才工作,让我们重写enter函数和exit函数,进行进入是开启计时器,退出是关闭计时器
- 然后我们ctrl+拖拽将这个计时器节点拖拽到这个敌人移动状态脚本中,并且关闭自动启用属性
- 来设置计时器启动和关闭、
- 然后我们可以把获取玩家位置方向的那行代码从帧更新物理函数里面移动到这个计时器信号函数中。这样子路径只是每秒更新2次,降低一些需求的性能
- 然后接着我们来在主场景中创建一个敌人的分组,我们创建多个敌人放进去这个分组里,我们先创建一个新的node2d节点重命名为EnemyGroup节点并且开启按照y轴进行排序
- 然后我们创建多个enemy节点放进去
- 接着我们隐藏一下这个2d导航区域,注意这个区域就是敌人会追踪的区域,相当于追杀范围,离开这个范围敌人就不会进行追杀了