Enable state scoped entities by default (#19354)

# Objective

- Enable state scoped entities by default
- Provide a way to disable it when needed

---------

Co-authored-by: Ben Frankel <ben.frankel7@gmail.com>
This commit is contained in:
François Mockers 2025-05-26 22:26:41 +02:00 committed by GitHub
parent 8db7b6e122
commit 8a223be651
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 21 additions and 21 deletions

View File

@ -1,6 +1,6 @@
use proc_macro::TokenStream;
use quote::{format_ident, quote};
use syn::{parse_macro_input, spanned::Spanned, DeriveInput, Pat, Path, Result};
use syn::{parse_macro_input, spanned::Spanned, DeriveInput, LitBool, Pat, Path, Result};
use crate::bevy_state_path;
@ -13,14 +13,16 @@ struct StatesAttrs {
fn parse_states_attr(ast: &DeriveInput) -> Result<StatesAttrs> {
let mut attrs = StatesAttrs {
scoped_entities_enabled: false,
scoped_entities_enabled: true,
};
for attr in ast.attrs.iter() {
if attr.path().is_ident(STATES) {
attr.parse_nested_meta(|nested| {
if nested.path.is_ident(SCOPED_ENTITIES) {
attrs.scoped_entities_enabled = true;
if let Ok(value) = nested.value() {
attrs.scoped_entities_enabled = value.parse::<LitBool>()?.value();
}
Ok(())
} else {
Err(nested.error("Unsupported attribute"))

View File

@ -59,10 +59,11 @@ pub trait AppExtStates {
/// Enable state-scoped entity clearing for state `S`.
///
/// If the [`States`] trait was derived with the `#[states(scoped_entities)]` attribute, it
/// will be called automatically.
/// This is enabled by default. If you don't want this behavior, add the `#[states(scoped_entities = false)]`
/// attribute when deriving the [`States`] trait.
///
/// For more information refer to [`crate::state_scoped`].
#[doc(hidden)]
fn enable_state_scoped_entities<S: States>(&mut self) -> &mut Self;
#[cfg(feature = "bevy_reflect")]
@ -214,6 +215,7 @@ impl AppExtStates for SubApp {
self
}
#[doc(hidden)]
fn enable_state_scoped_entities<S: States>(&mut self) -> &mut Self {
if !self
.world()
@ -285,6 +287,7 @@ impl AppExtStates for App {
self
}
#[doc(hidden)]
fn enable_state_scoped_entities<S: States>(&mut self) -> &mut Self {
self.main_mut().enable_state_scoped_entities::<S>();
self

View File

@ -14,8 +14,7 @@ use crate::state::{StateTransitionEvent, States};
/// Entities marked with this component will be removed
/// when the world's state of the matching type no longer matches the supplied value.
///
/// To enable this feature remember to add the attribute `#[states(scoped_entities)]` when deriving [`States`].
/// It's also possible to enable it when adding the state to an app with [`enable_state_scoped_entities`](crate::app::AppExtStates::enable_state_scoped_entities).
/// If you need to disable this behavior, add the attribute `#[states(scoped_entities = false)]` when deriving [`States`].
///
/// ```
/// use bevy_state::prelude::*;
@ -23,7 +22,6 @@ use crate::state::{StateTransitionEvent, States};
/// use bevy_ecs::system::ScheduleSystem;
///
/// #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States)]
/// #[states(scoped_entities)]
/// enum GameState {
/// #[default]
/// MainMenu,
@ -44,7 +42,6 @@ use crate::state::{StateTransitionEvent, States};
/// # struct AppMock;
/// # impl AppMock {
/// # fn init_state<S>(&mut self) {}
/// # fn enable_state_scoped_entities<S>(&mut self) {}
/// # fn add_systems<S, M>(&mut self, schedule: S, systems: impl IntoScheduleConfigs<ScheduleSystem, M>) {}
/// # }
/// # struct Update;
@ -123,14 +120,12 @@ pub fn despawn_entities_on_exit_state<S: States>(
/// # struct AppMock;
/// # impl AppMock {
/// # fn init_state<S>(&mut self) {}
/// # fn enable_state_scoped_entities<S>(&mut self) {}
/// # fn add_systems<S, M>(&mut self, schedule: S, systems: impl IntoScheduleConfigs<ScheduleSystem, M>) {}
/// # }
/// # struct Update;
/// # let mut app = AppMock;
///
/// app.init_state::<GameState>();
/// app.enable_state_scoped_entities::<GameState>();
/// app.add_systems(OnEnter(GameState::InGame), spawn_player);
/// ```
#[derive(Component, Clone)]

View File

@ -10,7 +10,6 @@ fn main() {
App::new()
.add_plugins(DefaultPlugins)
.init_state::<GameState>()
.enable_state_scoped_entities::<GameState>()
.add_systems(Startup, setup_camera)
.add_systems(OnEnter(GameState::A), on_a_enter)
.add_systems(OnEnter(GameState::B), on_b_enter)

View File

@ -25,7 +25,6 @@ fn main() {
TimerMode::Repeating,
)))
.init_state::<GameState>()
.enable_state_scoped_entities::<GameState>()
.add_systems(Startup, setup_cameras)
.add_systems(OnEnter(GameState::Playing), setup)
.add_systems(

View File

@ -184,9 +184,6 @@ fn main() {
// We only want to run the [`setup_game`] function when we enter the [`AppState::InGame`] state, regardless
// of whether the game is paused or not.
.add_systems(OnEnter(InGame), setup_game)
// And we only want to run the [`clear_game`] function when we leave the [`AppState::InGame`] state, regardless
// of whether we're paused.
.enable_state_scoped_entities::<InGame>()
// We want the color change, toggle_pause and quit_to_menu systems to ignore the paused condition, so we can use the [`InGame`] derived
// state here as well.
.add_systems(
@ -200,15 +197,12 @@ fn main() {
)
// We can continue setting things up, following all the same patterns used above and in the `states` example.
.add_systems(OnEnter(IsPaused::Paused), setup_paused_screen)
.enable_state_scoped_entities::<IsPaused>()
.add_systems(OnEnter(TurboMode), setup_turbo_text)
.enable_state_scoped_entities::<TurboMode>()
.add_systems(
OnEnter(Tutorial::MovementInstructions),
movement_instructions,
)
.add_systems(OnEnter(Tutorial::PauseInstructions), pause_instructions)
.enable_state_scoped_entities::<Tutorial>()
.add_systems(
Update,
(

View File

@ -26,7 +26,6 @@ fn main() {
}
#[derive(Debug, Clone, Eq, PartialEq, Hash, States, Default)]
#[states(scoped_entities)]
enum Scene {
#[default]
Shapes,

View File

@ -26,7 +26,6 @@ fn main() {
}
#[derive(Debug, Clone, Eq, PartialEq, Hash, States, Default)]
#[states(scoped_entities)]
enum Scene {
#[default]
Light,

View File

@ -0,0 +1,10 @@
---
title: Entities are now state scoped by default
pull_requests: [19354]
---
State scoped entities is now enabled by default, and you don't need to call `app.enable_state_scoped_entities::<State>()` anymore.
If you were previously adding the `#[states(scoped_entities)]` attribute when deriving the `States` trait, you can remove it.
If you want to keep the previous behavior, you must add the attribute `#[states(scoped_entities = false)]`.