Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

System are not executed when using State, custom stage and system set. #2312

Closed
ledyba opened this issue Jun 6, 2021 · 8 comments
Closed
Labels
A-ECS Entities, components, systems, and events S-User-Error This issue was caused by a mistake in the user's approach

Comments

@ledyba
Copy link

ledyba commented Jun 6, 2021

Bevy version

0.5

Operating system & version

Windows 10 + Rust 1.52.1

What you did

I would like to use custom stage and custom system state, and would like to switch them using by State<T>.

Here is a complete example: __report__bevy_scheduing_sample/main.rs

Here is a main function:

fn main() {
  let before_system_set = SystemSet::on_update(MyState::UpdateState)
    .with_system(print_before_system.system());
  let after_system_set = SystemSet::on_update(MyState::UpdateState)
    .with_system(print_after_system.system());
  let update_system_set = SystemSet::on_update(MyState::UpdateState)
    .with_system(print_system.system());
  App::build()
    .add_plugins(DefaultPlugins)
    .add_state_to_stage(CoreStage::Update, MyState::UpdateState)
    .add_stage_before(
      CoreStage::Update,
      MyStage::BeforeRound,
      SystemStage::parallel(),
    )
    .add_stage_after(
      CoreStage::Update,
      MyStage::AfterRound,
      SystemStage::parallel(),
    )
    .add_system_set_to_stage(MyStage::BeforeRound, before_system_set)
    .add_system_set_to_stage(CoreStage::Update, update_system_set)
    .add_system_set_to_stage(MyStage::AfterRound, after_system_set)
    .run();
}

What you expected to happen

I have expected this app prints:

Before Update
Update
AfterUpdate
Before Update
Update
AfterUpdate

....(Repeats forever)

What actually happened

% cargo run
   Compiling bevy_scheduing_sample v0.1.0 (C:\Users\kaede\src\sample)
    Finished dev [unoptimized + debuginfo] target(s) in 3.81s
     Running `target\debug\bevy_scheduing_sample.exe`
Jun 07 01:39:44.564  WARN bevy_ecs::schedule::graph_utils: Chain(&bevy_ecs::schedule::state::State<bevy_scheduing_sample::MyState>::on_update::{{closure}}, &bevy_ecs::schedule::state::should_run_adapter<bevy_scheduing_sample::MyState>) wants to be after unknown label: DriverLabel(TypeId { t: 11331299601305256830 })
error: process didn't exit successfully: `target\debug\bevy_scheduing_sample.exe` (exit code: 0xc000013a, STATUS_CONTROL_C_EXIT)

Additional information

I would like to use custom stage (in this example, MyStage::BeforeRound and MyStage::AfterRound) to interact with components spawned in CoreStage::Update. Any other solution is available to do that? According to #1613, I understood that we have to use custom stages.

@ledyba ledyba added C-Bug An unexpected or incorrect behavior S-Needs-Triage This issue needs to be labelled labels Jun 6, 2021
@Ixentus
Copy link
Contributor

Ixentus commented Jun 6, 2021

You don't need custom stages, you can use CoreStage::PreUpdate and CoreStage::PostUpdate.

Every stage which has system(set)s which use states needs a StateDriver, this is why you're getting an error. You can use .add_state_to_stage(MyStage::BeforeRound, MyState::UpdateState) to add it.

@NathanSWard
Copy link
Contributor

@ledyba does @Ixentus 's answer suit your use case?

ledyba added a commit to ledyba/__report__bevy_scheduing_sample that referenced this issue Jun 7, 2021
@ledyba
Copy link
Author

ledyba commented Jun 7, 2021

@Ixentus Thanks! By adding those lines, it works as expected. (I would like to make my stage, because gamepad input system uses CoreStage::PreUpdate and I would like to use gamepad input before CoreStage::Update)
@NathanSWard Sorry, not yet. I need state transition.

I also would like to make another state, such as Loading state and loading system.

If loading is done, loading system sets will update the state from Loading to InGame.

#[derive(Debug, Clone, Eq, PartialEq, Hash)]
enum MyState {
  Loading, // <- Added
  InGame, // <- Renamed
}

fn load_system(mut state: ResMut<State<MyState>>) {
  // Prepare something (such as texture loading) and wait until done.
  // ...

  // if loading.is_done() {
    state.replace(MyState::InGame).unwrap();
  //}
}

I added this feature to previous example (full source), and it also works as expected, but roughly.

fn main() {
  // Added
  let loading_system_set = SystemSet::on_update(MyState::Loading)
    .with_system(load_system.system());

  let before_system_set = SystemSet::on_update(MyState::InGame)
    .with_system(print_before_system.system());
  let after_system_set = SystemSet::on_update(MyState::InGame)
    .with_system(print_after_system.system());
  let update_system_set = SystemSet::on_update(MyState::InGame)
    .with_system(print_system.system());
  App::build()
    .add_plugins(DefaultPlugins)
    .add_stage_before(
      CoreStage::Update,
      MyStage::BeforeRound,
      SystemStage::parallel(),
    )
    .add_stage_after(
      CoreStage::Update,
      MyStage::AfterRound,
      SystemStage::parallel(),
    )
    .add_state_to_stage(MyStage::BeforeRound, MyState::Loading)
    .add_state_to_stage(CoreStage::Update, MyState::Loading)
    .add_state_to_stage(MyStage::AfterRound, MyState::Loading)
    .add_system_set_to_stage(CoreStage::Update, loading_system_set) // <- Added
    .add_system_set_to_stage(MyStage::BeforeRound, before_system_set)
    .add_system_set_to_stage(CoreStage::Update, update_system_set)
    .add_system_set_to_stage(MyStage::AfterRound, after_system_set)
    .run();
}

It prints:

% cargo run
   Compiling bevy_scheduing_sample v0.1.0 (C:\Users\kaede\src\sample)
    Finished dev [unoptimized + debuginfo] target(s) in 4.05s
     Running `target\debug\bevy_scheduing_sample.exe`
Update
After Update
Before Update
Update
After Update
(... forever)

(Note: There is no Before Update output at first!)

First,I can't understand why it works.
If I understood correctly, loading_system_set is added just to CoreStage::Update, so the states of MyStage::BeforeRound and MyStage::AfterRound are still MyState::Loading.

Why I don't have to call state.replace(MyState::InGame).unwrap(); three times to update all three states in three stages?

And, how can I make sure before_system_set to be executed when the state is changed?

@Ixentus
Copy link
Contributor

Ixentus commented Jun 7, 2021

There is only 1 state (which can be Loading or InGame). So don't replace it in every stage. The docs say that should fail if the new state matches the current state.

Now the reason "Update" is ran before "Before Update" is that the Loading system is ran in the Update stage. When it sets the state to InGame, it is still in the update stage. So before_system_set won't be ran till the next tick.

ledyba added a commit to ledyba/__report__bevy_scheduing_sample that referenced this issue Jun 7, 2021
@ledyba
Copy link
Author

ledyba commented Jun 7, 2021

There is only 1 state (which can be Loading or InGame). So don't replace it in every stage. The docs say that should fail if the new state matches the current state.

I see, thanks. I read the source and I found State is just a resource, and not owned by each stages:

/// Adds a new [State] with the given `initial` value.
/// This inserts a new `State<T>` resource and adds a new "driver" to the given stage.
/// Each stage that uses `State<T>` for system run criteria needs a driver. If you need to use
/// your state in more than one stage, consider manually adding [State::get_driver] to the
/// stages you need it in.
pub fn add_state_to_stage<T>(&mut self, stage: impl StageLabel, initial: T) -> &mut Self
where
T: Component + Debug + Clone + Eq + Hash,
{
self.insert_resource(State::new(initial))
.add_system_set_to_stage(stage, State::<T>::get_driver())
}

As written in doc, this style also works (For me, this style makes me relieved, because I don't need to insert the same status for many times.):

    .insert_resource(State::new(MyState::Loading))
    .add_system_set_to_stage(MyStage::BeforeRound, State::<MyState>::get_driver())
    .add_system_set_to_stage(CoreStage::Update, State::<MyState>::get_driver())
    .add_system_set_to_stage(MyStage::AfterRound, State::<MyState>::get_driver())

Now the reason "Update" is ran before "Before Update" is that the Loading system is ran in the Update stage. When it sets the state to InGame, it is still in the update stage. So before_system_set won't be ran till the next tick.

I see... So I tried to moveloading_system_set from CoreStage::Update to CoreStage::First.
(Here is an complete source)

Then, it works perfectly!

% cargo run
   Compiling bevy_scheduing_sample v0.1.0 (C:\Users\kaede\src\sample)
    Finished dev [unoptimized + debuginfo] target(s) in 3.95s
     Running `target\debug\bevy_scheduing_sample.exe`
Loading is done
Before Update
Update
After Update
Before Update
Update
After Update
...

@ledyba
Copy link
Author

ledyba commented Jun 7, 2021

Thank you, @Ixentus and @NathanSWard !
I can resolve my issue perfectly, so I will close this issue.
(And sorry for reporting as a bug...)

@ledyba ledyba closed this as completed Jun 7, 2021
@Ixentus
Copy link
Contributor

Ixentus commented Jun 7, 2021

Glad we could help. Hopefully the next iteration of States and Stages will be easier to use :)

@ledyba
Copy link
Author

ledyba commented Jun 7, 2021

Oh, I'm really looking forward it!

@alice-i-cecile alice-i-cecile added A-ECS Entities, components, systems, and events S-User-Error This issue was caused by a mistake in the user's approach and removed C-Bug An unexpected or incorrect behavior S-Needs-Triage This issue needs to be labelled labels Jul 8, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ECS Entities, components, systems, and events S-User-Error This issue was caused by a mistake in the user's approach
Projects
None yet
Development

No branches or pull requests

4 participants