Tainted \\ Coders

Explicitly Order Events

Last updated:

By default Bevy will try and run systems that are in the same schedule in parallel as long as they do not both need mutable access to the same data.

When events are written they are added to a double buffered queue and kept for at least two frames to ensure any systems that were processed out of order (because they were run in parallel) will receive the event.

The problem here is you can be reading events from up to two frames ago, which may or may not introduce subtle bugs into your game logic.

To avoid this we should schedule systems that produce events to run before systems that consume them:

use bevy::prelude::*;

#[derive(Event)]
struct PlayerLeveledUp(u32);

fn event_producer(mut writer: EventWriter<PlayerLeveledUp>) {
  writer.send(PlayerLeveledUp(5));
}

fn event_consumer(mut reader: EventReader<PlayerLeveledUp>) {
  for event in reader.read() {
    println!("Player leveled up to level: {}", event.0);
  }
}

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(
            Update,
            // We could also (event_producer, event_consumer).chain()
            event_producer.before(event_consumer)
        );
}

This works great for events in the same Schedule but how can we order them between schedules?

We can explicitly order the schedules by modifying the MainScheduleOrder resource:

#[derive(ScheduleLabel, Debug, Hash, PartialEq, Eq, Clone)]
struct UpdatePlayers;

#[derive(ScheduleLabel, Debug, Hash, PartialEq, Eq, Clone)]
struct UpdateEnemies;

fn main() {
    let mut app = App::new();
    let update_players_schedule = Schedule::new(UpdatePlayers);
    let update_enemies_schedule = Schedule::new(UpdateEnemies);
    let mut main_schedule_order =
        app.world.resource_mut::<MainScheduleOrder>();

    main_schedule_order.insert_after(Update, UpdatePlayers);
    main_schedule_order.insert_after(UpdatePlayers, UpdateEnemies);

    app
        .add_plugins(DefaultPlugins)
        .add_schedule(update_players_schedule)
        .add_schedule(update_enemies_schedule)
        .add_systems(UpdatePlayers, event_producer)
        .add_systems(UpdateEnemies, event_consumer)
        .run();
}

Read more