-
-
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
Bundles with optional components #2157
Comments
Hmm, maybe a builder pattern would be better for this? If you have a big bundle that has a lot of optionals, then it would mean that you would have to set a lot of then to Anyways, you can already spawn custom bundles by using tuples. The Bevy Docs cover it. You can use it like |
Okay so imagine the following scenario: You want to synchronize an entity over the network, so you create a query like this on the server side: I hope that clears it up. |
If I understand correctly, when you put in a query an If that's the case, I wonder if a Component that is of type I'm 99% sure that I'm missing something though... 🤔 |
I'm pretty sure that it's queryable, you just have to switch where you put the I don't know if anyones uses it like this, but it is possible AFAIK. |
Thinking more about this, I actually think we shouldn't insert optional components from a bundle:
The Downside is that a bundle may not have the same components based on its value, but seems OK to me |
I ran into this when wanting to allow users to pass in a custom bundle for tiles. I'm not sure how I can let them have tags in bundles currently. Example: struct TileBundle {
tile: Tile,
visible: VisibleTile, // < If this is Option<VisibleTile> that doesn't make sense for a tag, yet there is no way for a user to not include the tag in the bundle.
} This type of code is useful for deferred spawning of entities. Where you want to build out all of your entity data first and then mass spawn all of them. |
This would likely require some major changes to how we handle Bundles internally and would likely result in more expensive archetype changes. Right now, we assume a Bundle's composition is static. This allows us to make bundle add/remove operations faster by pre-computing archetype transitions in the Archetype Graph and indexing them via the BundleId (which correlates 1:1 to a Bundle's TypeId). If we want to support this pattern, we'll probably need to find a way to treat permutations of "optional bundle components" as different BundleIds. There might be a way to reconcile this pattern with the "Bundle Builder" abstraction we've discussed in a couple of places: // TileBundle includes all possible TileBundle components
commands.spawn().insert_bundle(
TileBundle::default()
.without::<VisibleTile>()
) |
Nope, the |
I've been thinking about this feature a bit lately (even though I eventually realized that I don't need it for my current project after all):
Has anything changed in the last 1.5 years that would making implementing this feature easier or more difficult? |
|
Hmm, shouldn't any solution that would allow us to have optional Components in Bundles at all also allow us to impl Bundle for Option? A Bundle with a single optional Component is isomorphic to an Option of a Component after all; extending this from single Components to whole Bundles should be possible as well. I don't know much about the inner workings of Bundles and archetype transitions though, so I'm not sure what would need to change to allow implementing this at all, and what the tradeoffs are performance-wise. |
I guess. |
I encountered a use case where this may be relevant: I have an existing bundle with a However, since it is not possible to have optional components in a bundle I will need to refactor the code calling let bundle = MyBundle::from_asset(/*...*/);
commands.spawn(MyBundle); My current plan to address this is something like this: // Note, no longer actually a Bundle
impl MyBundle {
fn spawn<'w, 's, 'a>(self, commands: &'a mut Commands<'w, 's>) -> EntityCommands<'w, 's, 'a> {
if let Some(pending) = self.pending {
commands.spawn((pending, self.other_components))
} else {
commands.spawn(self.other_components)
}
}
} let bundle = MyBundle::from_asset(/*...*/);
bundle.spawn(&mut commands); Although while writing this I did think of another option. |
Another rusty solution in some cases may be to rely on a components |
Something to consider is what should happen if an attribute is set to The more natural scenario would be, in my opinion, to remove the current component, but I suppose the decision should arise from recurring patterns within the community. |
At first my gut instinct was the opposite. But I suppose if a bundle represents something like a "valid configuration" of components as it were, /not/ removing them could create an invalid or unintentional state. And having to explicitly check the options to remove them in such a case sounds annoying/error-prone, but conversely if you want the preserve behaviour it's going to be the greater pain (you can't really "undo" a removal, right?) Perhaps a flag or alternate method so both can be supported? 😅 |
Maybe the Just a suggestion |
I don't know much about bevy_ecs but couldn't this be hacked around for the time being with observers/component hooks or would that break optional queries? Something along the lines of adding OnAdd observers/hooks to all <T: Component> Option, add the component if Some and remove itself. |
I have the same use case. And I am seeing bad performance upon receiving a lot of components that need to be added to entity for the first time for hundreds of entities. Pseudocode: for entity_data in entities_data_from_server {
let entity = get_entity_or_spawn(...., entity_data.entity_id);
for component in entity_data.components {
match component {
ComponentA(comp) => { entity.insert(comp); }
ComponentB(comp) => { entity.insert(comp); }
....
}
}
} As much as I understand: It looks like something similar can be done with |
I've implemented a third-party version of this in If you're interested in adding this to Bevy itself, feel free to copy-paste my code directly and we can review it. |
What problem does this solve or what need does it fill?
Currently if a bundle has components which are wrapped in an
Option<>
, they are treated literally. However in my opinion it would be a good addition if we had the option to mark optional components, and they would be treated as such.What solution would you like?
Unfortunately I don't understand the inner workings of bevy enough to propose an exact solution, but after having a small conversation on discord, it has come to my attention, that there had been a similar feature like this in the old ECS called
DynamicBundle
. An example might look like this:What alternative(s) have you considered?
Manually checking every optional component and adding it manually.
Additional context
I hadn't used DynamicBundle when it was still in, so I don't know if what I'm proposing makes sense.
The text was updated successfully, but these errors were encountered: