Systems and the Frame Loop

Physics, spawning, despawning, and a fixed timestep.

Part 3 of 3Updated

A system is a function over a query:

fn integrate(world: &mut World, dt: f32) {
    for (pos, vel) in world.query_mut::<(Position, Velocity)>() {
        pos.x += vel.dx * dt;
        pos.y += vel.dy * dt;
        vel.dy += GRAVITY * dt;
    }
}

Use a fixed timestep for physics. Feeding real frame time into the integrator makes the simulation behave differently on different machines and explode when a frame stalls:

accumulator += frame_time.min(0.25);
while accumulator >= FIXED_DT {
    integrate(&mut world, FIXED_DT);
    accumulator -= FIXED_DT;
}

Defer structural changes. Spawning or despawning mid-iteration invalidates the storage you’re walking; queue the commands and apply them between systems.

Measure with cargo flamegraph before you reach for rayon. On a hundred thousand particles the bottleneck is usually memory bandwidth, not CPU, and adding threads makes it worse.