Skip to content

Enable state scoped entities by default #19354

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions crates/bevy_state/macros/src/states.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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"))
Expand Down
7 changes: 5 additions & 2 deletions crates/bevy_state/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]`
/// 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")]
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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
Expand Down
7 changes: 1 addition & 6 deletions crates/bevy_state/src/state_scoped.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,14 @@ 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::*;
/// use bevy_ecs::prelude::*;
/// use bevy_ecs::system::ScheduleSystem;
///
/// #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States)]
/// #[states(scoped_entities)]
/// enum GameState {
/// #[default]
/// MainMenu,
Expand All @@ -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;
Expand Down Expand Up @@ -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)]
Expand Down
1 change: 0 additions & 1 deletion examples/ecs/state_scoped.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 0 additions & 1 deletion examples/games/alien_cake_addict.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
6 changes: 0 additions & 6 deletions examples/state/computed_states.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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,
(
Expand Down
1 change: 0 additions & 1 deletion examples/testbed/2d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ fn main() {
}

#[derive(Debug, Clone, Eq, PartialEq, Hash, States, Default)]
#[states(scoped_entities)]
enum Scene {
#[default]
Shapes,
Expand Down
1 change: 0 additions & 1 deletion examples/testbed/3d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ fn main() {
}

#[derive(Debug, Clone, Eq, PartialEq, Hash, States, Default)]
#[states(scoped_entities)]
enum Scene {
#[default]
Light,
Expand Down
Original file line number Diff line number Diff line change
@@ -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)]`.