Replies: 6 comments 18 replies
-
I think both the name of Com and the use of Entity here are confusing. That can be revised though. On vacation but just leaving a quick note so I don't forget! |
Beta Was this translation helpful? Give feedback.
-
First, there's a lot to like here, but for the sake of brevity I'm not going to list all the things I am in favor of (although change detection on systems is a pleasant surprise), instead I'll get right to the critiques:
More comments later as I think of them. |
Beta Was this translation helpful? Give feedback.
-
Rename please, Com looks like Cow, and doesnt suggest anything about targeting. Entity by itself would make more sense if it referred to the source/system entity itself, but it doesnt, again not suggesting anything about targeting involved. Com: returns a component C on the target entity (if one is configured). To: Target<B: Bundle>: returns an Bundle B on the target entity (if one is configured). I like pretty much everything else, the use of all the new features is quite nice, and being able to poll systems for system param changes is great and how I thought this would be implemented 👍 |
Beta Was this translation helpful? Give feedback.
-
This makes me happy 👍. If I understand correctly,
Like Talin, I'm curious about the If I understand correctly, these entities will be siblings of their "children" (in semantic terms). To me that makes them (implicit) ghost nodes (ghost entities?). They are not ghost parents, but they are siblings of entities that may belong to a UI or Transform hierarchy, meaning the index of another entity among those
This sounds very similar to what I have been proposing for UI/Transform/Visibility as a
+1. I already feel a bit confused around observers and reactions. Reactions feel like a syntax sugar of an observer that listens to "on mutate" events + conveniences to read the values of the mutated data. If there was a way to make one nice API that fits both the "event" and "reactive effect" use case that would awesome.
Yes!! Is this not doable with an I can't wait to play with this ❤️ |
Beta Was this translation helpful? Give feedback.
-
I'm not too much a fan of this design. I had explored similar designs before in #16422 and #16309, but didn't find it to be workable. Look at the TODOs for nesting, chaining, and sharing signals/reactions. I didn't have a good path towards implementing any of it. Explicit reactions like this are not really declarative, which is what I want. This feels more like GTK property binding https://docs.gtk.org/gobject/class.Binding.html which I've used in the past. What I really want is something fully declarative, i.e.: fn my_ui_tree(foo: &Foo, bar: &Bar) -> UiTree {
// ...
}
commands.spawn_automatically_updating_ui(my_ui_tree); I really don't want to have to care about change detection or dependencies. All I want is to say given some input, return this output, and then have some system in the background figure out how to keep it up to date each frame. Besides not being declarative, it's not clear to me how you would hoist reactive state out of the entity tree. E.g. https://github.com/bevyengine/bevy_editor_prototypes/blob/3e1dad501ed762fddc3503748464800a4d94ab23/bevy_editor_panes/bevy_properties_pane/src/lib.rs#L112-L129 Or this example in react https://react.dev/learn/tutorial-tic-tac-toe (search for "To let the players know when the game is over, you can display text such as “Winner: X” or “Winner: O”. To do that you’ll add a status section to the Board component. The status will display the winner if the game is over and if the game is ongoing you’ll display which player’s turn is next:", and look at the code diff under it). |
Beta Was this translation helpful? Give feedback.
-
What's the justification for first party reactivity? It could be useful if bevy can provide like an The rest of the proposal is great, |
Beta Was this translation helpful? Give feedback.
-
I've been experimenting with an alternative approach to reactivity that doesn't use "ghost nodes", "multiple interconnected hierarchies", or "templates". It looks like this:
Everything above has been implemented. Here is a video of
for_each
being used in practice. Note that I've added normal Bevy ECS logic to track and display the "elapsed time" of each list item to illustrate the lifetime (and statefulness) of a given entity / illustrate when it has been replaced:2025-02-17.14-18-34.mp4
In broad strokes:
Reaction
component (which contains a "reactive system"). Reactions live on their own entity that relates to a "reaction target" entity using either the new ReactionTo/Reactions relationship (for reactions that react directly to the target entity) or the existing ChildOf relationship (for reactions that need to care about their position in the children collection)for_each
.for_each
uses a reactive system to return an iterator over keys and a function that takes that key and returns a Bundle).for_each
internally finds where it is in the parent's Children, and assumes the entities directly after it are its entities. When it comes time to update, it will only consider that slice of entities.Pros and Cons
First, the reasons why I like this (these are also the reasons I've been hesitant to embrace other proposals):
is_checked
boolean to determine if it is checked, you can directly mutateCheckbox::is_checked
and see the results. I believe this property is extremely valuable.Of course there are also downsides:
CheckBox { is_checked: #root->MyCheckBox::is_checked }
and resolve it to a Reaction that lives on the#root
entity, reacts toMyCheckBox
changes, queries forCheckBox
on the current nested entity, and assigns the value ofMyCheckBox::is_checked
. Not saying we need to use that API ... just an example.for_each
requires more work / is more error prone, as it is essentially layering its own concept of hierarchy on top. I think this is the correct place to eat this complexity, as the alternative is foisting it upon people spawning or accessing entities. We will probably be able to count the number of these built-in implementations on one or two hands. We will likely also be able to count the number of users writing their own implementions on one or two hands.for_each
by inserting or removing entities in a spot that it "owns". In practice, I don't think this will be hit often as the "easy" way of spawning new children appends to the end of the list (which cannot invalidate anything). We can also build guard rails (ex: label owned entities with marker components so we can detect entities that are manually inserted in an owned position), and every implementation we are considering suffers from this category of problem to an extent (it is the nature of implementating reactivity in "userspace Bevy ECS").While these downsides are present, I believe the benefits far outweigh the downsides. I am currently biased toward this approach over other proposals, with the disclaimer that this is still early stages and I have not explored every scenario yet.
Implementation Details
This implementation is composed of the following pieces:
Com<C>
: returns a component C on the target entity (if one is configured).Entity
: this will be set to the "target entity" (if one is configured)SourceEntity
: this will be set to the "source entity" (if one is configured)SystemParam::is_changed
(and piping that into the newSystem::is_changed
), allowing us to poll if a system (ex: one of its parameters) has changed since its last run.ReactionTo
is not present, it will do the same thing with theChildOf
reaction. Supporting both is a key piece!for_each
insert_when
, etc.So far none of this directly touches on why this is an alternative to things like "ghost nodes" or "multiple interconnected hierarchies".
Next Steps
Reaction::new(|checkbox: Com<Checkbox>, on_click: Triggered<Pointer<Click>>| {})
.Com
to access a targeted component in the Observer. However Observers currently requireTrigger<E>
, which isn't something we want for reactions.Beta Was this translation helpful? Give feedback.
All reactions