#[cfg(feature = "bevy_reflect")]1use bevy_ecs::reflect::ReflectComponent;2use bevy_ecs::{3component::Component,4entity::Entity,5message::MessageReader,6system::{Commands, Query},7};8#[cfg(feature = "bevy_reflect")]9use bevy_reflect::prelude::*;1011use crate::state::{StateTransitionEvent, States};1213/// Entities marked with this component will be removed14/// when the world's state of the matching type no longer matches the supplied value.15///16/// If you need to disable this behavior, add the attribute `#[states(scoped_entities = false)]` when deriving [`States`].17///18/// ```19/// use bevy_state::prelude::*;20/// use bevy_ecs::prelude::*;21/// use bevy_ecs::system::ScheduleSystem;22///23/// #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States)]24/// enum GameState {25/// #[default]26/// MainMenu,27/// SettingsMenu,28/// InGame,29/// }30///31/// # #[derive(Component)]32/// # struct Player;33///34/// fn spawn_player(mut commands: Commands) {35/// commands.spawn((36/// DespawnOnExit(GameState::InGame),37/// Player38/// ));39/// }40///41/// # struct AppMock;42/// # impl AppMock {43/// # fn init_state<S>(&mut self) {}44/// # fn add_systems<S, M>(&mut self, schedule: S, systems: impl IntoScheduleConfigs<ScheduleSystem, M>) {}45/// # }46/// # struct Update;47/// # let mut app = AppMock;48///49/// app.init_state::<GameState>();50/// app.add_systems(OnEnter(GameState::InGame), spawn_player);51/// ```52#[derive(Component, Clone)]53#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Component, Clone))]54pub struct DespawnOnExit<S: States>(pub S);5556impl<S> Default for DespawnOnExit<S>57where58S: States + Default,59{60fn default() -> Self {61Self(S::default())62}63}6465/// A deprecated alias for [`DespawnOnExit`].66#[deprecated(since = "0.17.0", note = "use DespawnOnExit instead")]67pub type StateScoped<S> = DespawnOnExit<S>;6869/// Despawns entities marked with [`DespawnOnExit<S>`] when their state no70/// longer matches the world state.71pub fn despawn_entities_on_exit_state<S: States>(72mut commands: Commands,73mut transitions: MessageReader<StateTransitionEvent<S>>,74query: Query<(Entity, &DespawnOnExit<S>)>,75) {76// We use the latest event, because state machine internals generate at most 177// transition event (per type) each frame. No event means no change happened78// and we skip iterating all entities.79let Some(transition) = transitions.read().last() else {80return;81};82if transition.entered == transition.exited {83return;84}85let Some(exited) = &transition.exited else {86return;87};88for (entity, binding) in &query {89if binding.0 == *exited {90commands.entity(entity).despawn();91}92}93}9495/// Entities marked with this component will be despawned96/// upon entering the given state.97///98/// ```99/// use bevy_state::prelude::*;100/// use bevy_ecs::{prelude::*, system::ScheduleSystem};101///102/// #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States)]103/// enum GameState {104/// #[default]105/// MainMenu,106/// SettingsMenu,107/// InGame,108/// }109///110/// # #[derive(Component)]111/// # struct Player;112///113/// fn spawn_player(mut commands: Commands) {114/// commands.spawn((115/// DespawnOnEnter(GameState::MainMenu),116/// Player117/// ));118/// }119///120/// # struct AppMock;121/// # impl AppMock {122/// # fn init_state<S>(&mut self) {}123/// # fn add_systems<S, M>(&mut self, schedule: S, systems: impl IntoScheduleConfigs<ScheduleSystem, M>) {}124/// # }125/// # struct Update;126/// # let mut app = AppMock;127///128/// app.init_state::<GameState>();129/// app.add_systems(OnEnter(GameState::InGame), spawn_player);130/// ```131#[derive(Component, Clone)]132#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Component))]133pub struct DespawnOnEnter<S: States>(pub S);134135/// Despawns entities marked with [`DespawnOnEnter<S>`] when their state136/// matches the world state.137pub fn despawn_entities_on_enter_state<S: States>(138mut commands: Commands,139mut transitions: MessageReader<StateTransitionEvent<S>>,140query: Query<(Entity, &DespawnOnEnter<S>)>,141) {142// We use the latest event, because state machine internals generate at most 1143// transition event (per type) each frame. No event means no change happened144// and we skip iterating all entities.145let Some(transition) = transitions.read().last() else {146return;147};148if transition.entered == transition.exited {149return;150}151let Some(entered) = &transition.entered else {152return;153};154for (entity, binding) in &query {155if binding.0 == *entered {156commands.entity(entity).despawn();157}158}159}160161162