bevyengine / bevy

A refreshingly simple data-driven game engine built in Rust
https://bevyengine.org
Apache License 2.0
36.1k stars 3.56k forks source link

Remove `StateTransition` schedule in favor of using observers #15133

Open alice-i-cecile opened 1 month ago

alice-i-cecile commented 1 month ago

What problem does this solve or what need does it fill?

Updating the app state is a very flexible and expressive operation, which is fundamentally deferred. States typically changes only once per frame, after PostUpdate.

This can lead to unwanted delays when handling multiple dependent transitions at once.

What solution would you like?

As discussed in #15127, it would be nice to remove this meta-schedule, and instead run the various OnEnter / OnExit / OnTransition schedules on demand, leveraging the new observers. This is a good fit for "highly complex rarely exercised logic", and resolves the delay problem mentioned above while simplifying the mental model for consumers.

15127 should be tackled in concert with this, removing NextState completely, and relying entirely on commands to handle transitions.

What alternative(s) have you considered?

Users can manually add more state transition systems to their schedule, including in their own schedules that run inside of Main. This is somewhat messy, nonstandard and hard to discover.

Additional context

Discussed briefly on Discord with @MiniaczQ here.

This design was not originally taken (including in the substates refactor) because observers didn't exist at the time!

benfrankel commented 1 month ago

If this is meant to replace the fixed once-per-frame state transitions schedule instead of providing an additional option, I'm strongly opposed. If not, I'd like to see an example use case that isn't already covered by world.run_schedule or a custom command that calls that (why don't we have a commands.run_schedule?).

alice-i-cecile commented 1 month ago

Can you explain why you prefer a fixed once-per-frame state transition?

And yeah, we can just add commands.run_schedule.

MiniaczQ commented 1 month ago

I'm actually not super sold on the importance of "run state transition at any point in time", I think we should have it as a niche feature and keep synchronized transitions as the main feature.

The biggest use for state transitions is spawning in entities, which may be processed in multiple schedules. If entities are spawned at arbitrary times within the schedule, it's hard to guarantee all of them are processed correctly every time.

benfrankel commented 1 month ago

for example if you have a Level(usize) state, and you generate / load a new level on enter you don't want arbitrarily many Level transitions in the same frame, at arbitrary points in the schedule probably there may be some use cases where you do want that that i'm not aware of

From Pyrious on Discord

or if you're in the middle of the frame and trigger a state change, then the last half of the frame you're running systems in the new state which likely isn't intended

From doot on Discord

benfrankel commented 1 month ago

I'd like to see an example use case that isn't already covered by world.run_schedule or a custom command that calls that (why don't we have a commands.run_schedule?).

Actually, world.run_schedule alone requires you to run the transition hooks (systems) for all state types via the StateTransitions schedule. You don't get the flexibility of only running the transition hooks for one state type and its descendant (computed / sub) states.

There is actually a proposal in the hackmd called "Flush hooks as observers" where the StateTransitions schedule remains, but instead of running a bunch of regular systems with ordering, it triggers an event for each state type, and observers will respond to the event by running state transition hooks. This enables triggering the event for a particular state type outside of the StateTransitions schedule, wherever you want, while keeping the once-per-frame flush point intact.

But again, whether this proposal should be implemented depends on the existence of an example use case, which I don't have but I also haven't thought much about.