mirror of
https://github.com/bevyengine/bevy.git
synced 2026-05-06 06:06:42 -04:00
Fix: DespawnOnEnter/OnExit/When can trigger for same state transitions (#23390)
# Objective - Fixes #23071 - Also allow state scoped messages to clear on same state transitions if desired. ## Solution - Add a `&& !transition.allow_same_state_transitions` check whenever the `entered` state and `exited` state are compared. The `allow_same_state_transitions` property is available from `StateTransitionEvent`. This extra comparison is already being used for `OnEnter` and `OnExit`. ## Testing - Added a unit test to see that DespawnOnExit runs during same state transitions if desired, since the logic is basically the same for all three Despawn* Components. - Ran the `state_scoped` example and everything looks ok
This commit is contained in:
@@ -93,7 +93,7 @@ pub fn despawn_entities_when_state<S: States>(
|
||||
let Some(transition) = transitions.read().last() else {
|
||||
return;
|
||||
};
|
||||
if transition.entered == transition.exited {
|
||||
if transition.entered == transition.exited && !transition.allow_same_state_transitions {
|
||||
return;
|
||||
}
|
||||
for (entity, when) in &query {
|
||||
@@ -171,7 +171,7 @@ pub fn despawn_entities_on_exit_state<S: States>(
|
||||
let Some(transition) = transitions.read().last() else {
|
||||
return;
|
||||
};
|
||||
if transition.entered == transition.exited {
|
||||
if transition.entered == transition.exited && !transition.allow_same_state_transitions {
|
||||
return;
|
||||
}
|
||||
let Some(exited) = &transition.exited else {
|
||||
@@ -249,7 +249,7 @@ pub fn despawn_entities_on_enter_state<S: States>(
|
||||
let Some(transition) = transitions.read().last() else {
|
||||
return;
|
||||
};
|
||||
if transition.entered == transition.exited {
|
||||
if transition.entered == transition.exited && !transition.allow_same_state_transitions {
|
||||
return;
|
||||
}
|
||||
let Some(entered) = &transition.entered else {
|
||||
@@ -332,4 +332,59 @@ mod tests {
|
||||
.is_none());
|
||||
assert!(app.world().get_entity(entity).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn despawn_on_exit_same_state_transition() {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, States)]
|
||||
enum State {
|
||||
On,
|
||||
}
|
||||
|
||||
let mut app = App::new();
|
||||
app.add_plugins(StatesPlugin);
|
||||
|
||||
app.insert_state(State::On);
|
||||
app.update();
|
||||
|
||||
assert_eq!(
|
||||
app.world()
|
||||
.resource::<bevy_state::state::State<State>>()
|
||||
.get(),
|
||||
&State::On
|
||||
);
|
||||
|
||||
let entity = app.world_mut().spawn(DespawnOnExit(State::On)).id();
|
||||
assert!(app.world().get_entity(entity).is_ok());
|
||||
|
||||
app.world_mut().commands().set_state(State::On);
|
||||
app.update();
|
||||
|
||||
assert_eq!(
|
||||
app.world()
|
||||
.resource::<bevy_state::state::State<State>>()
|
||||
.get(),
|
||||
&State::On
|
||||
);
|
||||
// entity was despawned on exit, despite setting the state to the same state.
|
||||
// this is because "set_state" runs state transitions even if
|
||||
// the next state and the previous are equal.
|
||||
assert!(app.world().get_entity(entity).is_err());
|
||||
|
||||
let entity = app.world_mut().spawn(DespawnOnExit(State::On)).id();
|
||||
assert!(app.world().get_entity(entity).is_ok());
|
||||
|
||||
app.world_mut().commands().set_state_if_neq(State::On);
|
||||
app.update();
|
||||
|
||||
assert_eq!(
|
||||
app.world()
|
||||
.resource::<bevy_state::state::State<State>>()
|
||||
.get(),
|
||||
&State::On
|
||||
);
|
||||
// entity was not despawned on exit
|
||||
// this is because "set_state_if_neq" skips state transitions since
|
||||
// the app's next state is the same as its previous.
|
||||
assert!(app.world().get_entity(entity).is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ fn clear_messages_on_exit<S: States>(
|
||||
let Some(transition) = transitions.read().last() else {
|
||||
return;
|
||||
};
|
||||
if transition.entered == transition.exited {
|
||||
if transition.entered == transition.exited && !transition.allow_same_state_transitions {
|
||||
return;
|
||||
}
|
||||
let Some(exited) = transition.exited.clone() else {
|
||||
@@ -92,7 +92,7 @@ fn clear_messages_on_enter<S: States>(
|
||||
let Some(transition) = transitions.read().last() else {
|
||||
return;
|
||||
};
|
||||
if transition.entered == transition.exited {
|
||||
if transition.entered == transition.exited && !transition.allow_same_state_transitions {
|
||||
return;
|
||||
}
|
||||
let Some(entered) = transition.entered.clone() else {
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
title: "`DespawnOnEnter` / `DespawnOnExit` can now trigger during same state transitions"
|
||||
pull_requests: [23390]
|
||||
---
|
||||
|
||||
`DespawnOnEnter` and `DespawnOnExit` can now trigger on entities with those components during same state transitions.
|
||||
|
||||
If your application transitions between states using `NextState::set()`, your application will trigger `DespawnOnEnter` and `DespawnOnExit` during same state transitions.
|
||||
|
||||
If this is undesired, use `NextState::set_if_neq()` instead to transition between states. `set_if_neq()` does not run any state transition schedules if the target state is the same as the current one.
|
||||
Reference in New Issue
Block a user