use std::fmt::Debug; use std::hash::Hash; use std::mem; use crate as bevy_ecs; use crate::schedule::{ScheduleLabel, SystemSet}; use crate::system::Resource; use crate::world::World; /// Types that can define world-wide states in a finite-state machine. /// /// The [`Default`] trait defines the starting state. /// Multiple states can be defined for the same world, /// allowing you to classify the state of the world across orthogonal dimensions. /// You can access the current state of type `T` with the [`State`] resource, /// and the queued state with the [`NextState`] resource. /// /// State transitions typically occur in the [`OnEnter`] and [`OnExit`] schedules, /// which can be run via the [`apply_state_transition::`] system. /// Systems that run each frame in various states are typically stored in the main schedule, /// and are conventionally part of the [`OnUpdate(T::Variant)`] system set. /// /// # Example /// /// ```rust /// use bevy_ecs::prelude::States; /// /// #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default)] /// enum GameState { /// #[default] /// MainMenu, /// SettingsMenu, /// InGame, /// } /// /// impl States for GameState { /// type Iter = std::array::IntoIter; /// /// fn variants() -> Self::Iter { /// [GameState::MainMenu, GameState::SettingsMenu, GameState::InGame].into_iter() /// } /// } /// /// ``` pub trait States: 'static + Send + Sync + Clone + PartialEq + Eq + Hash + Debug + Default { type Iter: Iterator; /// Returns an iterator over all the state variants. fn variants() -> Self::Iter; } /// The label of a [`Schedule`](super::Schedule) that runs whenever [`State`] /// enters this state. #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)] pub struct OnEnter(pub S); /// The label of a [`Schedule`](super::Schedule) that runs whenever [`State`] /// exits this state. #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)] pub struct OnExit(pub S); /// A [`SystemSet`] that will run within `CoreSet::StateTransitions` when this state is active. /// /// This is provided for convenience. A more general [`state_equals`](crate::schedule::common_conditions::state_equals) /// [condition](super::Condition) also exists for systems that need to run elsewhere. #[derive(SystemSet, Clone, Debug, PartialEq, Eq, Hash)] pub struct OnUpdate(pub S); /// A finite-state machine whose transitions have associated schedules /// ([`OnEnter(state)`] and [`OnExit(state)`]). /// /// The current state value can be accessed through this resource. To *change* the state, /// queue a transition in the [`NextState`] resource, and it will be applied by the next /// [`apply_state_transition::`] system. /// /// The starting state is defined via the [`Default`] implementation for `S`. #[derive(Resource, Default)] pub struct State(pub S); /// The next state of [`State`]. /// /// To queue a transition, just set the contained value to `Some(next_state)`. /// Note that these transitions can be overriden by other systems: /// only the actual value of this resource at the time of [`apply_state_transition`] matters. #[derive(Resource, Default)] pub struct NextState(pub Option); impl NextState { /// Tentatively set a planned state transition to `Some(state)`. pub fn set(&mut self, state: S) { self.0 = Some(state); } } /// Run the enter schedule for the current state pub fn run_enter_schedule(world: &mut World) { world.run_schedule(OnEnter(world.resource::>().0.clone())); } /// If a new state is queued in [`NextState`], this system: /// - Takes the new state value from [`NextState`] and updates [`State`]. /// - Runs the [`OnExit(exited_state)`] schedule. /// - Runs the [`OnEnter(entered_state)`] schedule. pub fn apply_state_transition(world: &mut World) { if world.resource::>().0.is_some() { let entered_state = world.resource_mut::>().0.take().unwrap(); let exited_state = mem::replace( &mut world.resource_mut::>().0, entered_state.clone(), ); world.run_schedule(OnExit(exited_state)); world.run_schedule(OnEnter(entered_state)); } }