关于 godot-game-loop-waves
面向 Godot 开发者的波次游戏循环技能,介绍以 WaveResource 配置遭遇内容、由管理器编排生成和通关判定的架构,并按敌人规模选择节点或服务器端方案。
主要功能
- 01WaveResource 驱动遭遇配置
- 02管理器处理波次时序与胜利判定
- 03Marker 与加权生成器提供生成位置
- 04按密度选择节点、MultiMesh 或 RID 方案
- 05提供对象池、异步寻路等模式
适用场景
- 01搭建线性关卡的敌人波次
- 02制作按难度倍率递增的无尽模式
- 03实现玩家进入 Area3D 后触发遭遇
- 04为大规模敌群选择合适的生成架构
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ thedivergentai/ game-dev-agentic-skills --skill godot-game-loop-waves安装后在 Claude Code 中输入/godot-game-loop-waves确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/migration-notes.mdwave-expert-patterns.mdscripts/wave_loop_patterns.gdwave_manager.gdwave_resource.gdwave_spawner.gdwave_weighted_spawner.gd详细介绍
架构设计: “Wave-State”模式
成熟的实现会将波次视为数据驱动的转换。不要硬编码生成数量,而是使用 WaveResource 定义“遭遇”,并由 WaveManager 按顺序处理。
核心职责
- 管理器:编排时间线,处理波次间的延迟,并跟踪“胜利”条件(所有敌人均已死亡)。
- 生成器:解耦的节点,为敌人出现的位置提供空间信息(
wave_spawner.gd/wave_weighted_spawner.gd)。 - 资源:不可变的数据容器,让设计师无需修改代码即可重新平衡游戏。
密度决策树(编码前选择规模)
| 实时密度 | 方案 | 必须加载 |
|---|---|---|
| 约 80 个以下的 SceneTree 敌人 | 节点管理器 + Marker 生成器 | wave_manager.gd、wave_spawner.gd、wave_resource.gd |
| 群集视觉效果 / 数百个 | MultiMesh + 加权组合 | wave_loop_patterns.gd(MultiMesh / 异步路径)、wave_weighted_spawner.gd |
| 约 10k 个物体 | PhysicsServer / NavigationServer RIDs(每个小怪不创建 Node) | wave_loop_patterns.gd 中的服务器 RID 模式;不要扩展 wave_manager 节点生成 |
wave_manager.gd 是 SceneTree 的推荐路径(延迟执行 add_child、通过组/信号统计清除数量、可选对象池)。将其视为原型→中等规模方案——对于 RID 群集,请遵循 wave_loop_patterns.gd,而不是实例化数千个节点。
组合推荐路径
- 编写 wave_resource.gd 组合表。
- 放置 wave_spawner.gd Marker(需要权重控制多样性时,使用 wave_weighted_spawner.gd)。
- 将 wave_manager.gd 的
spawner指向该 Marker;管理器会延迟生成,并通过&"enemies"组 + 信号清除敌人。 - 使用权重替代固定数量时,在组合循环中调用
WaveWeightedSpawner.spawn_enemy()(或将管理器的spawner设置为加权节点)。
专家级代码模式
1. 异步波次触发
在 wave_manager.gd 中使用 await 计时器——编写自定义时间线前必须阅读。生成时使用 call_deferred(&"add_child", …);通过信号/组(而非扫描 get_children())清除敌人。
2. 基于组合的生成
在 wave_resource.gd 中定义多样性;使用 wave_spawner.gd / wave_weighted_spawner.gd 放置单位。不要在管理器中硬编码场景路径。
专家级决策矩阵:进程
| 模式 | 最适用于 | 逻辑 |
|---|---|---|
| 线性 | 剧情任务 | 手工编写的 WaveResource 列表。 |
| 无尽 | 生存模式 | 使用倍率计算生成的 WaveResource。 |
| 触发式 | RPG 遭遇 | 仅当玩家进入 Area3D 时才开始波次。 |
绝对不要做
- 绝对不要遍历 get_children() 来查找所有敌人 — 这非常慢。务必将敌人添加到 "enemies" 组,并使用
get_tree().get_nodes_in_group(&"enemies")高效访问。 - 绝对不要频繁实例化和释放数百个敌人 — 这会导致垃圾回收卡顿。使用对象池复用现有敌人实例。
- 绝对不要为蜂群生成数千个独立的 MeshInstance3D 节点 — 这会严重拖累绘制调用。使用
MultiMeshInstance3D将数千个网格合并为一次 GPU 调用。 - 绝对不要在主线程上为数百个 Agent 计算寻路 — 这会让游戏卡死。为导航区域启用
use_async_iterations,或使用NavigationServer3D.query_path()。 - 绝对不要忘记在添加子节点前检查 is_inside_tree() — 如果生成器已排队等待删除,添加子节点会导致崩溃。务必确认生成器仍处于场景树中。
- 绝对不要将预加载的资源(例如 stats.tres)直接赋给生成的敌人 — 它们会共享完全相同的生命值和属性。务必调用
base_stats.duplicate_deep(),为每个敌人提供独立数据。 - 绝对不要在高频组调用中使用标准字符串 — 务必使用
StringName(&"enemies"、&"take_damage"),以获得最佳哈希性能并避免不必要的字符串分配。 - 绝对不要在物理回调中同步直接生成实体 — 在物理步骤期间实例化节点可能会破坏物理状态。务必使用
call_deferred(&"add_child", enemy)。 - 绝对不要让死亡敌人的 CollisionShapes 继续处于启用状态 — 尸体会阻挡防御塔和导航。死亡时立即使用
set_deferred("disabled", true)。 - 绝对不要通过 MultiplayerSynchronizer 同步复杂的 Object 类型 — 它只支持基本类型。对于复杂数据,请同步 UID 或 ID,并在客户端本地查找数据。
- 绝对不要在没有向玩家提供反馈的情况下自动开始波次 — 务必提供 UI 倒计时、可视化的“波次即将来袭”效果或开始按钮,让玩家保有自主权。
- 绝对不要将生成位置硬编码为 (0,0,0) — 在编辑器中使用
Marker3D节点,这样无需翻查代码即可直观地调整生成点。 - 绝对不要每帧通过统计子节点来检查波次是否完成 — 这样开销太大。维护本地计数器,或使用基于信号的系统来追踪活跃敌人数量。
- 绝对不要让所有实体类型使用同一个导航地图 — 如果同时有飞行和步行敌人,请使用独立的导航地图,避免寻路问题。
- 绝对不要为生成器的碰撞形状使用非等比缩放 — 这会破坏碰撞检测计算。请改为调整形状资源属性。
可用脚本
必读:实现相应模式前,先阅读对应脚本。
wave_loop_patterns.gd
10 种专家模式:MultiMesh 蜂群、异步寻路、后台预加载和服务器端物理敌人。
wave_manager.gd
负责协调时间线、波次间延迟,并通过组计数和信号追踪清场情况。使用 call_deferred 调用 add_child;可通过 use_pool / recycle_enemy 选择启用对象池。
wave_resource.gd
用于存储波次组成和难度设置的数据容器。
wave_spawner.gd
Marker3D 空间传送门——使用 get_spawn_position() 获取生成位置,并可选加入半径抖动。将其设置为 WaveManager.spawner。
wave_weighted_spawner.gd
在 Marker 处按权重随机选择敌人。当组成多样性由概率决定,而非固定数量时使用。
专家级波次模式
1. 群体遮挡剔除
为了优化数百个敌人同时出现时的性能,请启用遮挡剔除。
- 设置:在竞技场中添加一个
OccluderInstance3D并烘焙。 - 效果:完全被墙壁或柱子遮挡的敌人不会交由 GPU 处理,从而显著提升 FPS。
2. 波次 UI 架构
使用 CanvasLayer 和信号,将波次数据与 UI 解耦。
- 波次计数器:显示当前波次和总波次。
- 生命值条:对于 Boss,可在
CanvasLayer上使用TextureProgressBar;对于单个敌人的生命值条,可使用带视口纹理的Sprite3D。
按需获取专家知识
**大模型无知规则:**如果通用 Agent 在阅读前不会知道某项内容,就加载参考资料——绝不要删除专家增量内容。
- wave-expert-patterns.md — 恢复基础教学内容(架构、原因、实现深度)
参考资料
渐进式披露:仅在研究特定 API 时打开官方文档链接;在路由到相关领域时加载相关技能——不要预加载整个技能网络。
官方文档
- 资源 —
WaveResource组合和延迟可由设计人员编辑,无需在管理器中硬编码生成表。 - 节点和场景实例 — 对于波次敌人,使用
PackedScene.instantiate()加上add_child/call_deferred是安全的生成方式。 - 组 — 使用
StringName组和get_node_count_in_group跟踪存活的怪物,而不是每帧扫描子节点。 - 空闲与物理处理 — 使用计时器和
await控制节奏;不要在物理回调中实例化对象而不延迟处理。 - SceneTreeTimer — 使用
create_timer设置波次前延迟和生成间隔,无需使用永远运行的_process倒计时。 - 使用信号 —
wave_started/wave_cleared/all_waves_complete可让 UI、音频和战斗系统与管理器的时间线解耦。 - 后台加载 — 使用
ResourceLoader.load_threaded_request加载 Boss 或大型波次,避免首次生成时卡顿。 - 随机数生成 — 使用
RandomNumberGenerator.rand_weighted实现加权组合和生成位置抖动。 - 使用 MultiMesh — 当数百个小怪导致绘制调用激增时,批处理群体的视觉效果。
- 遮挡剔除 — 隐藏竞技场中不在镜头内的怪物,让密集波次的 GPU 开销保持在可控范围内。
- 导航简介(3D) — 为飞行和行走的波次单位使用异步 Agent 路径和独立导航地图。
- 使用服务器 — 当 SceneTree 节点无法扩展时,使用 PhysicsServer3D/NavigationServer3D RID 处理群体。
相关技能
前置要求
- godot-project-foundations — 在将 WaveManager 接入竞技场之前,了解场景树、导出属性和分组。
- godot-gdscript-mastery — 异步波次触发所依赖的类型化
await、信号和call_deferred模式。 - godot-resource-data-patterns — 了解
Resource/@export组合和duplicate_deep,避免生成的怪物共享属性。
补充内容
- godot-signal-architecture — 管理波次/UI/战斗信号的所有权,让倒计时和清场事件避免泄漏。
- godot-combat-system — 了解命中框、死亡和伤害机制,以便波次单位死亡时递减活跃敌人计数。
- godot-navigation-pathfinding — 使用 NavigationServer 查询、避让遮罩和地图,为数百个波次 Agent 寻路。
- godot-scene-management — 了解对象池、线程加载以及波次频繁创建和移除 PackedScenes 时的安全处理。
- godot-performance-optimization — 使用 MultiMesh、遮挡和服务器端物体来控制群集密度预算。
- godot-monte-carlo-balancer — 在发布无尽模式前,对生成数量、速率和难度曲线进行风格矩阵采样。
下游 / 使用方
- godot-genre-tower-defense — 使用 WaveResource 序列和清场获胜条件驱动路线/传送门波次。
- godot-genre-shooter — 了解波次射击游戏中的竞技场/尸潮生成节奏和敌人多样性。
- godot-genre-survival — 基于同一管理器循环构建无尽倍率波次和压力递增机制。
主入口
- godot-master — 用于跨技能发现的库路由器和镜像模块入口。