fix state transitions for dead/pending so they happen faster
this removes some "dead" frames when pushing/popping states during the transition between them where there would be at least 1 tick where no update/render would be called because the top state would be still in a "dead" or "pending" state
This commit is contained in:
parent
e2888ca710
commit
af09c61796
|
@ -113,6 +113,7 @@ impl<ContextType> StateContainer<ContextType> {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[inline]
|
||||
pub fn pending_transition_in(&mut self) {
|
||||
self.pending_state_change = Some(State::TransitionIn);
|
||||
|
@ -215,6 +216,8 @@ impl<ContextType> States<ContextType> {
|
|||
}
|
||||
|
||||
fn process_state_changes(&mut self, context: &mut ContextType) -> Result<(), StateError> {
|
||||
// TODO: this function is pretty gross honestly.
|
||||
|
||||
if let Some(command) = self.command.take() {
|
||||
match command {
|
||||
StateChange::Push(new_state) => {
|
||||
|
@ -245,37 +248,16 @@ impl<ContextType> States<ContextType> {
|
|||
|
||||
// handle any pending state change queued from the previous frame, so that we can
|
||||
// process the state as necessary below ...
|
||||
// for some pending state changes, we process them here instead of in the match later on
|
||||
// in this function so that we're able to transition between old and new states all in
|
||||
// a single frame. this way we don't have any 'dead' frames where no update/renders get
|
||||
// run because a state is 'stuck' in a dead or pending state still.
|
||||
if let Some(state) = self.states.front_mut() {
|
||||
if let Some(pending_state_change) = state.pending_state_change() {
|
||||
match pending_state_change {
|
||||
State::Dead => state.kill(context)?,
|
||||
State::Paused => state.pause(context)?,
|
||||
State::Active => state.activate(context)?,
|
||||
State::TransitionOut(to) => state.transition_out(to, context)?,
|
||||
State::TransitionIn => state.transition_in(context)?,
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
State::Dead => {
|
||||
state.kill(context)?;
|
||||
|
||||
|
||||
// now figure out what state change processing is needed based on the current state ...
|
||||
match self.state_of_front_state() {
|
||||
Some(State::Pending) => {
|
||||
// top state is just sitting there pending, lets start it up ...
|
||||
let state = self.states.front_mut().unwrap();
|
||||
state.pending_transition_in();
|
||||
},
|
||||
Some(State::Paused) => {
|
||||
if self.pending_state.is_some() {
|
||||
// top state is paused and we have a new state waiting to be added.
|
||||
// add the new state
|
||||
let mut new_state = StateContainer::new(self.pending_state.take().unwrap());
|
||||
new_state.change_state(State::Pending, context);
|
||||
self.states.push_front(new_state);
|
||||
}
|
||||
},
|
||||
Some(State::Dead) => {
|
||||
// remove the dead state
|
||||
self.states.pop_front();
|
||||
|
||||
|
@ -288,9 +270,43 @@ impl<ContextType> States<ContextType> {
|
|||
// otherwise, we're probably waking up a state that was paused and needs to
|
||||
// be resumed since it's once again on top
|
||||
let state = self.states.front_mut().unwrap();
|
||||
state.pending_transition_in();
|
||||
state.transition_in(context)?;
|
||||
}
|
||||
},
|
||||
State::Paused => {
|
||||
state.pause(context)?;
|
||||
|
||||
if self.pending_state.is_some() {
|
||||
// top state is paused and we have a new state waiting to be added.
|
||||
// add the new state
|
||||
let mut new_state = StateContainer::new(self.pending_state.take().unwrap());
|
||||
new_state.change_state(State::Pending, context);
|
||||
self.states.push_front(new_state);
|
||||
}
|
||||
},
|
||||
State::Active => state.activate(context)?,
|
||||
State::TransitionOut(to) => state.transition_out(to, context)?,
|
||||
State::TransitionIn => state.transition_in(context)?,
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// special case, switch pending state into transition right away so we don't lose a frame
|
||||
if self.state_of_front_state() == Some(State::Pending) {
|
||||
// top state is just sitting there pending, lets start it up ...
|
||||
let state = self.states.front_mut().unwrap();
|
||||
state.transition_in(context)?;
|
||||
}
|
||||
|
||||
// now figure out what state change processing is needed based on the current state ...
|
||||
match self.state_of_front_state() {
|
||||
Some(State::Paused) => {
|
||||
panic!("oops - paused");
|
||||
},
|
||||
Some(State::Dead) => {
|
||||
panic!("oops - dead");
|
||||
},
|
||||
Some(State::TransitionIn) => {
|
||||
let state = self.states.front_mut().unwrap();
|
||||
if state.state().transition(State::TransitionIn, context) {
|
||||
|
@ -459,13 +475,13 @@ mod tests {
|
|||
|
||||
states.push(TestState::new(FOO))?;
|
||||
assert_eq!(context.take_log(), vec![]);
|
||||
tick(&mut states, &mut context)?;
|
||||
assert_eq!(context.take_log(), vec![StateChange(FOO, Pending, Dead)]);
|
||||
|
||||
// state will transition in
|
||||
tick(&mut states, &mut context)?;
|
||||
assert_eq!(
|
||||
context.take_log(),
|
||||
vec![
|
||||
StateChange(FOO, Pending, Dead),
|
||||
StateChange(FOO, TransitionIn, Pending),
|
||||
Transition(FOO, TransitionIn),
|
||||
Update(FOO, TransitionIn),
|
||||
|
@ -485,6 +501,7 @@ mod tests {
|
|||
|
||||
states.pop()?;
|
||||
assert_eq!(context.take_log(), vec![]);
|
||||
|
||||
// state begins to transition out to 'dead'
|
||||
tick(&mut states, &mut context)?;
|
||||
assert_eq!(
|
||||
|
@ -519,13 +536,13 @@ mod tests {
|
|||
|
||||
states.push(TestState::new_with_transition_length(FOO, 5))?;
|
||||
assert_eq!(context.take_log(), vec![]);
|
||||
tick(&mut states, &mut context)?;
|
||||
assert_eq!(context.take_log(), vec![StateChange(FOO, Pending, Dead)]);
|
||||
|
||||
// state will transition in
|
||||
tick(&mut states, &mut context)?;
|
||||
assert_eq!(
|
||||
context.take_log(),
|
||||
vec![
|
||||
StateChange(FOO, Pending, Dead),
|
||||
StateChange(FOO, TransitionIn, Pending),
|
||||
Transition(FOO, TransitionIn),
|
||||
Update(FOO, TransitionIn),
|
||||
|
@ -557,6 +574,7 @@ mod tests {
|
|||
|
||||
states.pop()?;
|
||||
assert_eq!(context.take_log(), vec![]);
|
||||
|
||||
// state begins to transition out to 'dead'
|
||||
tick(&mut states, &mut context)?;
|
||||
assert_eq!(
|
||||
|
@ -603,16 +621,15 @@ mod tests {
|
|||
let mut context = TestContext::new();
|
||||
|
||||
// push first state
|
||||
|
||||
states.push(TestState::new(FIRST))?;
|
||||
assert_eq!(context.take_log(), vec![]);
|
||||
tick(&mut states, &mut context)?;
|
||||
assert_eq!(context.take_log(), vec![StateChange(FIRST, Pending, Dead)]);
|
||||
|
||||
// first state will transition in
|
||||
tick(&mut states, &mut context)?;
|
||||
assert_eq!(
|
||||
context.take_log(),
|
||||
vec![
|
||||
StateChange(FIRST, Pending, Dead),
|
||||
StateChange(FIRST, TransitionIn, Pending),
|
||||
Transition(FIRST, TransitionIn),
|
||||
Update(FIRST, TransitionIn),
|
||||
|
@ -631,9 +648,9 @@ mod tests {
|
|||
);
|
||||
|
||||
// push second state
|
||||
|
||||
states.push(TestState::new(SECOND))?;
|
||||
assert_eq!(context.take_log(), vec![]);
|
||||
|
||||
// first state begins to transition out to 'paused' state
|
||||
tick(&mut states, &mut context)?;
|
||||
assert_eq!(
|
||||
|
@ -646,22 +663,13 @@ mod tests {
|
|||
]
|
||||
);
|
||||
// state finished transitioning out, now is paused
|
||||
// second state starts up, will transition in
|
||||
tick(&mut states, &mut context)?;
|
||||
assert_eq!(
|
||||
context.take_log(),
|
||||
vec![
|
||||
StateChange(FIRST, Paused, TransitionOut(TransitionTo::Paused)),
|
||||
StateChange(SECOND, Pending, Dead),
|
||||
]
|
||||
);
|
||||
// second state starts up
|
||||
tick(&mut states, &mut context)?;
|
||||
assert_eq!(context.take_log(), vec![]);
|
||||
// second state will transition in
|
||||
tick(&mut states, &mut context)?;
|
||||
assert_eq!(
|
||||
context.take_log(),
|
||||
vec![
|
||||
StateChange(SECOND, TransitionIn, Pending),
|
||||
Transition(SECOND, TransitionIn),
|
||||
Update(SECOND, TransitionIn),
|
||||
|
@ -680,9 +688,9 @@ mod tests {
|
|||
);
|
||||
|
||||
// pop second state
|
||||
|
||||
states.pop()?;
|
||||
assert_eq!(context.take_log(), vec![]);
|
||||
|
||||
// second state begins to transition out to 'dead'
|
||||
tick(&mut states, &mut context)?;
|
||||
assert_eq!(
|
||||
|
@ -694,14 +702,13 @@ mod tests {
|
|||
Render(SECOND, TransitionOut(TransitionTo::Dead))
|
||||
]
|
||||
);
|
||||
// second state finished transitioning out, now dies. first state wakes up again.
|
||||
tick(&mut states, &mut context)?;
|
||||
assert_eq!(context.take_log(), vec![StateChange(SECOND, Dead, TransitionOut(TransitionTo::Dead))]);
|
||||
// first state will transition in
|
||||
// second state finished transitioning out, now dies. first state wakes up again and
|
||||
// starts to transition back in
|
||||
tick(&mut states, &mut context)?;
|
||||
assert_eq!(
|
||||
context.take_log(),
|
||||
vec![
|
||||
StateChange(SECOND, Dead, TransitionOut(TransitionTo::Dead)),
|
||||
StateChange(FIRST, TransitionIn, Paused),
|
||||
Transition(FIRST, TransitionIn),
|
||||
Update(FIRST, TransitionIn),
|
||||
|
@ -720,9 +727,9 @@ mod tests {
|
|||
);
|
||||
|
||||
// pop first state
|
||||
|
||||
states.pop()?;
|
||||
assert_eq!(context.take_log(), vec![]);
|
||||
|
||||
// first state begins to transition out to 'dead'
|
||||
tick(&mut states, &mut context)?;
|
||||
assert_eq!(
|
||||
|
@ -757,16 +764,15 @@ mod tests {
|
|||
let mut context = TestContext::new();
|
||||
|
||||
// push first state
|
||||
|
||||
states.push(TestState::new_with_transition_length(FIRST, 3))?;
|
||||
assert_eq!(context.take_log(), vec![]);
|
||||
tick(&mut states, &mut context)?;
|
||||
assert_eq!(context.take_log(), vec![StateChange(FIRST, Pending, Dead)]);
|
||||
|
||||
// first state will transition in
|
||||
tick(&mut states, &mut context)?;
|
||||
assert_eq!(
|
||||
context.take_log(),
|
||||
vec![
|
||||
StateChange(FIRST, Pending, Dead),
|
||||
StateChange(FIRST, TransitionIn, Pending),
|
||||
Transition(FIRST, TransitionIn),
|
||||
Update(FIRST, TransitionIn),
|
||||
|
@ -800,6 +806,7 @@ mod tests {
|
|||
|
||||
states.push(TestState::new_with_transition_length(SECOND, 5))?;
|
||||
assert_eq!(context.take_log(), vec![]);
|
||||
|
||||
// first state begins to transition out to 'paused' state
|
||||
tick(&mut states, &mut context)?;
|
||||
assert_eq!(
|
||||
|
@ -823,23 +830,13 @@ mod tests {
|
|||
]
|
||||
);
|
||||
}
|
||||
// first state finished transitioning out, now is paused
|
||||
// first state finished transitioning out, now is paused. second state will transition in
|
||||
tick(&mut states, &mut context)?;
|
||||
assert_eq!(
|
||||
context.take_log(),
|
||||
vec![
|
||||
StateChange(FIRST, Paused, TransitionOut(TransitionTo::Paused)),
|
||||
StateChange(SECOND, Pending, Dead)
|
||||
]
|
||||
);
|
||||
// second state starts up
|
||||
tick(&mut states, &mut context)?;
|
||||
assert_eq!(context.take_log(), vec![]);
|
||||
// second state will transition in
|
||||
tick(&mut states, &mut context)?;
|
||||
assert_eq!(
|
||||
context.take_log(),
|
||||
vec![
|
||||
StateChange(SECOND, Pending, Dead),
|
||||
StateChange(SECOND, TransitionIn, Pending),
|
||||
Transition(SECOND, TransitionIn),
|
||||
Update(SECOND, TransitionIn),
|
||||
|
@ -870,9 +867,9 @@ mod tests {
|
|||
);
|
||||
|
||||
// pop second state
|
||||
|
||||
states.pop()?;
|
||||
assert_eq!(context.take_log(), vec![]);
|
||||
|
||||
// second state begins to transition out to 'dead'
|
||||
tick(&mut states, &mut context)?;
|
||||
assert_eq!(
|
||||
|
@ -896,14 +893,13 @@ mod tests {
|
|||
]
|
||||
);
|
||||
}
|
||||
// second state finished transitioning out, now dies. first state wakes up again.
|
||||
tick(&mut states, &mut context)?;
|
||||
assert_eq!(context.take_log(), vec![StateChange(SECOND, Dead, TransitionOut(TransitionTo::Dead))]);
|
||||
// first state will transition in
|
||||
// second state finished transitioning out, now dies. first state wakes up again and
|
||||
// starts to transition back in
|
||||
tick(&mut states, &mut context)?;
|
||||
assert_eq!(
|
||||
context.take_log(),
|
||||
vec![
|
||||
StateChange(SECOND, Dead, TransitionOut(TransitionTo::Dead)),
|
||||
StateChange(FIRST, TransitionIn, Paused),
|
||||
Transition(FIRST, TransitionIn),
|
||||
Update(FIRST, TransitionIn),
|
||||
|
@ -934,9 +930,9 @@ mod tests {
|
|||
);
|
||||
|
||||
// pop first state
|
||||
|
||||
states.pop()?;
|
||||
assert_eq!(context.take_log(), vec![]);
|
||||
|
||||
// first state begins to transition out to 'dead'
|
||||
tick(&mut states, &mut context)?;
|
||||
assert_eq!(
|
||||
|
@ -1029,17 +1025,16 @@ mod tests {
|
|||
let mut states = States::<TestContext>::new();
|
||||
let mut context = TestContext::new();
|
||||
|
||||
// pop first state. it will do the rest this time ...
|
||||
|
||||
// push first state. it will do the rest this time ...
|
||||
states.push(SelfPushPopState::new(FIRST, Some(5), 10))?;
|
||||
assert_eq!(context.take_log(), vec![]);
|
||||
tick(&mut states, &mut context)?;
|
||||
assert_eq!(context.take_log(), vec![StateChange(FIRST, Pending, Dead)]);
|
||||
|
||||
// first state will transition in
|
||||
tick(&mut states, &mut context)?;
|
||||
assert_eq!(
|
||||
context.take_log(),
|
||||
vec![
|
||||
StateChange(FIRST, Pending, Dead),
|
||||
StateChange(FIRST, TransitionIn, Pending),
|
||||
Transition(FIRST, TransitionIn),
|
||||
Update(FIRST, TransitionIn),
|
||||
|
@ -1079,23 +1074,13 @@ mod tests {
|
|||
Render(FIRST, TransitionOut(TransitionTo::Paused))
|
||||
]
|
||||
);
|
||||
// first state finished transitioning out, now is paused
|
||||
// first state finished transitioning out, now is paused. second state will transition in
|
||||
tick(&mut states, &mut context)?;
|
||||
assert_eq!(
|
||||
context.take_log(),
|
||||
vec![
|
||||
StateChange(FIRST, Paused, TransitionOut(TransitionTo::Paused)),
|
||||
StateChange(SECOND, Pending, Dead)
|
||||
]
|
||||
);
|
||||
// second state starts up
|
||||
tick(&mut states, &mut context)?;
|
||||
assert_eq!(context.take_log(), vec![]);
|
||||
// second state will transition in
|
||||
tick(&mut states, &mut context)?;
|
||||
assert_eq!(
|
||||
context.take_log(),
|
||||
vec![
|
||||
StateChange(SECOND, Pending, Dead),
|
||||
StateChange(SECOND, TransitionIn, Pending),
|
||||
Transition(SECOND, TransitionIn),
|
||||
Update(SECOND, TransitionIn),
|
||||
|
@ -1135,14 +1120,13 @@ mod tests {
|
|||
Render(SECOND, TransitionOut(TransitionTo::Dead))
|
||||
]
|
||||
);
|
||||
// second state finished transitioning out, now dies. first state wakes up again.
|
||||
tick(&mut states, &mut context)?;
|
||||
assert_eq!(context.take_log(), vec![StateChange(SECOND, Dead, TransitionOut(TransitionTo::Dead))]);
|
||||
// first state will transition in
|
||||
// second state finished transitioning out, now dies. first state wakes up again and
|
||||
// starts to transition back in
|
||||
tick(&mut states, &mut context)?;
|
||||
assert_eq!(
|
||||
context.take_log(),
|
||||
vec![
|
||||
StateChange(SECOND, Dead, TransitionOut(TransitionTo::Dead)),
|
||||
StateChange(FIRST, TransitionIn, Paused),
|
||||
Transition(FIRST, TransitionIn),
|
||||
Update(FIRST, TransitionIn),
|
||||
|
@ -1205,17 +1189,13 @@ mod tests {
|
|||
|
||||
states.push(TestState::new(FOO))?;
|
||||
assert_eq!(context.take_log(), vec![]);
|
||||
tick(&mut states, &mut context)?;
|
||||
assert_eq!(context.take_log(), vec![StateChange(FOO, Pending, Dead)]);
|
||||
|
||||
assert_matches!(states.push(TestState::new(123)), Err(StateError::HasPendingStateChange));
|
||||
assert_matches!(states.pop(), Err(StateError::HasPendingStateChange));
|
||||
|
||||
// state will transition in
|
||||
tick(&mut states, &mut context)?;
|
||||
assert_eq!(
|
||||
context.take_log(),
|
||||
vec![
|
||||
StateChange(FOO, Pending, Dead),
|
||||
StateChange(FOO, TransitionIn, Pending),
|
||||
Transition(FOO, TransitionIn),
|
||||
Update(FOO, TransitionIn),
|
||||
|
@ -1239,6 +1219,7 @@ mod tests {
|
|||
|
||||
states.pop()?;
|
||||
assert_eq!(context.take_log(), vec![]);
|
||||
|
||||
// state begins to transition out to 'dead'
|
||||
tick(&mut states, &mut context)?;
|
||||
assert_eq!(
|
||||
|
|
Loading…
Reference in a new issue