-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
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
Run criteria should be composable; states should act more like other run criteria #2233
Comments
If I have In the table I will omit the states where both returned the same thing, as then it's trivial that output should return that. No beats everything: if something says No, there is no reason to ever check again.
|
Combining criteria raised quite a few arguments a while ago... We settled on a minimal version for now, criteria piping. Criteria created by states are fully-interoperable with that, too: use the We might eventually have built-in combinators, and/or piping in from arbitrary amounts of criteria without needing to express it all as a long "chain" of pipes. I don't think these plans are explicitly documented anywhere, though. cc @TheRawMeatball? (Judging by the amount of edits your table has accrued while I was typing this up, I think you're now well aware why this isn't as straightforward as it is at first glance.) |
It's not straightforward, but I think I found the rules that work. So, how would you run a FixedTimeStep with State::on_update(GameState::InGame), today in bevy 0.5? |
What about when the first system returns any of the
You would have to, unfortunately, partially replicate the function of one of them to create a pipeable version. This is why we want to pipe from more than one criteria. |
The problem with the piping is that it doesn't actually compose, right? The system you pass to |
Flip the table. The logic commutes. |
Oh, right, we are talking about just
Not in a pleasant way, no. I see two options here:
|
I think Yes & YesAndCheckAgain -> Yes is the most tendentious rule in the table. Everything else seems very clear to me but that might be a bad one that kills the whole idea. |
It does make sense if we look at this combinator as strictly logical and - although, that doesn't check out for |
I agree that automatic combinators is something that's quite hard to convince ourselves we can always get right and I support waiting on that (for now, but with experience we might get more confidence about what the correct truth table is (or learn that there isn't one). But If there were a way to just toss the logic over the wall to the dev and say, "here's two ShouldRuns; we don't know how to combine them, you combine them", that would be composable. // These two systems are supplied by other crates, e.g. by Bevy core
fn a(..) -> ShouldRun {}
fn b(..) -> ShouldRun {}
// There is no way to pipe() a and b, they don't have the signature for that.
// Instead let's allow the user to define merge behavior
type CriteriaSystem = BoxedSystem<(), ShouldRun>;
fn merge(a: CriteriaSystem , b: CriteriaSystem , mergeFn: fn(ShouldRun, ShouldRun) -> ShouldRun) -> CriteriaSystem {}
|
That's exactly what I'm referring to with "piping from arbitrary amount of criteria". The idea was to generalize the implementation to fully cover criteria with no piped input, criteria with one piped input, and criteria with N piped inputs. |
I really like this approach. Rather than choosing one truth table we can offer combinators that implement different truth tables. And the user always has the choice of fully custom logic. I think we only need to handle the case of merging two systems. You can handle |
See also #1295. This is one of the larger pain points outside of areas that are simply missing functionality like UI, and seems to come up again and again. I'm going to add the high-impact label to this, in the hopes that we can work out a better solution in time for 0.6. |
It occured to me that there's nothing really specific about run criteria in the merge function I proposed. It's just a way of combining two systems that don't fit into a pipeline. It could be generalized to this:
(Or a macro that lets us do this for n systems). When trying to articulate why this has to be a change to bevy framework rather than just clever user code , it's because user code can't run systems: bevy always calls them. Since a lot of functionality is packaged as systems and not underlying composable functions, all the app developer can do is copypasta the logic inside the systems. That's the fundamental limitation that keeps this from being an external library crate. So, another way to remove the blocker is to allow systems to call other systems inline, e.g., by injecting &mut World. (I recognize that this messes up the nice parallelism from strongly-typed systems by hiding system dependencies). If that were available, app devs could roll their own combinators just by calling two systems and doing logic on their output(s). That would probably be the most general way to solve the problem. However that might be too far afield. |
Deduplicating in favor of #1295. |
What problem does this solve or what need does it fill?
The 0.5 StatesV2 system is a huge leap forward, but it still has some weaknesses.
One of the biggest weaknesses is that it doesn't compose well with other run criteria.
To give an example, suppose a user wants to handle input on a
FixedTimeStep
, but differently on a per-state basis.Today, it's not clear how to do that. It probably involves building a custom run criteria that manually composes the logic of FixedTimeStep and the logic of Statesv2.
What solution would you like?
I would like RunCriteria to become logically composable, so that:
Additionally, to make the motivating use-case work, it would be necessary to expose state-based run criteria as regular run criteria so they can be composed with other run criteria.
The text was updated successfully, but these errors were encountered: