Repository navigation
Closed
Labels
A-ECSEntities, components, systems, and eventsEntities, components, systems, and eventsC-FeatureA new feature, making something new possibleA new feature, making something new possibleC-UsabilityA targeted quality-of-life change that makes Bevy easier to useA targeted quality-of-life change that makes Bevy easier to useS-Needs-DesignThis issue requires design work to think about how it would best be accomplishedThis issue requires design work to think about how it would best be accomplished
Description
I ran into an issue using Commands::spawn_batch to spawn in a bunch of entities that have some custom components and also needed a SpriteComponents bundle for rendering, and the only way I could find to do this was to use commands.spawn(...).with_bundle(...) or inline the fields of SpriteComponents. Both of these are sub-optimal solutions in my eyes.
One way to solve this would be to add a combinator for Bundle like
trait Bundle {
// ...
fn and<B: Bundle>(self, other: B) -> And<Self, B> where Self: Sized {
And { first: self, second: other }
}
}
struct And<A, B> {
first: A,
second: B,
}
impl<A: Bundle, B: Bundle> Bundle for And<A, B> {
// ...
}such that
fn some_startup(mut commands: Commands) {
commands.spawn(bundle_a).with_bundle(bundle_b);
}
// has the same effect as
fn some_startup(mut commands: Commands) {
commands.spawn(bundle_a.and(bundle_b));
}This would allow you to combine bundles in places where you can only pass in a single bundle, for example Commands::spawn_batch.
Reacted by Alice Cecile, Marius Cobzarenco, Yura Taras, RustyStriker, Max Willsey and Thomas Versteeg
Reacted by Marius Cobzarenco
Metadata
Metadata
Assignees
Labels
A-ECSEntities, components, systems, and eventsEntities, components, systems, and eventsC-FeatureA new feature, making something new possibleA new feature, making something new possibleC-UsabilityA targeted quality-of-life change that makes Bevy easier to useA targeted quality-of-life change that makes Bevy easier to useS-Needs-DesignThis issue requires design work to think about how it would best be accomplishedThis issue requires design work to think about how it would best be accomplished
Activity
alice-i-cecile commentedon Jan 17, 2021
While this is being looked at, it might be useful to think about how we can flatten nested bundles. Bundles which contain a
SpriteBundleor similar are particularly natural, but don't seem to work in the obvious way.alice-i-cecile commentedon Jan 17, 2021
In Discord, there was discussion of an alternate method to solve this problem: simply flattening the bundles entirely.
On the face of it, to make this non-ambiguous, you'd have to disallow non-bundle tuple components. I'm not convinced this is a huge loss. They're a very strange way of thinking about components; I think specialization is better handled by making the component type generic and I don't see any other obvious uses.
alice-i-cecile commentedon Jan 22, 2021
To demonstrate a particular example of this problem, here's an example showing the painful interaction between
spawn_batchandBundles.Suppose we're adding a number of entities at once with
.spawn_batch:If we also want to add a
Ballmarker component, we can easily insert this:But if we're using a premade bundle, like
SpriteBundle, this becomes exceptionally painful, since we can't just modify its internals. Instead, we're forced to remake the entire bundle from scratch, causing our code to break ifSpriteBundleis ever updated by Bevy. This problem gets worse if we want to combine multiple pre-existing bundles into a single bundle to be spawned withspawn_batch.A method for
extendandextend_bundleonBundlewould help alleviate this pain substantially.RustyStriker commentedon Jan 30, 2021
I think a good and more intuitive solution would be moving the
.withfunctions to theBundleinstead, that way if you use a bundle in any way you will simply be able to use the same.withfunctions resulting in a more consistent code.EDIT: I think besides a more consistent code base, having the with on the bundle will allow you to separate the
spawncommand from the building of complex objects which will result in a better looking code.mwillsey commentedon Feb 1, 2021
@RustyStriker I think your proposed solution will still require some kind of "flattening" operator.
Since the
Withtype will have to implementBundle.I was just taking a look at implementing some kind of flatten attribute:
Right now, it seems like actually implementing
Bundlefor something that has aBundlewon't be possible, sinceBundle::gethas this "check before you read" requirement. That makes callinggeton any inner bundles impossible, since they might check their components, pass, and then start reading before you calledgeton any other bundles.I think the
BundleAPI will have to be tweaked at the very least to fix this checking behavior before we can get nested bundles. They are other issues as well: for examplewith_idsandwith_static_idswill be very hard to implement without allocatingVecs, since we still have to preserve sorted-ness for type ids across all nested bundles.cart commentedon Feb 1, 2021
I believe the Bevy ECS rewrite I'm wrapping up will make this sort of thing much easier. The Bundle api is much simpler now.
alice-i-cecile commentedon Feb 26, 2021
This is (maybe) planned for #1525, and if not, should be much easier to implement once it's merged.
alice-i-cecile commentedon Mar 7, 2021
This is largely solved by #1525, documented in #1570. Does this do everything you want it to?
cart commentedon Mar 7, 2021
I think theres still value in the "bundle builder" apis mentioned above. #1525 adds the ability to nest bundle types, but more dynamic
world.spawn().insert_bundle(a_bundle.with(b_bundle))seems useful.RustyYato commentedon Mar 7, 2021
Yes it looks like it is solved by #1525. I still think there is value in ad hoc composition here using something like
Bundle::with/Bundle::and. I think this should be rather simple to implement because the existing derives handle the heavy lifting. This would allow me to streamline combining custom components/bundles with exiting premade ones.TheRawMeatball commentedon Mar 7, 2021
Personally I don't think this is very useful since you could just do
world.spawn().insert_bundle(a_bundle).insert_bundle(b_bundle).alice-i-cecile commentedon Mar 7, 2021
And hopefully the same thing with commands shortly too.
But there's a reasonably important use case I think you're missing @TheRawMeatball: bundle builder functions that are nested a couple layers deep. These have cropped up for me a few times, where:
spawn_batch.Moreover, bundles will be getting many more uses with #1481, better scenes and so on, so adding a few core QoL methods like this will pay off there too.
Is there some appropriate collections trait we can impl for bundles? That really feels like the most idiomatic solution here: it's very strange to have something close to a Vec of components that doesn't behave at all like one.
RustyYato commentedon Mar 7, 2021
@TheRawMeatball As I showed in my initial issue, that doesn't work in some special cases.
I think @alice-i-cecile has listed off more cases in newer/upcoming versions. But I'd like to approach this from another angle. Having ad-hoc bundles is useful for many of the same reasons why tuples are useful. When you need to package multiple things together, but you don't want to give them a name. Either because it's verbose, or because there isn't a great name to give them.
@alice-i-cecile No, I don't believe there are any standard collection traits. If this feels like a
Vec<_>, maybe you can draw inspiration from there on what to add. (Based on what makes sense forBundle). For exampleBundle::andas proposed at the beginning looks kind-of likeVec::extend/Vec::append.Bundle::withas proposed by @mwillsey looks kind of likeVec::push.SUPERCILEX commentedon Apr 29, 2022
Isn't this issue obsoleted by
#[bundle]flattening attribute? You could define a quick new bundle that includes the other.alice-i-cecile commentedon Apr 29, 2022
It helps, but doesn't solve it. You can do that to define bundles, but not to work with them dynamically.
SUPERCILEX commentedon Apr 29, 2022
Ohhhh, right dang.
james7132 commentedon Oct 19, 2022
Following #2975, this seems to be resolved as you can now just use tuple bundles to group multiple bundles and components together.