bevy/crates
mgi388 7a1fcb7fe7
Rename StateScoped to DespawnOnExitState and add DespawnOnEnterState (#18818)
# Objective

- Alternative to and builds on top of #16284.
- Fixes #15849.

## Solution

- Rename component `StateScoped` to `DespawnOnExitState`.
- Rename system `clear_state_scoped_entities` to
`despawn_entities_on_exit_state`.
- Add `DespawnOnEnterState` and `despawn_entities_on_enter_state` which
is the `OnEnter` equivalent.

> [!NOTE]
> Compared to #16284, the main change is that I did the rename in such a
way as to keep the terms `OnExit` and `OnEnter` together. In my own
game, I was adding `VisibleOnEnterState` and `HiddenOnExitState` and
when naming those, I kept the `OnExit` and `OnEnter` together. When I
checked #16284 it stood out to me that the naming was a bit awkward.
Putting the `State` in the middle and breaking up `OnEnter` and `OnExit`
also breaks searching for those terms.

## Open questions

1. Should we split `enable_state_scoped_entities` into two functions,
one for the `OnEnter` and one for the `OnExit`? I personally have zero
need thus far for the `OnEnter` version, so I'd be interested in not
having this enabled unless I ask for it.
2. If yes to 1., should we follow my lead in my `Visibility` state
components (see below) and name these
`app.enable_despawn_entities_on_enter_state()` and
`app.enable_despawn_entities_on_exit_state()`, which IMO says what it
does on the tin?

## Testing

Ran all changed examples.

## Side note: `VisibleOnEnterState` and `HiddenOnExitState`

For reference to anyone else and to help with the open questions, I'm
including the code I wrote for controlling entity visibility when a
state is entered/exited.

<details>
<summary>visibility.rs</summary>

```rust
use bevy_app::prelude::*;
use bevy_ecs::prelude::*;
use bevy_reflect::prelude::*;
use bevy_render::prelude::*;
use bevy_state::{prelude::*, state::StateTransitionSteps};
use tracing::*;

pub trait AppExtStates {
    fn enable_visible_entities_on_enter_state<S: States>(&mut self) -> &mut Self;

    fn enable_hidden_entities_on_exit_state<S: States>(&mut self) -> &mut Self;
}

impl AppExtStates for App {
    fn enable_visible_entities_on_enter_state<S: States>(&mut self) -> &mut Self {
        self.main_mut()
            .enable_visible_entities_on_enter_state::<S>();
        self
    }

    fn enable_hidden_entities_on_exit_state<S: States>(&mut self) -> &mut Self {
        self.main_mut().enable_hidden_entities_on_exit_state::<S>();
        self
    }
}

impl AppExtStates for SubApp {
    fn enable_visible_entities_on_enter_state<S: States>(&mut self) -> &mut Self {
        if !self
            .world()
            .contains_resource::<Events<StateTransitionEvent<S>>>()
        {
            let name = core::any::type_name::<S>();
            warn!("Visible entities on enter state are enabled for state `{}`, but the state isn't installed in the app!", name);
        }
        // We work with [`StateTransition`] in set
        // [`StateTransitionSteps::ExitSchedules`] as opposed to [`OnExit`],
        // because [`OnExit`] only runs for one specific variant of the state.
        self.add_systems(
            StateTransition,
            update_to_visible_on_enter_state::<S>.in_set(StateTransitionSteps::ExitSchedules),
        )
    }

    fn enable_hidden_entities_on_exit_state<S: States>(&mut self) -> &mut Self {
        if !self
            .world()
            .contains_resource::<Events<StateTransitionEvent<S>>>()
        {
            let name = core::any::type_name::<S>();
            warn!("Hidden entities on exit state are enabled for state `{}`, but the state isn't installed in the app!", name);
        }
        // We work with [`StateTransition`] in set
        // [`StateTransitionSteps::ExitSchedules`] as opposed to [`OnExit`],
        // because [`OnExit`] only runs for one specific variant of the state.
        self.add_systems(
            StateTransition,
            update_to_hidden_on_exit_state::<S>.in_set(StateTransitionSteps::ExitSchedules),
        )
    }
}

#[derive(Clone, Component, Debug, Reflect)]
#[reflect(Component, Debug)]
pub struct VisibleOnEnterState<S: States>(pub S);

#[derive(Clone, Component, Debug, Reflect)]
#[reflect(Component, Debug)]
pub struct HiddenOnExitState<S: States>(pub S);

/// Makes entities marked with [`VisibleOnEnterState<S>`] visible when the state
/// `S` is entered.
pub fn update_to_visible_on_enter_state<S: States>(
    mut transitions: EventReader<StateTransitionEvent<S>>,
    mut query: Query<(&VisibleOnEnterState<S>, &mut Visibility)>,
) {
    // We use the latest event, because state machine internals generate at most
    // 1 transition event (per type) each frame. No event means no change
    // happened and we skip iterating all entities.
    let Some(transition) = transitions.read().last() else {
        return;
    };
    if transition.entered == transition.exited {
        return;
    }
    let Some(entered) = &transition.entered else {
        return;
    };
    for (binding, mut visibility) in query.iter_mut() {
        if binding.0 == *entered {
            visibility.set_if_neq(Visibility::Visible);
        }
    }
}

/// Makes entities marked with [`HiddenOnExitState<S>`] invisible when the state
/// `S` is exited.
pub fn update_to_hidden_on_exit_state<S: States>(
    mut transitions: EventReader<StateTransitionEvent<S>>,
    mut query: Query<(&HiddenOnExitState<S>, &mut Visibility)>,
) {
    // We use the latest event, because state machine internals generate at most
    // 1 transition event (per type) each frame. No event means no change
    // happened and we skip iterating all entities.
    let Some(transition) = transitions.read().last() else {
        return;
    };
    if transition.entered == transition.exited {
        return;
    }
    let Some(exited) = &transition.exited else {
        return;
    };
    for (binding, mut visibility) in query.iter_mut() {
        if binding.0 == *exited {
            visibility.set_if_neq(Visibility::Hidden);
        }
    }
}
```

</details>

---------

Co-authored-by: Benjamin Brienen <Benjamin.Brienen@outlook.com>
Co-authored-by: Ben Frankel <ben.frankel7@gmail.com>
2025-05-06 00:37:04 +00:00
..
bevy_a11y Update accesskit and accesskit_winit requirements (#18285) 2025-03-25 04:04:28 +00:00
bevy_animation Revert "Allow partial support for bevy_log in no_std (#18782)" (#18816) 2025-04-14 21:15:01 +00:00
bevy_anti_aliasing Remove Image::from_buffer name argument (only present in debug "dds" builds) (#18538) 2025-03-25 19:25:01 +00:00
bevy_app Rename bevy_platform_support to bevy_platform (#18813) 2025-04-11 23:13:28 +00:00
bevy_asset ignore files starting with . when loading folders (#11214) 2025-05-05 22:42:01 +00:00
bevy_audio Audio sink seek adopted (#18971) 2025-05-03 11:29:38 +00:00
bevy_color Implemented Alpha for f32. (#18653) 2025-05-06 00:00:17 +00:00
bevy_core_pipeline Rename bevy_platform_support to bevy_platform (#18813) 2025-04-11 23:13:28 +00:00
bevy_derive Link iOS example with rustc, and avoid C trampoline (#14780) 2025-03-17 21:14:07 +00:00
bevy_dev_tools Generic system config (#17962) 2025-03-12 00:12:30 +00:00
bevy_diagnostic Update sysinfo version to 0.35.0 (#19028) 2025-05-05 05:49:23 +00:00
bevy_dylib don't disable std in bevy_dylib (#18807) 2025-04-11 18:44:53 +00:00
bevy_ecs Derive clone_behavior for Components (#18811) 2025-05-06 00:32:59 +00:00
bevy_encase_derive Internalize BevyManifest logic. Switch to RwLock (#18263) 2025-03-12 00:46:01 +00:00
bevy_gilrs Rename bevy_platform_support to bevy_platform (#18813) 2025-04-11 23:13:28 +00:00
bevy_gizmos Rename EntityBorrow/TrustedEntityBorrow to ContainsEntity/EntityEquivalent (#18470) 2025-03-30 06:04:26 +00:00
bevy_gltf Revert "Allow partial support for bevy_log in no_std (#18782)" (#18816) 2025-04-14 21:15:01 +00:00
bevy_image Update ktx2 to 0.4.0 (#19073) 2025-05-05 16:42:36 +00:00
bevy_input Rename bevy_platform_support to bevy_platform (#18813) 2025-04-11 23:13:28 +00:00
bevy_input_focus Switch ChildOf back to tuple struct (#18672) 2025-04-02 00:10:10 +00:00
bevy_internal Revert "Allow partial support for bevy_log in no_std (#18782)" (#18816) 2025-04-14 21:15:01 +00:00
bevy_log feat(log): support customizing default log formatting (#17722) 2025-05-05 23:01:06 +00:00
bevy_macro_utils Fully qualify crate paths in BevyManifest (#18938) 2025-04-28 21:43:48 +00:00
bevy_math Fix rotate_by implementation for Aabb2d (#19015) 2025-05-04 13:05:27 +00:00
bevy_mesh Rename bevy_platform_support to bevy_platform (#18813) 2025-04-11 23:13:28 +00:00
bevy_mikktspace fix new nightly lint on mikktspace (#18988) 2025-04-30 05:19:01 +00:00
bevy_pbr Swap order of eviction/extraction when extracting for specialization (#18846) 2025-04-15 06:44:01 +00:00
bevy_picking Removed conversion from pointer physical coordinates to viewport local coordinates in bevy_picking make_ray function (#18870) 2025-04-27 13:54:28 +00:00
bevy_platform Update spin requirement from 0.9.8 to 0.10.0 (#18655) 2025-04-27 06:18:24 +00:00
bevy_ptr moved Debug from derive to impl_ptr in bevy_ptr (#18042) 2025-02-28 02:54:46 +00:00
bevy_reflect Add NonNilUuid support to bevy_reflect (#18604) 2025-05-04 08:22:57 +00:00
bevy_remote Rename bevy_platform_support to bevy_platform (#18813) 2025-04-11 23:13:28 +00:00
bevy_render Derive clone_behavior for Components (#18811) 2025-05-06 00:32:59 +00:00
bevy_scene Rename bevy_platform_support to bevy_platform (#18813) 2025-04-11 23:13:28 +00:00
bevy_sprite Sprite picking docs fix (#19016) 2025-05-05 17:45:14 +00:00
bevy_state Rename StateScoped to DespawnOnExitState and add DespawnOnEnterState (#18818) 2025-05-06 00:37:04 +00:00
bevy_tasks Rename bevy_platform_support to bevy_platform (#18813) 2025-04-11 23:13:28 +00:00
bevy_text Text background colors (#18892) 2025-05-04 08:18:46 +00:00
bevy_time Rename bevy_platform_support to bevy_platform (#18813) 2025-04-11 23:13:28 +00:00
bevy_transform Revert "Allow partial support for bevy_log in no_std (#18782)" (#18816) 2025-04-14 21:15:01 +00:00
bevy_ui Add a viewport UI widget (#17253) 2025-05-05 22:57:37 +00:00
bevy_utils Rename bevy_platform_support to bevy_platform (#18813) 2025-04-11 23:13:28 +00:00
bevy_window Expose deferred screen edges setting for ios devices (#18729) 2025-04-30 21:24:53 +00:00
bevy_winit Expose deferred screen edges setting for ios devices (#18729) 2025-04-30 21:24:53 +00:00