-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
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
Make current state a property of StateStage #1125
Conversation
Extends bevyengine#1021 Fixes bevyengine#1117 This also allows avoiding the Clone bound on state Possible future work: - Make state use Eq instead
Maybe this will seem a bit too much like magic to someone who doesn't know about mem::discriminant? Oh well! Also fix AppBuilder to not require otherwise unused clones
Hmm this changes things so that State transitions are no longer a global thing with a well defined order of execution. Each StateStage has its own "last state", which can cause confusing outcomes:
|
Lazy illustrative example: use bevy::prelude::*;
#[derive(Clone)]
pub enum AppState {
X,
Y,
Z,
}
fn main() {
App::build()
.add_plugins(DefaultPlugins)
.add_resource(State::new(AppState::X))
// uncomment the next line and 'enter_system' is not called
.add_stage_after(
stage::UPDATE,
"STAGE_A",
StateStage::<AppState>::default()
.with_enter_stage(
AppState::X,
SystemStage::parallel()
.with_system(a.system())
.with_system(a_x_enter.system()),
)
.with_update_stage(AppState::X, SystemStage::parallel().with_system(a.system()))
.with_exit_stage(AppState::X, SystemStage::parallel().with_system(a.system()))
.with_enter_stage(AppState::Y, SystemStage::parallel().with_system(a.system()))
.with_update_stage(AppState::Y, SystemStage::parallel().with_system(a.system()))
.with_exit_stage(AppState::Y, SystemStage::parallel().with_system(a.system()))
.with_enter_stage(AppState::Z, SystemStage::parallel().with_system(a.system()))
.with_update_stage(AppState::Z, SystemStage::parallel().with_system(a.system()))
.with_exit_stage(AppState::Z, SystemStage::parallel().with_system(a.system())),
)
.add_stage_after(
"STAGE_A",
"STAGE_B",
StateStage::<AppState>::default()
.with_enter_stage(AppState::X, SystemStage::parallel().with_system(b.system()))
.with_update_stage(AppState::X, SystemStage::parallel().with_system(b.system()))
.with_exit_stage(AppState::X, SystemStage::parallel().with_system(b.system()))
.with_enter_stage(
AppState::Y,
SystemStage::parallel()
.with_system(b.system())
.with_system(b_y_enter.system()),
)
.with_update_stage(AppState::Y, SystemStage::parallel().with_system(b.system()))
.with_exit_stage(AppState::Y, SystemStage::parallel().with_system(b.system()))
.with_enter_stage(AppState::Z, SystemStage::parallel().with_system(b.system()))
.with_update_stage(AppState::Z, SystemStage::parallel().with_system(b.system()))
.with_exit_stage(AppState::Z, SystemStage::parallel().with_system(b.system())),
)
.add_system_to_stage(stage::LAST, end.system())
.run();
}
fn a_x_enter(mut state: ResMut<State<AppState>>) {
state.set_next(AppState::Y).unwrap();
}
fn b_y_enter(mut state: ResMut<State<AppState>>) {
state.set_next(AppState::Z).unwrap();
}
fn a() {
println!(" a")
}
fn b() {
println!(" b")
}
fn end(mut x: Local<usize>) {
*x+=1;
if *x == 2 {
panic!("end");
}
} // modify state.rs to print each state
#[allow(clippy::mem_discriminant_non_enum)]
impl<T: Resource> Stage for StateStage<T> {
fn run(&mut self, world: &mut World, resources: &mut Resources) {
let current_stage = loop {
let next = {
let mut state = resources
.get_mut::<State<T>>()
.expect("Missing state resource");
state.previous = state.apply_next().or_else(|| state.previous.take());
mem::discriminant(&state.current)
};
if self.current_stage == Some(next) {
break next;
} else {
let cur = self.current_stage.map(|i| i);
if let Some(current_state_stages) =
self.current_stage.and_then(|it| self.stages.get_mut(&it))
{
println!("exit {:?}", cur.unwrap());
current_state_stages.exit.run(world, resources);
}
self.current_stage = Some(next);
if let Some(next_state_stages) = self.stages.get_mut(&next) {
println!("enter {:?}", next);
next_state_stages.enter.run(world, resources);
}
}
};
if let Some(current_state_stages) = self.stages.get_mut(¤t_stage) {
println!("update {:?}", current_stage);
current_state_stages.update.run(world, resources);
}
}
} |
My mental model is that each StateStage is standalone, and That is, I would say that your unexpected behaviour is exactly what I would expect from |
Hmm yeah thats a reasonable take. I think whatever solution we land on needs to be reconciled with the upcoming SystemSet::with_run_criteria (#1144). I think it can be accomplished with a new |
This is probably superseded by #1424. |
Extends #1021
Fixes #1117
This also allows avoiding the
Clone
bound on statePossible future work: