Skip to content

Unexpected behavior using States and Events #18428

Open
@TheGrungringMachine

Description

@TheGrungringMachine

Bevy version

0.15.3

What you did

use bevy::prelude::*;

#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Hash, States)]
pub enum Phases {
    #[default]
    None,
    Phase1,
    Phase2,
}

#[derive(Event, Debug, Default)]
pub struct AnEvent;

#[derive(Resource, Debug)]
struct ChangeDirectionTimer {
    timer: Timer,
}

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .init_state::<Phases>()
        .add_event::<AnEvent>()
        .insert_resource(ChangeDirectionTimer {
                timer: Timer::from_seconds(1., TimerMode::Once),
            })
        .add_systems(Startup, start)
        .add_systems(OnEnter(Phases::Phase1), send_event1)
        .add_systems(Update, end_phase1
            .run_if(in_state(Phases::Phase1).and(on_event::<AnEvent>)))
        .add_systems(OnEnter(Phases::Phase2), send_event2)
        .add_systems(Update, end_phase2
            .run_if(in_state(Phases::Phase2).and(on_event::<AnEvent>)))
        .run();
}

fn start(mut next_state: ResMut<NextState<Phases>>) {
    next_state.set(Phases::Phase1);
}

fn send_event1(
    mut event: EventWriter<AnEvent>,
) {
    println!("Sending event 1");
    event.send_default();
}

fn end_phase1(
    mut event: EventReader<AnEvent>,
    mut next_state: ResMut<NextState<Phases>>,
) {
    for _ in event.read() {
        println!("Ending phase 1");
        next_state.set(Phases::Phase2);
    }
}

fn send_event2(
    mut event: EventWriter<AnEvent>,
    time: Res<Time>,
    mut timer: ResMut<ChangeDirectionTimer>,
) {
    timer.timer.tick(time.delta());
    if timer.timer.just_finished() {
        println!("Sending event 2");
        event.send_default();
    }
}

fn end_phase2(
    mut event: EventReader<AnEvent>,
    mut next_state: ResMut<NextState<Phases>>,
) {
    for _ in event.read() {
        println!("Ending phase 2");
        next_state.set(Phases::None);
    }
}

What went wrong

The output of this code is:

Sending event 1
Ending phase 1
Ending phase 2

As the system end_phase2 only runs in the Phase2 state, it should be triggered by the delayed event 2, but it's like it is triggered still by the first event.

This is confirmed by commenting out the if statement in send_event2, which results in:

Sending event 1
Ending phase 1
Sending event 2
Ending phase 2
Ending phase 2

So end_phase2 is actually triggered twice despite the first event is triggered in another state with respect to the one during which end_phase2 should be running.

Additional information

Am I missing something or is it a bug?

Thanks!

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-ECSEntities, components, systems, and eventsA-StatesApp-level states machinesC-BugAn unexpected or incorrect behaviorS-Needs-InvestigationThis issue requires detective work to figure out what's going wrong

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions