-
-
Notifications
You must be signed in to change notification settings - Fork 3.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Robust State Matching (formerly Sub state support) #9957
Conversation
Signed-off-by: lee-orr <lee-orr@users.noreply.github.com>
Signed-off-by: lee-orr <lee-orr@users.noreply.github.com>
Signed-off-by: lee-orr <lee-orr@users.noreply.github.com>
Signed-off-by: lee-orr <lee-orr@users.noreply.github.com>
Signed-off-by: lee-orr <lee-orr@users.noreply.github.com>
I love this. I was looking these days to implenent this pattern for my game because I need different behaviour in my plugins, like, when player is alive I run some systems, when player is almost dead I run another system. And I have some other fields.. and it gets pretty ugly to work with. I think this PR improves ergonomics and reduces spaghetti a lot if you can do this:
It will also be nice if you can have other comparisions than ==, on fields, such as <, >, <=, >= and != but I think this implies some conditional properties. |
I wouldn't necessarily recommend using this for that purpose, I think it's probably better to have a custom conditional system that could use your "health" resource directly. However, you can still do so using by implementing a custom For example: #[derive(Debug, Eq, PartialEq, Hash, Clone)]
struct LowHealth;
impl StateMatcher<GameState> for LowHealth{
fn match_state(&self, state: &GameState ) -> bool {
match state {
GameState ::InGame { lives, ..} if lives < 2 => true,
_ => false
}
}
} |
…ey treat the State resource as optional, and fail gracefully if it does not exist. Signed-off-by: lee-orr <lee-orr@users.noreply.github.com>
Signed-off-by: lee-orr <lee-orr@users.noreply.github.com>
Signed-off-by: lee-orr <lee-orr@users.noreply.github.com>
I undrerstand. But can you actually check only for the in game state? For example you have a system that might run in both cases, when paused and when not paused. |
First, if you're defining a custom matcher, it's just a function that takes a reference to the state and returns a boolean, so you can do whatever you want! In addition, there are a few macros you can use if what you need is just a simple pattern match, for example: .add_systems(
Update,
change_color.run_if(in_state!(AppState, AppState::InGame { .. })),
) for an ad-hoc solution. And if you want something re-usable, you can use the state_matcher!(InGame, AppState, AppState::InGame { .. }); |
Signed-off-by: lee-orr <lee-orr@users.noreply.github.com>
Signed-off-by: lee-orr <lee-orr@users.noreply.github.com>
Signed-off-by: lee-orr <lee-orr@users.noreply.github.com>
Signed-off-by: lee-orr <lee-orr@users.noreply.github.com>
Signed-off-by: lee-orr <lee-orr@users.noreply.github.com>
Signed-off-by: lee-orr <lee-orr@users.noreply.github.com>
…into sub-state-support
Adjusted the PR description, cleaned some stuff up for clippy and the like, added important portions to the prelude, and added Conditional States, which cover the original use case of de-coupled states that can only exist when another state is in an appropriate state. |
Signed-off-by: lee-orr <lee-orr@users.noreply.github.com>
Signed-off-by: lee-orr <lee-orr@users.noreply.github.com>
Signed-off-by: lee-orr <lee-orr@users.noreply.github.com>
Signed-off-by: lee-orr <lee-orr@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright, I finally have the energy for a proper design review.
Things I like:
- awesome examples
- extremely helpful PR description
- use of pattern matching to capture more complex patterns in states. There's complexity here, but it's familiar and well-justified
- end user API is genuinely quite nice at this point
Blocking concerns:
- I really dislike how ConditionalScheduleLabel blurs boundaries and adds a lot of complexity to the ECS internals
- StateMatchers are a lot of internal complexity, and I have trouble seeing how manual implementations would be useful enough to end users
Unless we can substantially reduce implementation complexity, I'm opposed to adding this feature to Bevy itself. Definitely useful to some users, but the complexity isn't worth it. If there are simple changes you need to make to bevy_ecs to support this as an external crate, I'm happy to support that effort.
@alice-i-cecile - I wanted to note a couple of things:
I really feel that this is needed in order for the current not-just-flat-enums version of states to be useful, so I think it does need to be part of bevy. (Or, states as a whole shouldn't be a first-party part of bevy, or maybe should exist as a separate crate) I am however uncertain which aspects to simplify - can you provide some more clarity on that? |
Okay, great! I was mostly going off the updated PR description.
Can we make this private for now, and remove the corresponding example? On the implementation side I don't have strong concerns, but the user-facing complexity of it is really high.
Yeah, that's pretty reasonable: I'd really like to be able to get complexity down and ship this. I definitely think we want / need states in Bevy (they're too useful), but I'm mildly in favor of it living outside of |
I will get make the trait un-implementable. Unless there is significant objection, I would like to modify the example to showcase a "black-box" state, replacing the manual implementations there with public functions. Is that something you think is an acceptable compromise there? |
Yep, that sounds like a good way forward. Thanks again for being so receptive to the feedback <3 |
- remove the derive option - add a sealed trait and better markers to prevent manually implementing state matchers - replace the `custom state matchers` example with a `black box states` example Signed-off-by: lee-orr <lee-orr@users.noreply.github.com>
Signed-off-by: lee-orr <lee-orr@users.noreply.github.com>
Signed-off-by: lee-orr <lee-orr@users.noreply.github.com>
Signed-off-by: lee-orr <lee-orr@users.noreply.github.com>
while keeping `StateMatcher`'s internal implementation hidden. - Renamed `StateMatcher` to `InternalStateMatcher` - kept as a `pub(crate)` - Added a new `StateMatcher` trait that is public, and both requires and auto implemented for `InternalStateMatcher`'s - Replace all public uses of `InternalStateMatcher` with `StateMatcher` Signed-off-by: lee-orr <lee-orr@users.noreply.github.com>
Signed-off-by: lee-orr <lee-orr@users.noreply.github.com>
Signed-off-by: lee-orr <lee-orr@users.noreply.github.com>
Closing this in favour of #10088 |
## Summary/Description This PR extends states to allow support for a wider variety of state types and patterns, by providing 3 distinct types of state: - Standard [`States`] can only be changed by manually setting the [`NextState<S>`] resource. These states are the baseline on which the other state types are built, and can be used on their own for many simple patterns. See the [state example](https://github.com/bevyengine/bevy/blob/latest/examples/ecs/state.rs) for a simple use case - these are the states that existed so far in Bevy. - [`SubStates`] are children of other states - they can be changed manually using [`NextState<S>`], but are removed from the [`World`] if the source states aren't in the right state. See the [sub_states example](https://github.com/lee-orr/bevy/blob/derived_state/examples/ecs/sub_states.rs) for a simple use case based on the derive macro, or read the trait docs for more complex scenarios. - [`ComputedStates`] are fully derived from other states - they provide a [`compute`](ComputedStates::compute) method that takes in the source states and returns their derived value. They are particularly useful for situations where a simplified view of the source states is necessary - such as having an `InAMenu` computed state derived from a source state that defines multiple distinct menus. See the [computed state example](https://github.com/lee-orr/bevy/blob/derived_state/examples/ecs/computed_states.rscomputed_states.rs) to see a sampling of uses for these states. # Objective This PR is another attempt at allowing Bevy to better handle complex state objects in a manner that doesn't rely on strict equality. While my previous attempts (#10088 and #9957) relied on complex matching capacities at the point of adding a system to application, this one instead relies on deterministically deriving simple states from more complex ones. As a result, it does not require any special macros, nor does it change any other interactions with the state system once you define and add your derived state. It also maintains a degree of distinction between `State` and just normal application state - your derivations have to end up being discreet pre-determined values, meaning there is less of a risk/temptation to place a significant amount of logic and data within a given state. ### Addition - Sub States closes #9942 After some conversation with Maintainers & SMEs, a significant concern was that people might attempt to use this feature as if it were sub-states, and find themselves unable to use it appropriately. Since `ComputedState` is mainly a state matching feature, while `SubStates` are more of a state mutation related feature - but one that is easy to add with the help of the machinery introduced by `ComputedState`, it was added here as well. The relevant discussion is here: https://discord.com/channels/691052431525675048/1200556329803186316 ## Solution closes #11358 The solution is to create a new type of state - one implementing `ComputedStates` - which is deterministically tied to one or more other states. Implementors write a function to transform the source states into the computed state, and it gets triggered whenever one of the source states changes. In addition, we added the `FreelyMutableState` trait , which is implemented as part of the derive macro for `States`. This allows us to limit use of `NextState<S>` to states that are actually mutable, preventing mis-use of `ComputedStates`. --- ## Changelog - Added `ComputedStates` trait - Added `FreelyMutableState` trait - Converted `NextState` resource to an Enum, with `Unchanged` and `Pending` - Added `App::add_computed_state::<S: ComputedStates>()`, to allow for easily adding derived states to an App. - Moved the `StateTransition` schedule label from `bevy_app` to `bevy_ecs` - but maintained the export in `bevy_app` for continuity. - Modified the process for updating states. Instead of just having an `apply_state_transition` system that can be added anywhere, we now have a multi-stage process that has to run within the `StateTransition` label. First, all the state changes are calculated - manual transitions rely on `apply_state_transition`, while computed transitions run their computation process before both call `internal_apply_state_transition` to apply the transition, send out the transition event, trigger dependent states, and record which exit/transition/enter schedules need to occur. Once all the states have been updated, the transition schedules are called - first the exit schedules, then transition schedules and finally enter schedules. - Added `SubStates` trait - Adjusted `apply_state_transition` to be a no-op if the `State<S>` resource doesn't exist ## Migration Guide If the user accessed the NextState resource's value directly or created them from scratch they will need to adjust to use the new enum variants: - if they created a `NextState(Some(S))` - they should now use `NextState::Pending(S)` - if they created a `NextState(None)` -they should now use `NextState::Unchanged` - if they matched on the `NextState` value, they would need to make the adjustments above If the user manually utilized `apply_state_transition`, they should instead use systems that trigger the `StateTransition` schedule. --- ## Future Work There is still some future potential work in the area, but I wanted to keep these potential features and changes separate to keep the scope here contained, and keep the core of it easy to understand and use. However, I do want to note some of these things, both as inspiration to others and an illustration of what this PR could unlock. - `NextState::Remove` - Now that the `State` related mechanisms all utilize options (#11417), it's fairly easy to add support for explicit state removal. And while `ComputedStates` can add and remove themselves, right now `FreelyMutableState`s can't be removed from within the state system. While it existed originally in this PR, it is a different question with a separate scope and usability concerns - so having it as it's own future PR seems like the best approach. This feature currently lives in a separate branch in my fork, and the differences between it and this PR can be seen here: lee-orr#5 - `NextState::ReEnter` - this would allow you to trigger exit & entry systems for the current state type. We can potentially also add a `NextState::ReEnterRecirsive` to also re-trigger any states that depend on the current one. - More mechanisms for `State` updates - This PR would finally make states that aren't a set of exclusive Enums useful, and with that comes the question of setting state more effectively. Right now, to update a state you either need to fully create the new state, or include the `Res<Option<State<S>>>` resource in your system, clone the state, mutate it, and then use `NextState.set(my_mutated_state)` to make it the pending next state. There are a few other potential methods that could be implemented in future PRs: - Inverse Compute States - these would essentially be compute states that have an additional (manually defined) function that can be used to nudge the source states so that they result in the computed states having a given value. For example, you could use set the `IsPaused` state, and it would attempt to pause or unpause the game by modifying the `AppState` as needed. - Closure-based state modification - this would involve adding a `NextState.modify(f: impl Fn(Option<S> -> Option<S>)` method, and then you can pass in closures or function pointers to adjust the state as needed. - Message-based state modification - this would involve either creating states that can respond to specific messages, similar to Elm or Redux. These could either use the `NextState` mechanism or the Event mechanism. - ~`SubStates` - which are essentially a hybrid of computed and manual states. In the simplest (and most likely) version, they would work by having a computed element that determines whether the state should exist, and if it should has the capacity to add a new version in, but then any changes to it's content would be freely mutated.~ this feature is now part of this PR. See above. - Lastly, since states are getting more complex there might be value in moving them out of `bevy_ecs` and into their own crate, or at least out of the `schedule` module into a `states` module. #11087 As mentioned, all these future work elements are TBD and are explicitly not part of this PR - I just wanted to provide them as potential explorations for the future. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Marcel Champagne <voiceofmarcel@gmail.com> Co-authored-by: MiniaczQ <xnetroidpl@gmail.com>
Note - this PR has changed a lot since it's original inception, so I felt it's worth re-writing the description to make sure it is accurate. For the sake of keeping a good record, you can find the original description further down.
I'm also adding some additional notes with a date below.
Objective
The main purpose of this PR is to enable versatile state matching. The current implementation of States relies solely on equality when determining whether a given system or schedule should run:
This can lead to a variety of issues when the structure of the state object changes during development, or if you don't accurately foresee the way the states could change.
In addition, it is much harder to handle state changes that should only occur when entering/leaving a collection of states, but not when moving between them - like so:
This PR provides a set of tools for matching states in a more robust, reliable, and ergonomic way.
Solution
There are a few layers to the solution, and originally I went from "lowest level" to "highest level" - but I feel that added to some confusion on the reasoning/purpose behind each layer of the solution. So here, I will be going from the "highest level" stuff, which is most likely to be used directly by end users, to the "lowest level" stuff that serves as infrastructure for the higher level elements.
State Matching Macros
Notes (9 Oct): updated below:
The solution adds the following macros:
entering!
,exiting!
,state_matches!
andtransitioning!
that allow you to use pattern matching syntax to determine whether the current (and/or previous) state should cause the systems to run.So, for the examples above we would be able to use:
note the
every
keyword in theexiting!
macro - this means that it will run every time you transition into one of the states regardless of the previous state. By contrast, theentering
macro will only run when we enter a matching state from a non-matching state because we aren't using theevery
keyword.Also added are run conditions with the same names:
entering
,exiting
,state_matches
,transitioning
- these can be passed anyStateMatcher
s - which are now eitherStates
or one of theFn
variants with auto implementations.Previous Version
The solution adds the following macros:
on_enter!
,on_exit!
,state_matches!
andon_transition!
that allow you to use pattern matching syntax to determine whether the current (and/or previous) state should cause the systems to run.So, for the examples above we would be able to use:
note the
every
keyword in theon_exit!
macro - this means that it will run every time you transition into one of the states regardless of the previous state. By contrast, theon_enter
macro will only run when we enter a matching state from a non-matching state because we aren't using theevery
keyword.State Matchers
The state matching macros utilize
StateMatcher
s under the hood. This is really the core of the solution space here - separating the data (implementors ofStates
) and the mechanism for determining whether we should run in a given state (implementors ofStateMatcher<S: States>
).State matchers provide 2 methods:
The
MatchesStateTransition
Enum represents the different options for handling transitions:TransitionMatches
- when the whole transition matches,MainMatches
- when the main matches, but the transition as a whole is invalid,NoMatch
- when neither the main nor the transition match. This is used to enableevery
and normal options in the macros, as well as to allow for custom logic if you so choose.The
main
parameter is always the primary state instance we care about - if looking aton_exit
or thefrom
inon_transition
, this would be the previous state, while if looking aton_enter
or theto
inon_transition
, this would be the next state. If we are not in a transition,main
will be set butsecondary
will be None.In normal use, you would probably rely on either the macros above (which generate state matchers), on on one of the default implementations - such as
States<S>
which relies onEq
,Fn(&S) -> bool
,Fn(&S, Option<&S>) -> bool
orFn(Option<&S>, Option<&S>) -> MatchesStateTransition
.Note (9 Oct) - the derive option will be removed and the trait will be made un-implementable from a public standpoint. Only the auto-implemented versions will be available.
~~However, you can also use a derive macro to implement matchers from Unit structs:
or field-less Enums:
And of course, you can also manually implement it if you so choose.~~
Underlying Changes to State
Res<State<S>>
andRes<NextState<S>>
withinbevy_ecs
rely on optional versions of the resources if possibleNextState<S>
is no longer a struct, but rather an enum with 3 options:Keep
,Value(S)
,Setter(Box<dyn Fn(S) -> S>)
. This is the only breaking change. The reasoning is: the previous use ofNextState(Option<S>)
created a confusion withNone
, where if one was not aware that states couldn't be removed they might assume setting the value to None removes the state. In addition, given that with this change states can be removed, that confusion would only get worse. In addition, with more complexStates
that aren't just a single, flat enum you might want to be able to "patch" a state rather than fully replace it. TheSetter
option allows you to provide a callback for patching the state, as it is at the time whenapply_state_transition
is called. By replacing theOption<S>
, we opened up to a few additional use cases.- which will run systems using Matcher based states.OnStateEntry
,OnStateTransition
andOnStateExit
Note (9 Oct) - The new scahedule labels were replaced with non-generic
Entering
,Exiting
andTransitioning
schedule labelsUnderlying Changes to Conditions and AppNote (9 Oct) - this has been fully removed
Added aConditionalScheduleLabel
trait, with auto implementations forS: ScheduleLabel
and(ScheduleLabel, Condition)
- that can apply the condition to a givenIntoSystemConfig
/IntoSystemSetConfig
, and return a(ScheduleLabel, SystemConfig)
or(ScheduleLabel, SystemSetconfig)
. The macros generate(ScheduleLabel, Condition)
tuple. This is necessary becauseSystemLabel
s don't expose any information on their sub-type from within theSchedules
resource, so they can only be triggered or recognized via strict equality. As a result, we use specialOnStateEntry<S>
/OnStateTransition<S>
/OnStateExit<S>
schedules to house all the relevant systems - and rely on run conditions to filter them with a given matcher.ReplaceScheduleLabel
withConditionalScheduleLabel
in.add_systems
,.configure_set
and.configure_sets
.Notes on the macro syntax
You'll notice that the macro syntax is similar, but not identical, to the
matches!
syntax. This originates from the need to be able to determine the state typeS
for our matchers & schedule labels (see below). As work on the PR continued, this also evolved into the marco's supporting a more robust syntax to resolve issues that came up.I wanted to provide a full accounting of the macros, their syntax, and the reasoning behind them - but didn't want to place that within the earlier segments since I didn't want the more significant concepts to be lost to the details here.
This PR provides 5 macros:
on_enter!
,on_exit!
andon_transition!
are used to trigger when entering/exiting matching statesstate_matches!
is used to create a run condition that is true when we are in a matching statederive(StateMatcher)
is used to derive theStateMatcher<S: States>
trait, with the help of some associated attributes.A quick overview of the structures for the macros:
on_enter!
,on_exit!
andstate_matches!
have one of two structuresmacro!(StateMatcher)
ormacro!(StateTypeIdent, MatchPattern, ...)
on_transition!
is structured like so:on_transition!(StateTypeIdent, {MatchPattern, ...}, {MatchPattern, ...})
with the first set ofMatchPattern
s matching thefrom
state, and the second set ofMatchPattern
s matching theto
statederive(StateMatcher)
uses a#[state_type(StateTypeIdent)]
attribute to define the type, and then one (or more for enums)#[matcher(MatchPattern, ...)]
attributes to determine the actual matching processStateMatcher
This section is only available for the 3 simplest macros -
on_enter!
,on_exit!
, andstate_matches!
- and simply allows you to pass in an existingStateMatcher<S>
. This is there to allow you to easily re-use existing state matchers in context. Note that since allStates
implementStateMatcher<Self>
, you can pass in a state value as well. Just remember that when scheduling - it will use matching, rather than theOnEnter(S)
/OnExit(S)
schedule labels.StateTypeIdent
This is the name/path to the state type. This is required for more complex patterns, since we can't count on type inference at that point.
MatchPattern
A
MatchPattern
starts with an optionalevery
keyword - which ensure the match will be true whenever themain
state matches, regardless of thesecondary
state(see the StateMatcher explanation for more onmain
andsecondary
states in a state matcher).After that, you can either provide a
Pattern
or aClosure
. The Pattern works like a normal pattern match, but you can exclude the root type name if you want (for example,InGame(_)
instead ofAppState::InGame(_)
). The Closure should follow one of the default implementations ofStateMatcher
-Fn(&S) -> bool
,Fn(&S, Option<&S>) -> bool
,Fn(Option<&S>, Option<&S>) -> bool
,Fn(&S, Option<&S>) -> MatchesStateTransition
orFn(Option<&S>, Option<&S>) -> MatchesStateTransition
.Note that you can have more than one MatchPattern in a macro, in a comma-separated list. If you do, they will be evaluated in order, and for transitions
MatchesStateTransition::MainMatches
will return immediately, onlyMatchesStateTransition::NoMatch
will continue to evaluate the next pattern. This is needed because, at times, a simpleevery
may not be enough.For example, I might have a system for clearing the UI, and I want it to run whenever I move between distinct UI's. However, while I'm in game the UI remains consistent, even if the in game state changes - so what I want is:
This will first check if I'm leaving
InGame(_)
, and if so if I'm also moving toInGame(_)
it will return false. However, otherwise - it will see that it should matchevery _
- meaning transitioning to and from any other state should return true and run theclear_ui
system.Changelog
At this point, due to the large amount of variation over the history of this PR, I believe it's better to rely on the summary above and the diff.
Migration Guide
The only breaking change is to
NextState<S>
. If it is being used with the.set(S)
api, it won't break. However, if you are manually constructingNextState
objects and inserting them viacommands
, you will need to change the way you construct them fromNextState(Some(S::Variant))
toNextState::StateValue(S::Variant)
.The Original Description:
Objective
This PR is meant to address #9942 - adding sub-state support. As discussions on the topic evolved in the discord chat, the PR became a more versatile state matching and handling system that is still mostly compatible with the existing version.
Solution
The solution involves multiple elements:
Inheriting the ability to use non-enums/more complex enums as States from: Remove States::variants and remove enum-only restriction its derive #9945
Adding an
IntoConditionalScheduleLabel
, and utilizing it inadd_systems
,configure_set
andconfigure_sets
instead ofScheduleLabel
.IntoConditionalScheduleLabel
exposes a function that emits aScheduleLabel
and an optionalBoxedCondition
, and applies the condition to the systems/sets if it is provided. This opens up the avenue for creating the ergonomics of aScheduleLabel
with a conditional, if you don't want the overhead of an exclusive Schedule run for your situation. AllScheduleLabel
's automatically implementIntoConditionalScheduleLabel
, so it should not break existing code.Separating the
States
andStateMatcher
- the State is the type representing the actual state, while the StateMatcher basically replaces strict equality for determining whether we are currently in/entering/exiting a state we want to act on. AllStates
implementStateMatcher
with strict equality, so existing code will not break.Adding schedules for
OnStateEntry
,OnStateTransition
andOnStateExit
- which will run systems using Matcher based states.Adding macros for ease of use when pattern matching -
on_enter
,on_exit
, a strict variant of each,in_state
andstate_matcher
Making all the systems relying on
Res<State<S>>
andRes<NextState<S>>
withinbevy_ecs
rely on optional versions of the resources if possible, to allow for states that aren't always present.added
add_conditional_state
andadd_strict_conditional_state
toApp
, allowing for creating states that only exist while a parent state is in a matching configuration.NextState<S>
is no longer a struct, but rather an enum with 3 options:MaintainCurrent
,StateValue
,StateSetter
. This is the only breaking change. The reasoning is: the previous use ofNextState(Option<S>)
created a confusion withNone
, where if one was not aware that states couldn't be removed they might assume setting the value to None removes the state. In addition, given that with this change states can be removed, that confusion would only get worse. In addition, with more complexStates
that aren't just a single, flat enum you might want to be able to "patch" a state rather than fully replace it. TheStateSetter
option allows you to provide a callback for patching the state, as it is at the time whenapply_state_transition
is called. By replacing theOption<S>
, we opened up to a few additional use cases.For a good example of how it all works, I'd recommend looking at the
examples/ecs/state.rs
file, since it has been updated to showcase most of these options.Changelog
Changes are limited to the
states.rs
file in reach, but varied - I believe the summary in the solution section is the best way to process it.Migration Guide
The only breaking change is to
NextState<S>
. If it is being used with the.set(S)
api, it won't break. However, if you are manually constructingNextState
objects and inserting them viacommands
, you will need to change the way you construct them fromNextState(Some(S::Variant))
toNextState::StateValue(S::Variant)
.