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>
This commit is contained in:
parent
60cdefd128
commit
7a1fcb7fe7
11
Cargo.toml
11
Cargo.toml
@ -2305,6 +2305,17 @@ description = "Pipe the output of one system into a second, allowing you to hand
|
|||||||
category = "ECS (Entity Component System)"
|
category = "ECS (Entity Component System)"
|
||||||
wasm = false
|
wasm = false
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "state_scoped"
|
||||||
|
path = "examples/ecs/state_scoped.rs"
|
||||||
|
doc-scrape-examples = true
|
||||||
|
|
||||||
|
[package.metadata.example.state_scoped]
|
||||||
|
name = "State Scoped"
|
||||||
|
description = "Shows how to spawn entities that are automatically despawned either when entering or exiting specific game states."
|
||||||
|
category = "ECS (Entity Component System)"
|
||||||
|
wasm = false
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "system_closure"
|
name = "system_closure"
|
||||||
path = "examples/ecs/system_closure.rs"
|
path = "examples/ecs/system_closure.rs"
|
||||||
|
@ -8,7 +8,7 @@ use crate::{
|
|||||||
setup_state_transitions_in_world, ComputedStates, FreelyMutableState, NextState, State,
|
setup_state_transitions_in_world, ComputedStates, FreelyMutableState, NextState, State,
|
||||||
StateTransition, StateTransitionEvent, StateTransitionSteps, States, SubStates,
|
StateTransition, StateTransitionEvent, StateTransitionSteps, States, SubStates,
|
||||||
},
|
},
|
||||||
state_scoped::clear_state_scoped_entities,
|
state_scoped::{despawn_entities_on_enter_state, despawn_entities_on_exit_state},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "bevy_reflect")]
|
#[cfg(feature = "bevy_reflect")]
|
||||||
@ -62,7 +62,7 @@ pub trait AppExtStates {
|
|||||||
/// If the [`States`] trait was derived with the `#[states(scoped_entities)]` attribute, it
|
/// If the [`States`] trait was derived with the `#[states(scoped_entities)]` attribute, it
|
||||||
/// will be called automatically.
|
/// will be called automatically.
|
||||||
///
|
///
|
||||||
/// For more information refer to [`StateScoped`](crate::state_scoped::StateScoped).
|
/// For more information refer to [`crate::state_scoped`].
|
||||||
fn enable_state_scoped_entities<S: States>(&mut self) -> &mut Self;
|
fn enable_state_scoped_entities<S: States>(&mut self) -> &mut Self;
|
||||||
|
|
||||||
#[cfg(feature = "bevy_reflect")]
|
#[cfg(feature = "bevy_reflect")]
|
||||||
@ -222,11 +222,20 @@ impl AppExtStates for SubApp {
|
|||||||
let name = core::any::type_name::<S>();
|
let name = core::any::type_name::<S>();
|
||||||
warn!("State scoped entities are enabled for state `{}`, but the state isn't installed in the app!", name);
|
warn!("State scoped entities 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.
|
// Note: We work with `StateTransition` in set
|
||||||
|
// `StateTransitionSteps::ExitSchedules` rather than `OnExit`, because
|
||||||
|
// `OnExit` only runs for one specific variant of the state.
|
||||||
self.add_systems(
|
self.add_systems(
|
||||||
StateTransition,
|
StateTransition,
|
||||||
clear_state_scoped_entities::<S>.in_set(StateTransitionSteps::ExitSchedules),
|
despawn_entities_on_exit_state::<S>.in_set(StateTransitionSteps::ExitSchedules),
|
||||||
|
)
|
||||||
|
// Note: We work with `StateTransition` in set
|
||||||
|
// `StateTransitionSteps::EnterSchedules` rather than `OnEnter`, because
|
||||||
|
// `OnEnter` only runs for one specific variant of the state.
|
||||||
|
.add_systems(
|
||||||
|
StateTransition,
|
||||||
|
despawn_entities_on_enter_state::<S>.in_set(StateTransitionSteps::EnterSchedules),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +28,9 @@
|
|||||||
//! - A [`StateTransitionEvent<S>`](crate::state::StateTransitionEvent) that gets fired when a given state changes.
|
//! - A [`StateTransitionEvent<S>`](crate::state::StateTransitionEvent) that gets fired when a given state changes.
|
||||||
//! - The [`in_state<S>`](crate::condition::in_state) and [`state_changed<S>`](crate::condition::state_changed) run conditions - which are used
|
//! - The [`in_state<S>`](crate::condition::in_state) and [`state_changed<S>`](crate::condition::state_changed) run conditions - which are used
|
||||||
//! to determine whether a system should run based on the current state.
|
//! to determine whether a system should run based on the current state.
|
||||||
|
//!
|
||||||
|
//! Bevy also provides ("state-scoped entities")[`crate::state_scoped`] functionality for managing the lifetime of entities in the context of game states.
|
||||||
|
//! This, especially in combination with system scheduling, enables a flexible and expressive way to manage spawning and despawning entities.
|
||||||
|
|
||||||
# and
|
/// Provides tools for managing the lifetime of entities based on state transitions.
|
||||||
/// [`clear_state_scoped_entities`](crate::state_scoped::clear_state_scoped_entities) for managing lifetime of entities.
|
|
||||||
pub mod state_scoped;
|
pub mod state_scoped;
|
||||||
#[cfg(feature = "bevy_app")]
|
#[cfg(feature = "bevy_app")]
|
||||||
/// Provides [`App`](bevy_app::App) and [`SubApp`](bevy_app::SubApp) with methods for registering
|
/// Provides [`App`](bevy_app::App) and [`SubApp`](bevy_app::SubApp) with methods for registering
|
||||||
@ -89,6 +91,6 @@ pub mod prelude {
|
|||||||
OnExit, OnTransition, State, StateSet, StateTransition, StateTransitionEvent, States,
|
OnExit, OnTransition, State, StateSet, StateTransition, StateTransitionEvent, States,
|
||||||
SubStates, TransitionSchedules,
|
SubStates, TransitionSchedules,
|
||||||
},
|
},
|
||||||
state_scoped::StateScoped,
|
state_scoped::{DespawnOnEnterState, DespawnOnExitState},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,11 @@ pub trait States: 'static + Send + Sync + Clone + PartialEq + Eq + Hash + Debug
|
|||||||
/// `ComputedState` dependencies.
|
/// `ComputedState` dependencies.
|
||||||
const DEPENDENCY_DEPTH: usize = 1;
|
const DEPENDENCY_DEPTH: usize = 1;
|
||||||
|
|
||||||
/// Should [`StateScoped`](crate::state_scoped::StateScoped) be enabled for this state? If set to `true`,
|
/// Should [state scoping](crate::state_scoped) be enabled for this state?
|
||||||
/// the `StateScoped` component will be used to remove entities when changing state.
|
/// If set to `true`, the
|
||||||
|
/// [`DespawnOnEnterState`](crate::state_scoped::DespawnOnEnterState) and
|
||||||
|
/// [`DespawnOnExitState`](crate::state_scoped::DespawnOnEnterState)
|
||||||
|
/// components are used to remove entities when entering or exiting the
|
||||||
|
/// state.
|
||||||
const SCOPED_ENTITIES_ENABLED: bool = false;
|
const SCOPED_ENTITIES_ENABLED: bool = false;
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ use crate::state::{StateTransitionEvent, States};
|
|||||||
///
|
///
|
||||||
/// fn spawn_player(mut commands: Commands) {
|
/// fn spawn_player(mut commands: Commands) {
|
||||||
/// commands.spawn((
|
/// commands.spawn((
|
||||||
/// StateScoped(GameState::InGame),
|
/// DespawnOnExitState(GameState::InGame),
|
||||||
/// Player
|
/// Player
|
||||||
/// ));
|
/// ));
|
||||||
/// }
|
/// }
|
||||||
@ -55,9 +55,9 @@ use crate::state::{StateTransitionEvent, States};
|
|||||||
/// ```
|
/// ```
|
||||||
#[derive(Component, Clone)]
|
#[derive(Component, Clone)]
|
||||||
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Component, Clone))]
|
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Component, Clone))]
|
||||||
pub struct StateScoped<S: States>(pub S);
|
pub struct DespawnOnExitState<S: States>(pub S);
|
||||||
|
|
||||||
impl<S> Default for StateScoped<S>
|
impl<S> Default for DespawnOnExitState<S>
|
||||||
where
|
where
|
||||||
S: States + Default,
|
S: States + Default,
|
||||||
{
|
{
|
||||||
@ -66,12 +66,12 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes entities marked with [`StateScoped<S>`]
|
/// Despawns entities marked with [`DespawnOnExitState<S>`] when their state no
|
||||||
/// when their state no longer matches the world state.
|
/// longer matches the world state.
|
||||||
pub fn clear_state_scoped_entities<S: States>(
|
pub fn despawn_entities_on_exit_state<S: States>(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut transitions: EventReader<StateTransitionEvent<S>>,
|
mut transitions: EventReader<StateTransitionEvent<S>>,
|
||||||
query: Query<(Entity, &StateScoped<S>)>,
|
query: Query<(Entity, &DespawnOnExitState<S>)>,
|
||||||
) {
|
) {
|
||||||
// We use the latest event, because state machine internals generate at most 1
|
// 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
|
// transition event (per type) each frame. No event means no change happened
|
||||||
@ -91,3 +91,74 @@ pub fn clear_state_scoped_entities<S: States>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Entities marked with this component will be despawned
|
||||||
|
/// upon entering the given state.
|
||||||
|
///
|
||||||
|
/// To enable this feature remember to configure your application
|
||||||
|
/// with [`enable_state_scoped_entities`](crate::app::AppExtStates::enable_state_scoped_entities) on your state(s) of choice.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use bevy_state::prelude::*;
|
||||||
|
/// use bevy_ecs::{prelude::*, system::ScheduleSystem};
|
||||||
|
///
|
||||||
|
/// #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States)]
|
||||||
|
/// enum GameState {
|
||||||
|
/// #[default]
|
||||||
|
/// MainMenu,
|
||||||
|
/// SettingsMenu,
|
||||||
|
/// InGame,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// # #[derive(Component)]
|
||||||
|
/// # struct Player;
|
||||||
|
///
|
||||||
|
/// fn spawn_player(mut commands: Commands) {
|
||||||
|
/// commands.spawn((
|
||||||
|
/// DespawnOnEnterState(GameState::MainMenu),
|
||||||
|
/// Player
|
||||||
|
/// ));
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// # 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)]
|
||||||
|
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Component))]
|
||||||
|
pub struct DespawnOnEnterState<S: States>(pub S);
|
||||||
|
|
||||||
|
/// Despawns entities marked with [`DespawnOnEnterState<S>`] when their state
|
||||||
|
/// matches the world state.
|
||||||
|
pub fn despawn_entities_on_enter_state<S: States>(
|
||||||
|
mut commands: Commands,
|
||||||
|
mut transitions: EventReader<StateTransitionEvent<S>>,
|
||||||
|
query: Query<(Entity, &DespawnOnEnterState<S>)>,
|
||||||
|
) {
|
||||||
|
// 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 (entity, binding) in &query {
|
||||||
|
if binding.0 == *entered {
|
||||||
|
commands.entity(entity).despawn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -89,7 +89,8 @@ fn add_state_scoped_event_impl<E: Event, S: FreelyMutableState>(
|
|||||||
pub trait StateScopedEventsAppExt {
|
pub trait StateScopedEventsAppExt {
|
||||||
/// Adds an [`Event`] that is automatically cleaned up when leaving the specified `state`.
|
/// Adds an [`Event`] that is automatically cleaned up when leaving the specified `state`.
|
||||||
///
|
///
|
||||||
/// Note that event cleanup is ordered ambiguously relative to [`StateScoped`](crate::prelude::StateScoped) entity
|
/// Note that event cleanup is ordered ambiguously relative to [`DespawnOnEnterState`](crate::prelude::DespawnOnEnterState)
|
||||||
|
/// and [`DespawnOnExitState`](crate::prelude::DespawnOnExitState) entity
|
||||||
/// cleanup and the [`OnExit`] schedule for the target state. All of these (state scoped
|
/// cleanup and the [`OnExit`] schedule for the target state. All of these (state scoped
|
||||||
/// entities and events cleanup, and `OnExit`) occur within schedule [`StateTransition`](crate::prelude::StateTransition)
|
/// entities and events cleanup, and `OnExit`) occur within schedule [`StateTransition`](crate::prelude::StateTransition)
|
||||||
/// and system set `StateTransitionSteps::ExitSchedules`.
|
/// and system set `StateTransitionSteps::ExitSchedules`.
|
||||||
|
@ -329,6 +329,7 @@ Example | Description
|
|||||||
[Run Conditions](../examples/ecs/run_conditions.rs) | Run systems only when one or multiple conditions are met
|
[Run Conditions](../examples/ecs/run_conditions.rs) | Run systems only when one or multiple conditions are met
|
||||||
[Send and receive events](../examples/ecs/send_and_receive_events.rs) | Demonstrates how to send and receive events of the same type in a single system
|
[Send and receive events](../examples/ecs/send_and_receive_events.rs) | Demonstrates how to send and receive events of the same type in a single system
|
||||||
[Startup System](../examples/ecs/startup_system.rs) | Demonstrates a startup system (one that runs once when the app starts up)
|
[Startup System](../examples/ecs/startup_system.rs) | Demonstrates a startup system (one that runs once when the app starts up)
|
||||||
|
[State Scoped](../examples/ecs/state_scoped.rs) | Shows how to spawn entities that are automatically despawned either when entering or exiting specific game states.
|
||||||
[System Closure](../examples/ecs/system_closure.rs) | Show how to use closures as systems, and how to configure `Local` variables by capturing external state
|
[System Closure](../examples/ecs/system_closure.rs) | Show how to use closures as systems, and how to configure `Local` variables by capturing external state
|
||||||
[System Parameter](../examples/ecs/system_param.rs) | Illustrates creating custom system parameters with `SystemParam`
|
[System Parameter](../examples/ecs/system_param.rs) | Illustrates creating custom system parameters with `SystemParam`
|
||||||
[System Piping](../examples/ecs/system_piping.rs) | Pipe the output of one system into a second, allowing you to handle any errors gracefully
|
[System Piping](../examples/ecs/system_piping.rs) | Pipe the output of one system into a second, allowing you to handle any errors gracefully
|
||||||
|
127
examples/ecs/state_scoped.rs
Normal file
127
examples/ecs/state_scoped.rs
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
//! Shows how to spawn entities that are automatically despawned either when
|
||||||
|
//! entering or exiting specific game states.
|
||||||
|
//!
|
||||||
|
//! This pattern is useful for managing menus, levels, or other state-specific
|
||||||
|
//! content that should only exist during certain states.
|
||||||
|
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
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)
|
||||||
|
.add_systems(OnExit(GameState::A), on_a_exit)
|
||||||
|
.add_systems(OnExit(GameState::B), on_b_exit)
|
||||||
|
.add_systems(Update, toggle)
|
||||||
|
.insert_resource(TickTock(Timer::from_seconds(1.0, TimerMode::Repeating)))
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States)]
|
||||||
|
enum GameState {
|
||||||
|
#[default]
|
||||||
|
A,
|
||||||
|
B,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Resource)]
|
||||||
|
struct TickTock(Timer);
|
||||||
|
|
||||||
|
fn on_a_enter(mut commands: Commands) {
|
||||||
|
info!("on_a_enter");
|
||||||
|
commands.spawn((
|
||||||
|
DespawnOnExitState(GameState::A),
|
||||||
|
Text::new("Game is in state 'A'"),
|
||||||
|
TextFont {
|
||||||
|
font_size: 33.0,
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
TextColor(Color::srgb(0.5, 0.5, 1.0)),
|
||||||
|
Node {
|
||||||
|
position_type: PositionType::Absolute,
|
||||||
|
top: Val::Px(0.0),
|
||||||
|
left: Val::Px(0.0),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_a_exit(mut commands: Commands) {
|
||||||
|
info!("on_a_exit");
|
||||||
|
commands.spawn((
|
||||||
|
DespawnOnEnterState(GameState::A),
|
||||||
|
Text::new("Game state 'A' will be back in 1 second"),
|
||||||
|
TextFont {
|
||||||
|
font_size: 33.0,
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
TextColor(Color::srgb(0.5, 0.5, 1.0)),
|
||||||
|
Node {
|
||||||
|
position_type: PositionType::Absolute,
|
||||||
|
top: Val::Px(0.0),
|
||||||
|
left: Val::Px(500.0),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_b_enter(mut commands: Commands) {
|
||||||
|
info!("on_b_enter");
|
||||||
|
commands.spawn((
|
||||||
|
DespawnOnExitState(GameState::B),
|
||||||
|
Text::new("Game is in state 'B'"),
|
||||||
|
TextFont {
|
||||||
|
font_size: 33.0,
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
TextColor(Color::srgb(0.5, 0.5, 1.0)),
|
||||||
|
Node {
|
||||||
|
position_type: PositionType::Absolute,
|
||||||
|
top: Val::Px(50.0),
|
||||||
|
left: Val::Px(0.0),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_b_exit(mut commands: Commands) {
|
||||||
|
info!("on_b_exit");
|
||||||
|
commands.spawn((
|
||||||
|
DespawnOnEnterState(GameState::B),
|
||||||
|
Text::new("Game state 'B' will be back in 1 second"),
|
||||||
|
TextFont {
|
||||||
|
font_size: 33.0,
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
TextColor(Color::srgb(0.5, 0.5, 1.0)),
|
||||||
|
Node {
|
||||||
|
position_type: PositionType::Absolute,
|
||||||
|
top: Val::Px(50.0),
|
||||||
|
left: Val::Px(500.0),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_camera(mut commands: Commands) {
|
||||||
|
commands.spawn(Camera3d::default());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn toggle(
|
||||||
|
time: Res<Time>,
|
||||||
|
mut timer: ResMut<TickTock>,
|
||||||
|
state: Res<State<GameState>>,
|
||||||
|
mut next_state: ResMut<NextState<GameState>>,
|
||||||
|
) {
|
||||||
|
if !timer.0.tick(time.delta()).finished() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*next_state = match state.get() {
|
||||||
|
GameState::A => NextState::Pending(GameState::B),
|
||||||
|
GameState::B => NextState::Pending(GameState::A),
|
||||||
|
}
|
||||||
|
}
|
@ -121,7 +121,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>, mut game: ResMu
|
|||||||
game.player.move_cooldown = Timer::from_seconds(0.3, TimerMode::Once);
|
game.player.move_cooldown = Timer::from_seconds(0.3, TimerMode::Once);
|
||||||
|
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
StateScoped(GameState::Playing),
|
DespawnOnExitState(GameState::Playing),
|
||||||
PointLight {
|
PointLight {
|
||||||
intensity: 2_000_000.0,
|
intensity: 2_000_000.0,
|
||||||
shadows_enabled: true,
|
shadows_enabled: true,
|
||||||
@ -140,7 +140,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>, mut game: ResMu
|
|||||||
.map(|i| {
|
.map(|i| {
|
||||||
let height = rng.gen_range(-0.1..0.1);
|
let height = rng.gen_range(-0.1..0.1);
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
StateScoped(GameState::Playing),
|
DespawnOnExitState(GameState::Playing),
|
||||||
Transform::from_xyz(i as f32, height - 0.2, j as f32),
|
Transform::from_xyz(i as f32, height - 0.2, j as f32),
|
||||||
SceneRoot(cell_scene.clone()),
|
SceneRoot(cell_scene.clone()),
|
||||||
));
|
));
|
||||||
@ -154,7 +154,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>, mut game: ResMu
|
|||||||
game.player.entity = Some(
|
game.player.entity = Some(
|
||||||
commands
|
commands
|
||||||
.spawn((
|
.spawn((
|
||||||
StateScoped(GameState::Playing),
|
DespawnOnExitState(GameState::Playing),
|
||||||
Transform {
|
Transform {
|
||||||
translation: Vec3::new(
|
translation: Vec3::new(
|
||||||
game.player.i as f32,
|
game.player.i as f32,
|
||||||
@ -178,7 +178,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>, mut game: ResMu
|
|||||||
|
|
||||||
// scoreboard
|
// scoreboard
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
StateScoped(GameState::Playing),
|
DespawnOnExitState(GameState::Playing),
|
||||||
Text::new("Score:"),
|
Text::new("Score:"),
|
||||||
TextFont {
|
TextFont {
|
||||||
font_size: 33.0,
|
font_size: 33.0,
|
||||||
@ -340,7 +340,7 @@ fn spawn_bonus(
|
|||||||
game.bonus.entity = Some(
|
game.bonus.entity = Some(
|
||||||
commands
|
commands
|
||||||
.spawn((
|
.spawn((
|
||||||
StateScoped(GameState::Playing),
|
DespawnOnExitState(GameState::Playing),
|
||||||
Transform::from_xyz(
|
Transform::from_xyz(
|
||||||
game.bonus.i as f32,
|
game.bonus.i as f32,
|
||||||
game.board[game.bonus.j][game.bonus.i].height + 0.2,
|
game.board[game.bonus.j][game.bonus.i].height + 0.2,
|
||||||
@ -390,7 +390,7 @@ fn game_over_keyboard(
|
|||||||
// display the number of cake eaten before losing
|
// display the number of cake eaten before losing
|
||||||
fn display_score(mut commands: Commands, game: Res<Game>) {
|
fn display_score(mut commands: Commands, game: Res<Game>) {
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
StateScoped(GameState::GameOver),
|
DespawnOnExitState(GameState::GameOver),
|
||||||
Node {
|
Node {
|
||||||
width: Val::Percent(100.),
|
width: Val::Percent(100.),
|
||||||
align_items: AlignItems::Center,
|
align_items: AlignItems::Center,
|
||||||
|
@ -409,7 +409,7 @@ mod ui {
|
|||||||
|
|
||||||
pub fn setup_game(mut commands: Commands, asset_server: Res<AssetServer>) {
|
pub fn setup_game(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
StateScoped(InGame),
|
DespawnOnExitState(InGame),
|
||||||
Sprite::from_image(asset_server.load("branding/icon.png")),
|
Sprite::from_image(asset_server.load("branding/icon.png")),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -449,7 +449,7 @@ mod ui {
|
|||||||
pub fn setup_paused_screen(mut commands: Commands) {
|
pub fn setup_paused_screen(mut commands: Commands) {
|
||||||
info!("Printing Pause");
|
info!("Printing Pause");
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
StateScoped(IsPaused::Paused),
|
DespawnOnExitState(IsPaused::Paused),
|
||||||
Node {
|
Node {
|
||||||
// center button
|
// center button
|
||||||
width: Val::Percent(100.),
|
width: Val::Percent(100.),
|
||||||
@ -487,7 +487,7 @@ mod ui {
|
|||||||
|
|
||||||
pub fn setup_turbo_text(mut commands: Commands) {
|
pub fn setup_turbo_text(mut commands: Commands) {
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
StateScoped(TurboMode),
|
DespawnOnExitState(TurboMode),
|
||||||
Node {
|
Node {
|
||||||
// center button
|
// center button
|
||||||
width: Val::Percent(100.),
|
width: Val::Percent(100.),
|
||||||
@ -523,7 +523,7 @@ mod ui {
|
|||||||
|
|
||||||
pub fn movement_instructions(mut commands: Commands) {
|
pub fn movement_instructions(mut commands: Commands) {
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
StateScoped(Tutorial::MovementInstructions),
|
DespawnOnExitState(Tutorial::MovementInstructions),
|
||||||
Node {
|
Node {
|
||||||
// center button
|
// center button
|
||||||
width: Val::Percent(100.),
|
width: Val::Percent(100.),
|
||||||
@ -574,7 +574,7 @@ mod ui {
|
|||||||
|
|
||||||
pub fn pause_instructions(mut commands: Commands) {
|
pub fn pause_instructions(mut commands: Commands) {
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
StateScoped(Tutorial::PauseInstructions),
|
DespawnOnExitState(Tutorial::PauseInstructions),
|
||||||
Node {
|
Node {
|
||||||
// center button
|
// center button
|
||||||
width: Val::Percent(100.),
|
width: Val::Percent(100.),
|
||||||
|
@ -197,7 +197,7 @@ mod ui {
|
|||||||
|
|
||||||
pub fn setup_paused_screen(mut commands: Commands) {
|
pub fn setup_paused_screen(mut commands: Commands) {
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
StateScoped(IsPaused::Paused),
|
DespawnOnExitState(IsPaused::Paused),
|
||||||
Node {
|
Node {
|
||||||
// center button
|
// center button
|
||||||
width: Val::Percent(100.),
|
width: Val::Percent(100.),
|
||||||
|
@ -69,7 +69,7 @@ mod shapes {
|
|||||||
mut meshes: ResMut<Assets<Mesh>>,
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
mut materials: ResMut<Assets<ColorMaterial>>,
|
mut materials: ResMut<Assets<ColorMaterial>>,
|
||||||
) {
|
) {
|
||||||
commands.spawn((Camera2d, StateScoped(super::Scene::Shapes)));
|
commands.spawn((Camera2d, DespawnOnExitState(super::Scene::Shapes)));
|
||||||
|
|
||||||
let shapes = [
|
let shapes = [
|
||||||
meshes.add(Circle::new(50.0)),
|
meshes.add(Circle::new(50.0)),
|
||||||
@ -102,7 +102,7 @@ mod shapes {
|
|||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
),
|
),
|
||||||
StateScoped(super::Scene::Shapes),
|
DespawnOnExitState(super::Scene::Shapes),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -127,21 +127,21 @@ mod bloom {
|
|||||||
},
|
},
|
||||||
Tonemapping::TonyMcMapface,
|
Tonemapping::TonyMcMapface,
|
||||||
Bloom::default(),
|
Bloom::default(),
|
||||||
StateScoped(super::Scene::Bloom),
|
DespawnOnExitState(super::Scene::Bloom),
|
||||||
));
|
));
|
||||||
|
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
Mesh2d(meshes.add(Circle::new(100.))),
|
Mesh2d(meshes.add(Circle::new(100.))),
|
||||||
MeshMaterial2d(materials.add(Color::srgb(7.5, 0.0, 7.5))),
|
MeshMaterial2d(materials.add(Color::srgb(7.5, 0.0, 7.5))),
|
||||||
Transform::from_translation(Vec3::new(-200., 0., 0.)),
|
Transform::from_translation(Vec3::new(-200., 0., 0.)),
|
||||||
StateScoped(super::Scene::Bloom),
|
DespawnOnExitState(super::Scene::Bloom),
|
||||||
));
|
));
|
||||||
|
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
Mesh2d(meshes.add(RegularPolygon::new(100., 6))),
|
Mesh2d(meshes.add(RegularPolygon::new(100., 6))),
|
||||||
MeshMaterial2d(materials.add(Color::srgb(6.25, 9.4, 9.1))),
|
MeshMaterial2d(materials.add(Color::srgb(6.25, 9.4, 9.1))),
|
||||||
Transform::from_translation(Vec3::new(200., 0., 0.)),
|
Transform::from_translation(Vec3::new(200., 0., 0.)),
|
||||||
StateScoped(super::Scene::Bloom),
|
DespawnOnExitState(super::Scene::Bloom),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -153,7 +153,7 @@ mod text {
|
|||||||
use bevy::text::TextBounds;
|
use bevy::text::TextBounds;
|
||||||
|
|
||||||
pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||||
commands.spawn((Camera2d, StateScoped(super::Scene::Text)));
|
commands.spawn((Camera2d, DespawnOnExitState(super::Scene::Text)));
|
||||||
|
|
||||||
for (i, justify) in [
|
for (i, justify) in [
|
||||||
JustifyText::Left,
|
JustifyText::Left,
|
||||||
@ -187,14 +187,14 @@ mod text {
|
|||||||
.with_scale(1.0 + Vec2::splat(fraction).extend(1.))
|
.with_scale(1.0 + Vec2::splat(fraction).extend(1.))
|
||||||
.with_rotation(Quat::from_rotation_z(fraction * core::f32::consts::PI)),
|
.with_rotation(Quat::from_rotation_z(fraction * core::f32::consts::PI)),
|
||||||
TextColor(Color::hsla(fraction * 360.0, 0.8, 0.8, 0.8)),
|
TextColor(Color::hsla(fraction * 360.0, 0.8, 0.8, 0.8)),
|
||||||
StateScoped(super::Scene::Text),
|
DespawnOnExitState(super::Scene::Text),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
Text2d::new("This text is invisible."),
|
Text2d::new("This text is invisible."),
|
||||||
Visibility::Hidden,
|
Visibility::Hidden,
|
||||||
StateScoped(super::Scene::Text),
|
DespawnOnExitState(super::Scene::Text),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,7 +211,7 @@ mod text {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Transform::from_translation(dest),
|
Transform::from_translation(dest),
|
||||||
StateScoped(super::Scene::Text),
|
DespawnOnExitState(super::Scene::Text),
|
||||||
));
|
));
|
||||||
|
|
||||||
for anchor in [
|
for anchor in [
|
||||||
@ -225,7 +225,7 @@ mod text {
|
|||||||
TextLayout::new_with_justify(justify),
|
TextLayout::new_with_justify(justify),
|
||||||
Transform::from_translation(dest + Vec3::Z),
|
Transform::from_translation(dest + Vec3::Z),
|
||||||
anchor,
|
anchor,
|
||||||
StateScoped(super::Scene::Text),
|
DespawnOnExitState(super::Scene::Text),
|
||||||
children![
|
children![
|
||||||
(
|
(
|
||||||
TextSpan::new(format!("{}, {}\n", anchor.x, anchor.y)),
|
TextSpan::new(format!("{}, {}\n", anchor.x, anchor.y)),
|
||||||
@ -250,7 +250,7 @@ mod text {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Transform::from_translation(dest - Vec3::Z),
|
Transform::from_translation(dest - Vec3::Z),
|
||||||
StateScoped(super::Scene::Text),
|
DespawnOnExitState(super::Scene::Text),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -263,7 +263,7 @@ mod sprite {
|
|||||||
use bevy::sprite::Anchor;
|
use bevy::sprite::Anchor;
|
||||||
|
|
||||||
pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||||
commands.spawn((Camera2d, StateScoped(super::Scene::Sprite)));
|
commands.spawn((Camera2d, DespawnOnExitState(super::Scene::Sprite)));
|
||||||
for (anchor, flip_x, flip_y, color) in [
|
for (anchor, flip_x, flip_y, color) in [
|
||||||
(Anchor::BOTTOM_LEFT, false, false, Color::WHITE),
|
(Anchor::BOTTOM_LEFT, false, false, Color::WHITE),
|
||||||
(Anchor::BOTTOM_RIGHT, true, false, RED.into()),
|
(Anchor::BOTTOM_RIGHT, true, false, RED.into()),
|
||||||
@ -279,7 +279,7 @@ mod sprite {
|
|||||||
color,
|
color,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
StateScoped(super::Scene::Sprite),
|
DespawnOnExitState(super::Scene::Sprite),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -289,7 +289,7 @@ mod gizmos {
|
|||||||
use bevy::{color::palettes::css::*, prelude::*};
|
use bevy::{color::palettes::css::*, prelude::*};
|
||||||
|
|
||||||
pub fn setup(mut commands: Commands) {
|
pub fn setup(mut commands: Commands) {
|
||||||
commands.spawn((Camera2d, StateScoped(super::Scene::Gizmos)));
|
commands.spawn((Camera2d, DespawnOnExitState(super::Scene::Gizmos)));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_gizmos(mut gizmos: Gizmos) {
|
pub fn draw_gizmos(mut gizmos: Gizmos) {
|
||||||
|
@ -81,7 +81,7 @@ mod light {
|
|||||||
perceptual_roughness: 1.0,
|
perceptual_roughness: 1.0,
|
||||||
..default()
|
..default()
|
||||||
})),
|
})),
|
||||||
StateScoped(CURRENT_SCENE),
|
DespawnOnExitState(CURRENT_SCENE),
|
||||||
));
|
));
|
||||||
|
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
@ -91,7 +91,7 @@ mod light {
|
|||||||
..default()
|
..default()
|
||||||
})),
|
})),
|
||||||
Transform::from_xyz(0.0, 1.0, 0.0),
|
Transform::from_xyz(0.0, 1.0, 0.0),
|
||||||
StateScoped(CURRENT_SCENE),
|
DespawnOnExitState(CURRENT_SCENE),
|
||||||
));
|
));
|
||||||
|
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
@ -102,7 +102,7 @@ mod light {
|
|||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
Transform::from_xyz(1.0, 2.0, 0.0),
|
Transform::from_xyz(1.0, 2.0, 0.0),
|
||||||
StateScoped(CURRENT_SCENE),
|
DespawnOnExitState(CURRENT_SCENE),
|
||||||
));
|
));
|
||||||
|
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
@ -115,7 +115,7 @@ mod light {
|
|||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
Transform::from_xyz(-1.0, 2.0, 0.0).looking_at(Vec3::new(-1.0, 0.0, 0.0), Vec3::Z),
|
Transform::from_xyz(-1.0, 2.0, 0.0).looking_at(Vec3::new(-1.0, 0.0, 0.0), Vec3::Z),
|
||||||
StateScoped(CURRENT_SCENE),
|
DespawnOnExitState(CURRENT_SCENE),
|
||||||
));
|
));
|
||||||
|
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
@ -129,13 +129,13 @@ mod light {
|
|||||||
rotation: Quat::from_rotation_x(-PI / 4.),
|
rotation: Quat::from_rotation_x(-PI / 4.),
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
StateScoped(CURRENT_SCENE),
|
DespawnOnExitState(CURRENT_SCENE),
|
||||||
));
|
));
|
||||||
|
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
Camera3d::default(),
|
Camera3d::default(),
|
||||||
Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
|
Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
|
||||||
StateScoped(CURRENT_SCENE),
|
DespawnOnExitState(CURRENT_SCENE),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -162,7 +162,7 @@ mod bloom {
|
|||||||
Tonemapping::TonyMcMapface,
|
Tonemapping::TonyMcMapface,
|
||||||
Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
|
Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
|
||||||
Bloom::NATURAL,
|
Bloom::NATURAL,
|
||||||
StateScoped(CURRENT_SCENE),
|
DespawnOnExitState(CURRENT_SCENE),
|
||||||
));
|
));
|
||||||
|
|
||||||
let material_emissive1 = materials.add(StandardMaterial {
|
let material_emissive1 = materials.add(StandardMaterial {
|
||||||
@ -187,7 +187,7 @@ mod bloom {
|
|||||||
Mesh3d(mesh.clone()),
|
Mesh3d(mesh.clone()),
|
||||||
MeshMaterial3d(material),
|
MeshMaterial3d(material),
|
||||||
Transform::from_xyz(z as f32 * 2.0, 0.0, 0.0),
|
Transform::from_xyz(z as f32 * 2.0, 0.0, 0.0),
|
||||||
StateScoped(CURRENT_SCENE),
|
DespawnOnExitState(CURRENT_SCENE),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -208,7 +208,7 @@ mod gltf {
|
|||||||
intensity: 250.0,
|
intensity: 250.0,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
StateScoped(CURRENT_SCENE),
|
DespawnOnExitState(CURRENT_SCENE),
|
||||||
));
|
));
|
||||||
|
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
@ -216,13 +216,13 @@ mod gltf {
|
|||||||
shadows_enabled: true,
|
shadows_enabled: true,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
StateScoped(CURRENT_SCENE),
|
DespawnOnExitState(CURRENT_SCENE),
|
||||||
));
|
));
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
SceneRoot(asset_server.load(
|
SceneRoot(asset_server.load(
|
||||||
GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf"),
|
GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf"),
|
||||||
)),
|
)),
|
||||||
StateScoped(CURRENT_SCENE),
|
DespawnOnExitState(CURRENT_SCENE),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -259,7 +259,7 @@ mod animation {
|
|||||||
commands.spawn((
|
commands.spawn((
|
||||||
Camera3d::default(),
|
Camera3d::default(),
|
||||||
Transform::from_xyz(100.0, 100.0, 150.0).looking_at(Vec3::new(0.0, 20.0, 0.0), Vec3::Y),
|
Transform::from_xyz(100.0, 100.0, 150.0).looking_at(Vec3::new(0.0, 20.0, 0.0), Vec3::Y),
|
||||||
StateScoped(CURRENT_SCENE),
|
DespawnOnExitState(CURRENT_SCENE),
|
||||||
));
|
));
|
||||||
|
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
@ -268,13 +268,13 @@ mod animation {
|
|||||||
shadows_enabled: true,
|
shadows_enabled: true,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
StateScoped(CURRENT_SCENE),
|
DespawnOnExitState(CURRENT_SCENE),
|
||||||
));
|
));
|
||||||
|
|
||||||
commands
|
commands
|
||||||
.spawn((
|
.spawn((
|
||||||
SceneRoot(asset_server.load(GltfAssetLabel::Scene(0).from_asset(FOX_PATH))),
|
SceneRoot(asset_server.load(GltfAssetLabel::Scene(0).from_asset(FOX_PATH))),
|
||||||
StateScoped(CURRENT_SCENE),
|
DespawnOnExitState(CURRENT_SCENE),
|
||||||
))
|
))
|
||||||
.observe(pause_animation_frame);
|
.observe(pause_animation_frame);
|
||||||
}
|
}
|
||||||
@ -310,7 +310,7 @@ mod gizmos {
|
|||||||
commands.spawn((
|
commands.spawn((
|
||||||
Camera3d::default(),
|
Camera3d::default(),
|
||||||
Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
|
Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
|
||||||
StateScoped(super::Scene::Gizmos),
|
DespawnOnExitState(super::Scene::Gizmos),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,10 +74,10 @@ mod image {
|
|||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
|
||||||
pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||||
commands.spawn((Camera2d, StateScoped(super::Scene::Image)));
|
commands.spawn((Camera2d, DespawnOnExitState(super::Scene::Image)));
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
ImageNode::new(asset_server.load("branding/bevy_logo_dark.png")),
|
ImageNode::new(asset_server.load("branding/bevy_logo_dark.png")),
|
||||||
StateScoped(super::Scene::Image),
|
DespawnOnExitState(super::Scene::Image),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -86,7 +86,7 @@ mod text {
|
|||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
|
||||||
pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||||
commands.spawn((Camera2d, StateScoped(super::Scene::Text)));
|
commands.spawn((Camera2d, DespawnOnExitState(super::Scene::Text)));
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
Text::new("Hello World."),
|
Text::new("Hello World."),
|
||||||
TextFont {
|
TextFont {
|
||||||
@ -94,7 +94,7 @@ mod text {
|
|||||||
font_size: 200.,
|
font_size: 200.,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
StateScoped(super::Scene::Text),
|
DespawnOnExitState(super::Scene::Text),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -103,7 +103,7 @@ mod grid {
|
|||||||
use bevy::{color::palettes::css::*, prelude::*};
|
use bevy::{color::palettes::css::*, prelude::*};
|
||||||
|
|
||||||
pub fn setup(mut commands: Commands) {
|
pub fn setup(mut commands: Commands) {
|
||||||
commands.spawn((Camera2d, StateScoped(super::Scene::Grid)));
|
commands.spawn((Camera2d, DespawnOnExitState(super::Scene::Grid)));
|
||||||
// Top-level grid (app frame)
|
// Top-level grid (app frame)
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
Node {
|
Node {
|
||||||
@ -119,7 +119,7 @@ mod grid {
|
|||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
BackgroundColor(Color::WHITE),
|
BackgroundColor(Color::WHITE),
|
||||||
StateScoped(super::Scene::Grid),
|
DespawnOnExitState(super::Scene::Grid),
|
||||||
children![
|
children![
|
||||||
// Header
|
// Header
|
||||||
(
|
(
|
||||||
@ -163,14 +163,14 @@ mod borders {
|
|||||||
use bevy::{color::palettes::css::*, prelude::*};
|
use bevy::{color::palettes::css::*, prelude::*};
|
||||||
|
|
||||||
pub fn setup(mut commands: Commands) {
|
pub fn setup(mut commands: Commands) {
|
||||||
commands.spawn((Camera2d, StateScoped(super::Scene::Borders)));
|
commands.spawn((Camera2d, DespawnOnExitState(super::Scene::Borders)));
|
||||||
let root = commands
|
let root = commands
|
||||||
.spawn((
|
.spawn((
|
||||||
Node {
|
Node {
|
||||||
flex_wrap: FlexWrap::Wrap,
|
flex_wrap: FlexWrap::Wrap,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
StateScoped(super::Scene::Borders),
|
DespawnOnExitState(super::Scene::Borders),
|
||||||
))
|
))
|
||||||
.id();
|
.id();
|
||||||
|
|
||||||
@ -256,7 +256,7 @@ mod box_shadow {
|
|||||||
use bevy::{color::palettes::css::*, prelude::*};
|
use bevy::{color::palettes::css::*, prelude::*};
|
||||||
|
|
||||||
pub fn setup(mut commands: Commands) {
|
pub fn setup(mut commands: Commands) {
|
||||||
commands.spawn((Camera2d, StateScoped(super::Scene::BoxShadow)));
|
commands.spawn((Camera2d, DespawnOnExitState(super::Scene::BoxShadow)));
|
||||||
|
|
||||||
commands
|
commands
|
||||||
.spawn((
|
.spawn((
|
||||||
@ -269,7 +269,7 @@ mod box_shadow {
|
|||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
BackgroundColor(GREEN.into()),
|
BackgroundColor(GREEN.into()),
|
||||||
StateScoped(super::Scene::BoxShadow),
|
DespawnOnExitState(super::Scene::BoxShadow),
|
||||||
))
|
))
|
||||||
.with_children(|commands| {
|
.with_children(|commands| {
|
||||||
let example_nodes = [
|
let example_nodes = [
|
||||||
@ -339,7 +339,7 @@ mod text_wrap {
|
|||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
|
||||||
pub fn setup(mut commands: Commands) {
|
pub fn setup(mut commands: Commands) {
|
||||||
commands.spawn((Camera2d, StateScoped(super::Scene::TextWrap)));
|
commands.spawn((Camera2d, DespawnOnExitState(super::Scene::TextWrap)));
|
||||||
|
|
||||||
let root = commands
|
let root = commands
|
||||||
.spawn((
|
.spawn((
|
||||||
@ -351,7 +351,7 @@ mod text_wrap {
|
|||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
BackgroundColor(Color::BLACK),
|
BackgroundColor(Color::BLACK),
|
||||||
StateScoped(super::Scene::TextWrap),
|
DespawnOnExitState(super::Scene::TextWrap),
|
||||||
))
|
))
|
||||||
.id();
|
.id();
|
||||||
|
|
||||||
@ -381,7 +381,7 @@ mod overflow {
|
|||||||
use bevy::{color::palettes::css::*, prelude::*};
|
use bevy::{color::palettes::css::*, prelude::*};
|
||||||
|
|
||||||
pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||||
commands.spawn((Camera2d, StateScoped(super::Scene::Overflow)));
|
commands.spawn((Camera2d, DespawnOnExitState(super::Scene::Overflow)));
|
||||||
let image = asset_server.load("branding/icon.png");
|
let image = asset_server.load("branding/icon.png");
|
||||||
|
|
||||||
commands
|
commands
|
||||||
@ -394,7 +394,7 @@ mod overflow {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
BackgroundColor(BLUE.into()),
|
BackgroundColor(BLUE.into()),
|
||||||
StateScoped(super::Scene::Overflow),
|
DespawnOnExitState(super::Scene::Overflow),
|
||||||
))
|
))
|
||||||
.with_children(|parent| {
|
.with_children(|parent| {
|
||||||
for overflow in [
|
for overflow in [
|
||||||
@ -445,7 +445,7 @@ mod slice {
|
|||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
|
||||||
pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||||
commands.spawn((Camera2d, StateScoped(super::Scene::Slice)));
|
commands.spawn((Camera2d, DespawnOnExitState(super::Scene::Slice)));
|
||||||
let image = asset_server.load("textures/fantasy_ui_borders/numbered_slices.png");
|
let image = asset_server.load("textures/fantasy_ui_borders/numbered_slices.png");
|
||||||
|
|
||||||
let slicer = TextureSlicer {
|
let slicer = TextureSlicer {
|
||||||
@ -463,7 +463,7 @@ mod slice {
|
|||||||
justify_content: JustifyContent::SpaceAround,
|
justify_content: JustifyContent::SpaceAround,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
StateScoped(super::Scene::Slice),
|
DespawnOnExitState(super::Scene::Slice),
|
||||||
))
|
))
|
||||||
.with_children(|parent| {
|
.with_children(|parent| {
|
||||||
for [w, h] in [[150.0, 150.0], [300.0, 150.0], [150.0, 300.0]] {
|
for [w, h] in [[150.0, 150.0], [300.0, 150.0], [150.0, 300.0]] {
|
||||||
@ -489,7 +489,7 @@ mod layout_rounding {
|
|||||||
use bevy::{color::palettes::css::*, prelude::*};
|
use bevy::{color::palettes::css::*, prelude::*};
|
||||||
|
|
||||||
pub fn setup(mut commands: Commands) {
|
pub fn setup(mut commands: Commands) {
|
||||||
commands.spawn((Camera2d, StateScoped(super::Scene::LayoutRounding)));
|
commands.spawn((Camera2d, DespawnOnExitState(super::Scene::LayoutRounding)));
|
||||||
|
|
||||||
commands
|
commands
|
||||||
.spawn((
|
.spawn((
|
||||||
@ -501,7 +501,7 @@ mod layout_rounding {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
BackgroundColor(Color::WHITE),
|
BackgroundColor(Color::WHITE),
|
||||||
StateScoped(super::Scene::LayoutRounding),
|
DespawnOnExitState(super::Scene::LayoutRounding),
|
||||||
))
|
))
|
||||||
.with_children(|commands| {
|
.with_children(|commands| {
|
||||||
for i in 2..12 {
|
for i in 2..12 {
|
||||||
|
10
release-content/migration-guides/rename_StateScoped.md
Normal file
10
release-content/migration-guides/rename_StateScoped.md
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
title: `StateScoped` renamed to `DespawnOnExitState`
|
||||||
|
pull_requests: [18818]
|
||||||
|
---
|
||||||
|
|
||||||
|
Previously, Bevy provided the `StateScoped` component as a way to despawn an entity when **exiting** a state.
|
||||||
|
|
||||||
|
However, it can also be useful to have the opposite behavior, where an entity is despawned when **entering** a state. This is now possible with the new `DespawnOnEnterState` component.
|
||||||
|
|
||||||
|
To support despawning entities when entering a state, in Bevy 0.17 the `StateScoped` component was renamed to `DespawnOnExitState` and `clear_state_scoped_entities` was renamed to `despawn_entities_on_exit_state`. Replace all references and imports.
|
Loading…
Reference in New Issue
Block a user