-
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Support for With<T: Bundle>
#6516
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
Conversation
bevy/crates/bevy_ecs/src/bundle.rs Lines 39 to 56 in 0b2a0e9
|
I fully understand the intent behind bundle. This is more to help with implementing generic systems that operate on bundles, not to query bundles. This also doesn't deal with overlapping issues, because This just allows you to go from |
Not sure how I feel about bundle filters yet, but I think a more natural way of doing it would be to "just" allow |
I like the Edit: What about |
Yeah, that's why I'm hesitant. Not sure if I'm a fan of allowing
One way:
|
Can't we just make |
With<Bundle>
/// } | ||
/// # bevy_ecs::system::assert_is_system(compliment_entity_system); | ||
/// ``` | ||
pub type With<T> = <T as Bundle>::Filter; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oof, I really don't like this added complexity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Were you suggesting to just use Bundle
as the trait bound for With
in your other comment?
I'm not a big fan of allowing this pattern for anything but |
An alternative way of doing It's probably less efficient, but much less complex |
Less efficient in terms of implementation or runtime performance? If it's the latter, I don't think it'd be smart to compromise the performance of |
The other issue we may need to consider is that if this is being used as a stepping stone for entity kinds (#1634), then we would need to provide some ability to "upcast" an I don't think we'd be able to achieve that without using type information at the macro level, because it all has to happen at compile time. So the complexity added here may be necessary for that. |
Thinking more on this, if instead of Any query This means the implementation of Because of this, a better incremental approach may be to just re-introduce I would still be hesitant in using |
Seems like it was removed due to #2620 |
Given the discussion in #2620, I understand the objection to So then would it be correct to assume kinded entities would also be an "anti-feature" as a result, because it's effectively the same problem/solution with a different syntax? With the way bundles are implemented as-is in Bevy (i.e. |
Not necessarily; we could implement a trait over That way |
I don't quite see the benefit in the increased complexity of this approach (i.e. adding a new tuple of components that are like bundles in syntax, but not quite in usage), when the user could just do this to get what they want anyways: type Foo = (A, B);
fn system(query: Query<(), With<Foo>>) { } Putting artificial barriers in front of the user or taking power away from them because we don't trust in their ability to use the engine correctly, or because it may lead to poor OOP habits is a very pessimistic view of the user. :( Edit: I'm not even sure if your suggestion would be possible without causing ambiguity in the compiler. I already thought about doing something similar initially. The only way I can think of doing that without causing ambiguous cases would be do have a |
It's not increasing complexity, so much as moving it to a different trait. Here's a very rough example of how it could work: joseph-gio@6271a94 Now, this approach doesn't pave the way for entity kinds, but I don't think it needs to. We can always cross that bridge when we come to it. |
The implementation does make sense. And it does make the And again, this is all to prevent the user from using named bundles as filters, when the user could just define their bundles like this: type MyBundle = (A, B, C); Which is even less verbose than: #[derive(Bundle)]
struct MyBundle {
a: A,
b: B,
} So to me, we'd just be adding this artificial barrier for the user because we decided that anonymous and named bundles are mostly the same, but not quite. Instead, if we do allow If we don't want to allow named Edit: Alternatively, we could also approach the problem by removing named bundles, and making all bundles be anonymous, with "named" ones being simple |
That doesn't quite work. Type aliases can't be used as constructors, which means you can't do You might be right about complexity, but I'm not really opinionated enough to comment on it. |
Until the user does this: pub fn new_my_bundle(...) -> MyBundle { ... } With your changes, this also compiles/passes just fine: #[derive(Component, Default)]
struct Foo;
#[derive(Component, Default)]
struct Bar;
// #[derive(Bundle, Default)]
// struct FooBundle {
// foo: Foo,
// bar: Bar,
// }
type FooBundle2 = (Foo, Bar);
let mut world = World::new();
let e = world.spawn(FooBundle2::default()).id();
let mut q = world.query_filtered::<Entity, With<FooBundle2>>();
assert_eq!(q.single(&world), e); At this point I'm leaning towards just abandoning the idea because I don't think it justifies this arbitrary distinction between Let me know if you'd like me to update the PR with |
Agree here, this distinction is fiddly and just going to frustrate users. |
Objective
As far as I'm aware, there is currently no way to use a
Bundle
as a filter type. This applies to named bundles (i.e. using#[derive(Bundle)]
), as well as unnamed bundles, such as(A, B, C, ...)
.In other words, this transformation does not exist:
This makes the implementation of some generic systems unergonomic because for systems operating on an unknown number of generic components (i.e. a bundle), the user would have to define the filter query and the bundle separately. Basically the workaround is this:
This is also bad because there is effectively no guarantee that
F
and the components withinT
match.Solution
I propose a way to make this more ergonomic by using an associated type
Filter
onBundle
, which is automatically constructed by ECS macros. This type can then be used to create filtered queries for any genericBundle
orComponent
.In short, this PR allows the same system above to be written as:
This implementation also works with nested bundles:
This would also be a step towards a cleaner implementation of Entity Kinds (#1634) as described in @JoJoJet 's comment:
#1634 (comment)
Because instead of having to write
Entity<(With<A>, With<B>, With<C>, ...)>
, we'd be able to writeEntity<With<(A, B, C, ...)>>
, and useWith<T>
to derive theWorldQuery
needed for working with it.Changelog
Filter
associated type toBundle