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
|
|
|
use crate::state::{State, States};
|
2024-06-16 16:06:45 +00:00
|
|
|
use bevy_ecs::{change_detection::DetectChanges, system::Res};
|
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
|
|
|
|
|
|
|
/// A [`Condition`](bevy_ecs::prelude::Condition)-satisfying system that returns `true`
|
|
|
|
/// if the state machine exists.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// # use bevy_ecs::prelude::*;
|
|
|
|
/// # use bevy_state::prelude::*;
|
|
|
|
/// # #[derive(Resource, Default)]
|
|
|
|
/// # struct Counter(u8);
|
|
|
|
/// # let mut app = Schedule::default();
|
|
|
|
/// # let mut world = World::new();
|
|
|
|
/// # world.init_resource::<Counter>();
|
|
|
|
/// #[derive(States, Clone, Copy, Default, Eq, PartialEq, Hash, Debug)]
|
|
|
|
/// enum GameState {
|
|
|
|
/// #[default]
|
|
|
|
/// Playing,
|
|
|
|
/// Paused,
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// app.add_systems(
|
|
|
|
/// // `state_exists` will only return true if the
|
|
|
|
/// // given state exists
|
|
|
|
/// my_system.run_if(state_exists::<GameState>),
|
|
|
|
/// );
|
|
|
|
///
|
|
|
|
/// fn my_system(mut counter: ResMut<Counter>) {
|
|
|
|
/// counter.0 += 1;
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// // `GameState` does not yet exist `my_system` won't run
|
|
|
|
/// app.run(&mut world);
|
|
|
|
/// assert_eq!(world.resource::<Counter>().0, 0);
|
|
|
|
///
|
|
|
|
/// world.init_resource::<State<GameState>>();
|
|
|
|
///
|
|
|
|
/// // `GameState` now exists so `my_system` will run
|
|
|
|
/// app.run(&mut world);
|
|
|
|
/// assert_eq!(world.resource::<Counter>().0, 1);
|
|
|
|
/// ```
|
|
|
|
pub fn state_exists<S: States>(current_state: Option<Res<State<S>>>) -> bool {
|
|
|
|
current_state.is_some()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Generates a [`Condition`](bevy_ecs::prelude::Condition)-satisfying closure that returns `true`
|
|
|
|
/// if the state machine is currently in `state`.
|
|
|
|
///
|
|
|
|
/// Will return `false` if the state does not exist or if not in `state`.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// # use bevy_ecs::prelude::*;
|
|
|
|
/// # use bevy_state::prelude::*;
|
|
|
|
/// # #[derive(Resource, Default)]
|
|
|
|
/// # struct Counter(u8);
|
|
|
|
/// # let mut app = Schedule::default();
|
|
|
|
/// # let mut world = World::new();
|
|
|
|
/// # world.init_resource::<Counter>();
|
|
|
|
/// #[derive(States, Clone, Copy, Default, Eq, PartialEq, Hash, Debug)]
|
|
|
|
/// enum GameState {
|
|
|
|
/// #[default]
|
|
|
|
/// Playing,
|
|
|
|
/// Paused,
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// world.init_resource::<State<GameState>>();
|
|
|
|
///
|
|
|
|
/// app.add_systems((
|
|
|
|
/// // `in_state` will only return true if the
|
|
|
|
/// // given state equals the given value
|
|
|
|
/// play_system.run_if(in_state(GameState::Playing)),
|
|
|
|
/// pause_system.run_if(in_state(GameState::Paused)),
|
|
|
|
/// ));
|
|
|
|
///
|
|
|
|
/// fn play_system(mut counter: ResMut<Counter>) {
|
|
|
|
/// counter.0 += 1;
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// fn pause_system(mut counter: ResMut<Counter>) {
|
|
|
|
/// counter.0 -= 1;
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// // We default to `GameState::Playing` so `play_system` runs
|
|
|
|
/// app.run(&mut world);
|
|
|
|
/// assert_eq!(world.resource::<Counter>().0, 1);
|
|
|
|
///
|
|
|
|
/// *world.resource_mut::<State<GameState>>() = State::new(GameState::Paused);
|
|
|
|
///
|
|
|
|
/// // Now that we are in `GameState::Pause`, `pause_system` will run
|
|
|
|
/// app.run(&mut world);
|
|
|
|
/// assert_eq!(world.resource::<Counter>().0, 0);
|
|
|
|
/// ```
|
|
|
|
pub fn in_state<S: States>(state: S) -> impl FnMut(Option<Res<State<S>>>) -> bool + Clone {
|
|
|
|
move |current_state: Option<Res<State<S>>>| match current_state {
|
|
|
|
Some(current_state) => *current_state == state,
|
2024-06-16 16:06:45 +00:00
|
|
|
None => false,
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A [`Condition`](bevy_ecs::prelude::Condition)-satisfying system that returns `true`
|
|
|
|
/// if the state machine changed state.
|
|
|
|
///
|
|
|
|
/// To do things on transitions to/from specific states, use their respective OnEnter/OnExit
|
|
|
|
/// schedules. Use this run condition if you want to detect any change, regardless of the value.
|
|
|
|
///
|
|
|
|
/// Returns false if the state does not exist or the state has not changed.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// # use bevy_ecs::prelude::*;
|
|
|
|
/// # use bevy_state::prelude::*;
|
|
|
|
/// # #[derive(Resource, Default)]
|
|
|
|
/// # struct Counter(u8);
|
|
|
|
/// # let mut app = Schedule::default();
|
|
|
|
/// # let mut world = World::new();
|
|
|
|
/// # world.init_resource::<Counter>();
|
|
|
|
/// #[derive(States, Clone, Copy, Default, Eq, PartialEq, Hash, Debug)]
|
|
|
|
/// enum GameState {
|
|
|
|
/// #[default]
|
|
|
|
/// Playing,
|
|
|
|
/// Paused,
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// world.init_resource::<State<GameState>>();
|
|
|
|
///
|
|
|
|
/// app.add_systems(
|
|
|
|
/// // `state_changed` will only return true if the
|
|
|
|
/// // given states value has just been updated or
|
|
|
|
/// // the state has just been added
|
|
|
|
/// my_system.run_if(state_changed::<GameState>),
|
|
|
|
/// );
|
|
|
|
///
|
|
|
|
/// fn my_system(mut counter: ResMut<Counter>) {
|
|
|
|
/// counter.0 += 1;
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// // `GameState` has just been added so `my_system` will run
|
|
|
|
/// app.run(&mut world);
|
|
|
|
/// assert_eq!(world.resource::<Counter>().0, 1);
|
|
|
|
///
|
|
|
|
/// // `GameState` has not been updated so `my_system` will not run
|
|
|
|
/// app.run(&mut world);
|
|
|
|
/// assert_eq!(world.resource::<Counter>().0, 1);
|
|
|
|
///
|
|
|
|
/// *world.resource_mut::<State<GameState>>() = State::new(GameState::Paused);
|
|
|
|
///
|
|
|
|
/// // Now that `GameState` has been updated `my_system` will run
|
|
|
|
/// app.run(&mut world);
|
|
|
|
/// assert_eq!(world.resource::<Counter>().0, 2);
|
|
|
|
/// ```
|
|
|
|
pub fn state_changed<S: States>(current_state: Option<Res<State<S>>>) -> bool {
|
|
|
|
let Some(current_state) = current_state else {
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
current_state.is_changed()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use crate as bevy_state;
|
|
|
|
|
|
|
|
use bevy_ecs::schedule::{Condition, IntoSystemConfigs, Schedule};
|
|
|
|
|
|
|
|
use crate::prelude::*;
|
2024-06-07 12:49:21 +00:00
|
|
|
use bevy_state_macros::States;
|
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
|
|
|
|
|
|
|
#[derive(States, PartialEq, Eq, Debug, Default, Hash, Clone)]
|
|
|
|
enum TestState {
|
|
|
|
#[default]
|
|
|
|
A,
|
|
|
|
B,
|
|
|
|
}
|
|
|
|
|
|
|
|
fn test_system() {}
|
|
|
|
|
|
|
|
// Ensure distributive_run_if compiles with the common conditions.
|
|
|
|
#[test]
|
|
|
|
fn distributive_run_if_compiles() {
|
|
|
|
Schedule::default().add_systems(
|
|
|
|
(test_system, test_system)
|
|
|
|
.distributive_run_if(state_exists::<TestState>)
|
Re-name and Extend Run Conditions API (#13784)
# Objective
- My attempt at fulfilling #13629.
## Solution
Renames the `and_then` / `or_else` run condition methods to `and` /
`or`, respectively.
Extends the run conditions API to include a suite of binary logical
operators:
- `and`
- `or`
- `nand`
- `nor`
- `xor`
- `xnor`
## Testing
- Did you test these changes? If so, how?
- The test **run_condition_combinators** was extended to include the
added run condition combinators. A **double_counter** system was added
to test for combinators running on even count cycles.
- Are there any parts that need more testing?
- I'm not too sure how I feel about the "counter" style of testing but I
wanted to keep it consistent. If it's just a unit test I would prefer
simply to just assert `true` == _combinator output_ or `false` ==
_combinator output_ .
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
- Nothing too specific. The added methods should be equivalent to the
logical operators they are analogous to (`&&` , `||`, `^`, `!`).
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
- Should not be relevant, I'm using Windows.
## Changelog
- What changed as a result of this PR?
- The run conditions API.
- If applicable, organize changes under "Added", "Changed", or "Fixed"
sub-headings
- Changed:
- `and_then` run condition combinator renamed to simply `and`
- `or_else` run condition combinator renamed to simply `or`
- Added:
- `nand` run condition combinator.
- `nor` run condition combinator.
- `xor` run condition combinator.
- `xnor` run condition combinator.
## Migration Guide
- The `and_then` run condition method has been replaced with the `and`
run condition method.
- The `or_else` run condition method has been replaced with the `or` run
condition method.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Andres O. Vela <andresovela@users.noreply.github.com>
2024-06-10 13:41:56 +00:00
|
|
|
.distributive_run_if(in_state(TestState::A).or(in_state(TestState::B)))
|
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
|
|
|
.distributive_run_if(state_changed::<TestState>),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|