mirror of
https://github.com/bevyengine/bevy
synced 2024-11-21 20:23:28 +00:00
Add insert_state to App. (#11043)
# Objective Fix #10731. ## Solution Rename `App::add_state<T>(&mut self)` to `init_state`, and add `App::insert_state<T>(&mut self, state: T)`. I decided on these names because they are more similar to `init_resource` and `insert_resource`. I also removed the `States` trait's requirement for `Default`. Instead, `init_state` requires `FromWorld`. --- ## Changelog - Renamed `App::add_state` to `init_state`. - Added `App::insert_state`. - Removed the `States` trait's requirement for `Default`. ## Migration Guide - Renamed `App::add_state` to `init_state`.
This commit is contained in:
parent
42f721382c
commit
ba0f8f996f
7 changed files with 67 additions and 18 deletions
|
@ -348,6 +348,10 @@ impl App {
|
|||
self.plugins_state = PluginsState::Cleaned;
|
||||
}
|
||||
|
||||
/// Initializes a [`State`] with standard starting values.
|
||||
///
|
||||
/// If the [`State`] already exists, nothing happens.
|
||||
///
|
||||
/// Adds [`State<S>`] and [`NextState<S>`] resources, [`OnEnter`] and [`OnExit`] schedules
|
||||
/// for each state variant (if they don't already exist), an instance of [`apply_state_transition::<S>`] in
|
||||
/// [`StateTransition`] so that transitions happen before [`Update`](crate::Update) and
|
||||
|
@ -360,8 +364,44 @@ impl App {
|
|||
///
|
||||
/// Note that you can also apply state transitions at other points in the schedule
|
||||
/// by adding the [`apply_state_transition`] system manually.
|
||||
pub fn add_state<S: States>(&mut self) -> &mut Self {
|
||||
self.init_resource::<State<S>>()
|
||||
pub fn init_state<S: States + FromWorld>(&mut self) -> &mut Self {
|
||||
if !self.world.contains_resource::<State<S>>() {
|
||||
self.init_resource::<State<S>>()
|
||||
.init_resource::<NextState<S>>()
|
||||
.add_systems(
|
||||
StateTransition,
|
||||
(
|
||||
run_enter_schedule::<S>.run_if(run_once_condition()),
|
||||
apply_state_transition::<S>,
|
||||
)
|
||||
.chain(),
|
||||
);
|
||||
}
|
||||
|
||||
// The OnEnter, OnExit, and OnTransition schedules are lazily initialized
|
||||
// (i.e. when the first system is added to them), and World::try_run_schedule is used to fail
|
||||
// gracefully if they aren't present.
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Inserts a specific [`State`] to the current [`App`] and
|
||||
/// overrides any [`State`] previously added of the same type.
|
||||
///
|
||||
/// Adds [`State<S>`] and [`NextState<S>`] resources, [`OnEnter`] and [`OnExit`] schedules
|
||||
/// for each state variant (if they don't already exist), an instance of [`apply_state_transition::<S>`] in
|
||||
/// [`StateTransition`] so that transitions happen before [`Update`](crate::Update) and
|
||||
/// a instance of [`run_enter_schedule::<S>`] in [`StateTransition`] with a
|
||||
/// [`run_once`](`run_once_condition`) condition to run the on enter schedule of the
|
||||
/// initial state.
|
||||
///
|
||||
/// If you would like to control how other systems run based on the current state,
|
||||
/// you can emulate this behavior using the [`in_state`] [`Condition`].
|
||||
///
|
||||
/// Note that you can also apply state transitions at other points in the schedule
|
||||
/// by adding the [`apply_state_transition`] system manually.
|
||||
pub fn insert_state<S: States>(&mut self, state: S) -> &mut Self {
|
||||
self.insert_resource(State::new(state))
|
||||
.init_resource::<NextState<S>>()
|
||||
.add_systems(
|
||||
StateTransition,
|
||||
|
@ -1068,7 +1108,7 @@ mod tests {
|
|||
#[test]
|
||||
fn add_systems_should_create_schedule_if_it_does_not_exist() {
|
||||
let mut app = App::new();
|
||||
app.add_state::<AppState>()
|
||||
app.init_state::<AppState>()
|
||||
.add_systems(OnEnter(AppState::MainMenu), (foo, bar));
|
||||
|
||||
app.world.run_schedule(OnEnter(AppState::MainMenu));
|
||||
|
@ -1079,7 +1119,7 @@ mod tests {
|
|||
fn add_systems_should_create_schedule_if_it_does_not_exist2() {
|
||||
let mut app = App::new();
|
||||
app.add_systems(OnEnter(AppState::MainMenu), (foo, bar))
|
||||
.add_state::<AppState>();
|
||||
.init_state::<AppState>();
|
||||
|
||||
app.world.run_schedule(OnEnter(AppState::MainMenu));
|
||||
assert_eq!(app.world.entities().len(), 2);
|
||||
|
|
|
@ -5,6 +5,7 @@ use std::ops::Deref;
|
|||
|
||||
use crate as bevy_ecs;
|
||||
use crate::change_detection::DetectChangesMut;
|
||||
use crate::prelude::FromWorld;
|
||||
#[cfg(feature = "bevy_reflect")]
|
||||
use crate::reflect::ReflectResource;
|
||||
use crate::schedule::ScheduleLabel;
|
||||
|
@ -40,7 +41,7 @@ pub use bevy_ecs_macros::States;
|
|||
/// }
|
||||
///
|
||||
/// ```
|
||||
pub trait States: 'static + Send + Sync + Clone + PartialEq + Eq + Hash + Debug + Default {}
|
||||
pub trait States: 'static + Send + Sync + Clone + PartialEq + Eq + Hash + Debug {}
|
||||
|
||||
/// The label of a [`Schedule`](super::Schedule) that runs whenever [`State<S>`]
|
||||
/// enters this state.
|
||||
|
@ -72,12 +73,8 @@ pub struct OnTransition<S: States> {
|
|||
/// [`apply_state_transition::<S>`] system.
|
||||
///
|
||||
/// The starting state is defined via the [`Default`] implementation for `S`.
|
||||
#[derive(Resource, Default, Debug)]
|
||||
#[cfg_attr(
|
||||
feature = "bevy_reflect",
|
||||
derive(bevy_reflect::Reflect),
|
||||
reflect(Resource, Default)
|
||||
)]
|
||||
#[derive(Resource, Debug)]
|
||||
#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
|
||||
pub struct State<S: States>(S);
|
||||
|
||||
impl<S: States> State<S> {
|
||||
|
@ -94,6 +91,12 @@ impl<S: States> State<S> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<S: States + FromWorld> FromWorld for State<S> {
|
||||
fn from_world(world: &mut World) -> Self {
|
||||
Self(S::from_world(world))
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: States> PartialEq<S> for State<S> {
|
||||
fn eq(&self, other: &S) -> bool {
|
||||
self.get() == other
|
||||
|
@ -113,7 +116,7 @@ impl<S: States> Deref for State<S> {
|
|||
/// To queue a transition, just set the contained value to `Some(next_state)`.
|
||||
/// Note that these transitions can be overridden by other systems:
|
||||
/// only the actual value of this resource at the time of [`apply_state_transition`] matters.
|
||||
#[derive(Resource, Default, Debug)]
|
||||
#[derive(Resource, Debug)]
|
||||
#[cfg_attr(
|
||||
feature = "bevy_reflect",
|
||||
derive(bevy_reflect::Reflect),
|
||||
|
@ -121,6 +124,12 @@ impl<S: States> Deref for State<S> {
|
|||
)]
|
||||
pub struct NextState<S: States>(pub Option<S>);
|
||||
|
||||
impl<S: States> Default for NextState<S> {
|
||||
fn default() -> Self {
|
||||
Self(None)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: States> NextState<S> {
|
||||
/// Tentatively set a planned state transition to `Some(state)`.
|
||||
pub fn set(&mut self, state: S) {
|
||||
|
|
|
@ -12,7 +12,7 @@ use bevy::{asset::LoadedFolder, prelude::*, render::texture::ImageSampler};
|
|||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest())) // fallback to nearest sampling
|
||||
.add_state::<AppState>()
|
||||
.init_state::<AppState>()
|
||||
.add_systems(OnEnter(AppState::Setup), load_textures)
|
||||
.add_systems(Update, check_textures.run_if(in_state(AppState::Setup)))
|
||||
.add_systems(OnEnter(AppState::Finished), setup)
|
||||
|
|
|
@ -33,7 +33,7 @@ struct LevelUnload;
|
|||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_state::<AppState>()
|
||||
.init_state::<AppState>()
|
||||
.add_systems(Startup, setup_system)
|
||||
.add_systems(
|
||||
Update,
|
||||
|
|
|
@ -10,7 +10,7 @@ use bevy::prelude::*;
|
|||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_state::<AppState>()
|
||||
.init_state::<AppState>() // Alternatively we could use .insert_state(AppState::Menu)
|
||||
.add_systems(Startup, setup)
|
||||
// This system runs when we enter `AppState::Menu`, during the `StateTransition` schedule.
|
||||
// All systems from the exit schedule of the state we're leaving are run first,
|
||||
|
|
|
@ -23,7 +23,7 @@ fn main() {
|
|||
5.0,
|
||||
TimerMode::Repeating,
|
||||
)))
|
||||
.add_state::<GameState>()
|
||||
.init_state::<GameState>()
|
||||
.add_systems(Startup, setup_cameras)
|
||||
.add_systems(OnEnter(GameState::Playing), setup)
|
||||
.add_systems(
|
||||
|
|
|
@ -34,7 +34,7 @@ fn main() {
|
|||
.insert_resource(DisplayQuality::Medium)
|
||||
.insert_resource(Volume(7))
|
||||
// Declare the game state, whose starting value is determined by the `Default` trait
|
||||
.add_state::<GameState>()
|
||||
.init_state::<GameState>()
|
||||
.add_systems(Startup, setup)
|
||||
// Adds the plugins for each state
|
||||
.add_plugins((splash::SplashPlugin, menu::MenuPlugin, game::GamePlugin))
|
||||
|
@ -261,7 +261,7 @@ mod menu {
|
|||
// At start, the menu is not enabled. This will be changed in `menu_setup` when
|
||||
// entering the `GameState::Menu` state.
|
||||
// Current screen in the menu is handled by an independent state from `GameState`
|
||||
.add_state::<MenuState>()
|
||||
.init_state::<MenuState>()
|
||||
.add_systems(OnEnter(GameState::Menu), menu_setup)
|
||||
// Systems to handle the main menu screen
|
||||
.add_systems(OnEnter(MenuState::Main), main_menu_setup)
|
||||
|
|
Loading…
Reference in a new issue