bevy/crates/bevy_state/src/state/computed_states.rs
Lee-Orr 42ba9dfaea
Separate state crate (#13216)
# Objective

Extracts the state mechanisms into a new crate called "bevy_state".

This comes with a few goals:

- state wasn't really an inherent machinery of the ecs system, and so
keeping it within bevy_ecs felt forced
- by mixing it in with bevy_ecs, the maintainability of our more robust
state system was significantly compromised

moving state into a new crate makes it easier to encapsulate as it's own
feature, and easier to read and understand since it's no longer a
single, massive file.

## Solution

move the state-related elements from bevy_ecs to a new crate

## Testing

- Did you test these changes? If so, how? all the automated tests
migrated and passed, ran the pre-existing examples without changes to
validate.

---

## Migration Guide

Since bevy_state is now gated behind the `bevy_state` feature, projects
that use state but don't use the `default-features` will need to add
that feature flag.

Since it is no longer part of bevy_ecs, projects that use bevy_ecs
directly will need to manually pull in `bevy_state`, trigger the
StateTransition schedule, and handle any of the elements that bevy_app
currently sets up.

---------

Co-authored-by: Kristoffer Søholm <k.soeholm@gmail.com>
2024-05-09 18:06:05 +00:00

98 lines
3.5 KiB
Rust

use std::fmt::Debug;
use std::hash::Hash;
use bevy_ecs::schedule::Schedule;
use super::state_set::StateSet;
use super::states::States;
/// A state whose value is automatically computed based on the values of other [`States`].
///
/// A **computed state** is a state that is deterministically derived from a set of `SourceStates`.
/// The [`StateSet`] is passed into the `compute` method whenever one of them changes, and the
/// result becomes the state's value.
///
/// ```
/// # use bevy_state::prelude::*;
/// # use bevy_ecs::prelude::*;
///
/// /// Computed States require some state to derive from
/// #[derive(States, Clone, PartialEq, Eq, Hash, Debug, Default)]
/// enum AppState {
/// #[default]
/// Menu,
/// InGame { paused: bool }
/// }
///
///
/// #[derive(Clone, PartialEq, Eq, Hash, Debug)]
/// struct InGame;
///
/// impl ComputedStates for InGame {
/// /// We set the source state to be the state, or a tuple of states,
/// /// we want to depend on. You can also wrap each state in an Option,
/// /// if you want the computed state to execute even if the state doesn't
/// /// currently exist in the world.
/// type SourceStates = AppState;
///
/// /// We then define the compute function, which takes in
/// /// your SourceStates
/// fn compute(sources: AppState) -> Option<Self> {
/// match sources {
/// /// When we are in game, we want to return the InGame state
/// AppState::InGame { .. } => Some(InGame),
/// /// Otherwise, we don't want the `State<InGame>` resource to exist,
/// /// so we return None.
/// _ => None
/// }
/// }
/// }
/// ```
///
/// you can then add it to an App, and from there you use the state as normal
///
/// ```
/// # use bevy_state::prelude::*;
/// # use bevy_ecs::prelude::*;
///
/// # struct App;
/// # impl App {
/// # fn new() -> Self { App }
/// # fn init_state<S>(&mut self) -> &mut Self {self}
/// # fn add_computed_state<S>(&mut self) -> &mut Self {self}
/// # }
/// # struct AppState;
/// # struct InGame;
///
/// App::new()
/// .init_state::<AppState>()
/// .add_computed_state::<InGame>();
/// ```
pub trait ComputedStates: 'static + Send + Sync + Clone + PartialEq + Eq + Hash + Debug {
/// The set of states from which the [`Self`] is derived.
///
/// This can either be a single type that implements [`States`], an Option of a type
/// that implements [`States`], or a tuple
/// containing multiple types that implement [`States`] or Optional versions of them.
///
/// For example, `(MapState, EnemyState)` is valid, as is `(MapState, Option<EnemyState>)`
type SourceStates: StateSet;
/// Computes the next value of [`State<Self>`](crate::state::State).
/// This function gets called whenever one of the [`SourceStates`](Self::SourceStates) changes.
///
/// If the result is [`None`], the [`State<Self>`](crate::state::State) resource will be removed from the world.
fn compute(sources: Self::SourceStates) -> Option<Self>;
/// This function sets up systems that compute the state whenever one of the [`SourceStates`](Self::SourceStates)
/// change. It is called by `App::add_computed_state`, but can be called manually if `App` is not
/// used.
fn register_computed_state_systems(schedule: &mut Schedule) {
Self::SourceStates::register_computed_state_systems_in_schedule::<Self>(schedule);
}
}
impl<S: ComputedStates> States for S {
const DEPENDENCY_DEPTH: usize = S::SourceStates::SET_DEPENDENCY_DEPTH + 1;
}