Schedule

A schedule allows execution and paralellization of multiple systems.

The systems discussed in the previous chapter can be put into a schedule to contain the logic and order of execution.

In addition to executing systems one after another, a schedule can automatically paralellize execution and run multiple systems at the same time using rayon such that the observable effects occurr order.

In other words, if two systems have queries which do not access the same archetype and components, they will run in paralell. If an archetype reads a value which is written by another system declared before, they will run in sequence.

#![allow(unused)]
fn main() {
    let despawn = System::builder()
        .with_name("delete_outside_world")
        .with_query(Query::new((entity_ids(), distance())).filter(distance().gt(50.0)))
        .with_cmd_mut()
        .build(|mut q: QueryBorrow<_, _>, cmd: &mut CommandBuffer| {
            for (id, &dist) in &mut q {
                println!("Despawning {id} at: {dist}");
                cmd.despawn(id);
            }
        });

    let debug_world = System::builder()
        .with_name("debug_world")
        .with_world()
        .build(|world: &_| {
            tracing::debug!("World: {world:?}");
        });


    component! {
        is_static: () => [ flax::Debuggable ],
    }

    // Spawn 150 static entities, which wont move
    let mut rng = StdRng::seed_from_u64(42);

    for _ in 0..150 {
        let pos = vec2(rng.gen_range(-5.0..5.0), rng.gen_range(-5.0..5.0));
        Entity::builder()
            .set(position(), pos)
            .set_default(distance())
            .set_default(is_static())
            .spawn(&mut world);
    }

    // Since this system will move non static entities out from the origin, they will
    // eventually be despawned
    let move_out = System::builder()
        .with_name("move_out")
        .with_query(Query::new(position().as_mut()).filter(is_static().without()))
        .for_each(|pos| {
            let dir = pos.normalize_or_zero();

            *pos += dir;
        });

    // Spawn new entities with a random position each frame
    let spawn = System::builder().with_name("spawner").with_cmd_mut().build(
        move |cmd: &mut CommandBuffer| {
            for _ in 0..100 {
                let pos = vec2(rng.gen_range(-10.0..10.0), rng.gen_range(-10.0..10.0));
                println!("Spawning new entity at: {pos:?}");
                Entity::builder()
                    .set(position(), pos)
                    .set_default(distance())
                    .spawn_into(cmd);
            }
        },
    );

    let mut frame_count = 0;

    // Count the number of entities in the world and log it
    let count = System::builder()
        .with_name("count")
        .with_query(Query::new(()))
        .build(move |mut query: QueryBorrow<()>| {
            let count: usize = query.iter_batched().map(|v| v.len()).sum();
            println!("[{frame_count}]: {count}");
            frame_count += 1;
        });

    // Assemble the schedule, takes care of dependency management
    let mut schedule = Schedule::builder()
        .with_system(update_dist)
        .with_system(despawn)
        .with_system(spawn)
        .with_system(move_out)
        .with_system(debug_world)
        .with_system(count)
        .build();

    println!("{schedule:#?}");

    for i in 0..20 {
        println!("Frame: {i}");
        println!("Batches: {:#?}", schedule.batch_info(&world));
        schedule.execute_par(&mut world)?;
    }

}