
Bevy Game Loop Waves
Bevy ECS skill for encounter waves.
Install
npx skills add thedivergentai/Game-Dev-Agentic-Skills --skill bevy-game-loop-waves -g -a claude-code -y- Repository
- thedivergentai/Game-Dev-Agentic-Skills917
- Category
- Engineering
- Updated
- 19d ago
- License
- LGPL-3.0
- Context cost
- Mediumabout 1,700 tokens
- Runs scripts
- No, instructions only
What it does
Bevy ECS skill for encounter waves. Keywords: Bevy, waves, spawn, encounter, ECS.
Bevy wave loop
When to use
Load this when enemies arrive in waves and the wave index advances after a clear.
ECS shape
- Components: a wave index and a remaining-count you define.
- Resources: run-wide tables live in a resource (bevy-data-resources).
- Systems: mutate those components on
Updateor the fixed timestep. Use.chain()when damage must land before death. - Messages: one-shot facts (hit, pickup, quest advance) go through bevy-events-messaging. Wave advance below is a resource, not a new message type.
Examples
[dependencies]
bevy = "0.20"
The wave index and the spawn budget are one resource. Each enemy is an entity. Spawning and clearing share WaveDirector, so those systems are chained.
#[derive(Resource)]
struct WaveDirector {
index: u32,
budget: u32,
intermission: f32,
}
#[derive(Component)]
struct Enemy;
fn boot(mut commands: Commands) {
commands.insert_resource(WaveDirector {
index: 1,
budget: 4,
intermission: 0.0,
});
}
fn spawn_budget(mut commands: Commands, mut wave: ResMut<WaveDirector>) {
if wave.intermission > 0.0 || wave.budget == 0 {
return;
}
for _ in 0..wave.budget {
commands.spawn(Enemy);
}
wave.budget = 0;
}
fn advance_on_clear(
enemies: Query<(), With<Enemy>>,
mut wave: ResMut<WaveDirector>,
time: Res<Time>,
) {
if !enemies.is_empty() {
wave.intermission = 0.0;
return;
}
wave.intermission += time.delta_secs();
if wave.intermission < 2.0 {
return;
}
wave.index += 1;
wave.budget = 3 + wave.index;
wave.intermission = 0.0;
}
fn install(app: &mut App) {
app.add_systems(Startup, boot)
.add_systems(Update, (spawn_budget, advance_on_clear).chain());
}
A fixed step is the right place to tick remaining time on a live enemy. The clear system above stays on Update and only reads Enemy.
