pub trait SubStates: States + FreelyMutableState {
type SourceStates: StateSet;
// Required method
fn should_exist(sources: Self::SourceStates) -> Option<Self>;
// Provided method
fn register_sub_state_systems(schedule: &mut Schedule) { ... }
}
Expand description
A sub-state is a state that exists only when the source state meet certain conditions,
but unlike ComputedStates
- while they exist they can be manually modified.
The default approach to creating SubStates
is using the derive macro, and defining a single source state
and value to determine it’s existence.
#[derive(States, Clone, PartialEq, Eq, Hash, Debug, Default)]
enum AppState {
#[default]
Menu,
InGame
}
#[derive(SubStates, Clone, PartialEq, Eq, Hash, Debug, Default)]
#[source(AppState = AppState::InGame)]
enum GamePhase {
#[default]
Setup,
Battle,
Conclusion
}
you can then add it to an App, and from there you use the state as normal:
App::new()
.init_state::<AppState>()
.add_sub_state::<GamePhase>();
In more complex situations, the recommendation is to use an intermediary computed state, like so:
/// 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 set of states,
/// we want to depend on. Any of the states can be wrapped in an Option.
type SourceStates = Option<AppState>;
/// We then define the compute function, which takes in the AppState
fn compute(sources: Option<AppState>) -> Option<Self> {
match sources {
/// When we are in game, we want to return the InGame state
Some(AppState::InGame { .. }) => Some(InGame),
/// Otherwise, we don't want the `State<InGame>` resource to exist,
/// so we return None.
_ => None
}
}
}
#[derive(SubStates, Clone, PartialEq, Eq, Hash, Debug, Default)]
#[source(InGame = InGame)]
enum GamePhase {
#[default]
Setup,
Battle,
Conclusion
}
However, you can also manually implement them. If you do so, you’ll also need to manually implement the States
& FreelyMutableState
traits.
Unlike the derive, this does not require an implementation of Default
, since you are providing the exists
function
directly.
/// 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)]
enum GamePhase {
Setup,
Battle,
Conclusion
}
impl SubStates for GamePhase {
/// We set the source state to be the state, or set of states,
/// we want to depend on. Any of the states can be wrapped in an Option.
type SourceStates = Option<AppState>;
/// We then define the compute function, which takes in the [`Self::SourceStates`]
fn should_exist(sources: Option<AppState>) -> Option<Self> {
match sources {
/// When we are in game, so we want a GamePhase state to exist, and the default is
/// GamePhase::Setup
Some(AppState::InGame { .. }) => Some(GamePhase::Setup),
/// Otherwise, we don't want the `State<GamePhase>` resource to exist,
/// so we return None.
_ => None
}
}
}
impl States for GamePhase {
const DEPENDENCY_DEPTH : usize = <GamePhase as SubStates>::SourceStates::SET_DEPENDENCY_DEPTH + 1;
}
impl FreelyMutableState for GamePhase {}
Required Associated Types§
Required Methods§
fn should_exist(sources: Self::SourceStates) -> Option<Self>
fn should_exist(sources: Self::SourceStates) -> Option<Self>
This function gets called whenever one of the SourceStates
changes.
The result is used to determine the existence of State<Self>
.
If the result is None
, the State<Self>
resource will be removed from the world, otherwise
if the State<Self>
resource doesn’t exist - it will be created with the Some
value.
Provided Methods§
fn register_sub_state_systems(schedule: &mut Schedule)
fn register_sub_state_systems(schedule: &mut Schedule)
This function sets up systems that compute the state whenever one of the SourceStates
change. It is called by App::add_computed_state
, but can be called manually if App
is not
used.