-
Notifications
You must be signed in to change notification settings - Fork 66
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
Trait queries #39
Trait queries #39
Conversation
Unmonomorphized generic functions are unrepresentable :(
Stumbled across this existing alternate implementation of the idea as a stand-alone Rust crate: https://github.com/Diggsey/query_interface/blob/master/src/lib.rs |
What are the performance characteristics of this? I would expect iterating over a set of heterogenous component data to be slower than (presumably memory-contiguous) homogenous data. |
From an idiomatic perspective, I tend to think of components and systems as a separation of data and behavior. Since traits implement behaviors, a trait queries seems to un-separate these concerns. |
This is worth calling out. Like all performance questions, this will ultimately come down to benchmarking. And, this is largely an ergonomic feature: I would expect this to be more important in rich, complex areas of games, rather than compute-heavy inner loops. However, we would be able to cache which components implement the desired traits, and thus cache which archetypes must be touched. Effectively, a trait query expands out into a As a result, it should have similar performance characteristics to other queries which need to mutate the same number of archetypes and components (although this may be high, due to the nature of the feature). There may be a tiny amount of additional overhead due to having to construct the per entity iterators for all components that impl the correct trait. So, not bare-metal-fast, but should be quite good. And probably very competitive with other approaches that could be used to achieve similar levels of flexibility / reusability (e.g. spawning a bajillion generic systems will require overhead on system initialization, sticking this into a |
My perspective here is actually a bit different, if admittedly slightly unconventional: I tend to think of components as more than just Raw Data. Instead, they enable behavior, toggling on and off different effects and systems on an entity within the context of a specific schedule. This perspective is why the dataless marker component pattern works (to great effect!). I tend to view trait queries in this light, and in existing Bevy code bases I'm often reaching for generic systems with a trait bound to imitate this functionality. Like marker components, components with a trait enable behavior: the existence of a trait just gives some tighter correctness bounds and enables heterogeneous implementations of how exactly that behavior should be implemented. Ultimately trait queries serve two main purposes:
|
I have an usecase where this would be extremely useful for my implementation of an OSC dispatcher. An OSC dispatcher receives an OSC message, which has an address, and forwards it to all receivers within an application that have a matching address: flowchart LR
r["Server (UDP)"] -->|receive message| d[Dispatcher]
d -- match --> A[Receiver Component A]
d -. no match .-x B[Receiver Component B]
d -- match --> C[Receiver Component C]
The dispatcher must have a way to access all Components that can receive OSC messages. I've thus implemented an Instead it would be much easier to implement an |
I threw together a quick and dirty prototype, which uses a different approach than the previous attempts. I'm using it in my game, and it works quite well. Here's a usage example: https://github.com/JoJoJet/bevy-trait-query/blob/main/examples/people.rs This does not use reflection. To create each trait object, the If more than one component implements the trait for a given entity, it just ignores the extras for now. I have some ideas for making it universal, though. |
I figured out how to do it entirely with pointer arithmetic, which should make it comparable in performance to queries of concrete types. Although this requires ptr_byte_offsets in order to not be UB. It seems like that's stabilizing soonish, though. |
Figured out universal queries. Here's some benchmarks:
If I apply the nightly-only optimization mentioned earlier...
I imagine this will rapidly slow down as the number of trait impls and archetypes increase, but that's probably unavoidable. I really like this as a baseline. |
I also have a use-case for this: Ergonomic text localization. I am working on integrating Project Fluent into Bevy, which allows localization that goes beyond simple string replacement. To use this in code, you have to provide a message ID to identify the text you want to insert. Optionally, you can also have variables which can be strings, numbers, etc. that is replaced in the message and might be used to stuff like pluralization. Now, because you'll probably have a lot of text in your game, you don't want to manually define a system that updates your The problem is now to incorporate the variables, which will be components or resources themselves. The plugin cannot know in advance which concrete components or resources will be needed. I first experimented with using the I experimented with multiple things, but I think this is simply not possible without trait queries. With trait queries I could probably define an update system that can check if the variables have updated, pulls their values from the |
I've polished up my implementation, and it's now ready for wider testing. I would highly appreciate feedback from anyone who tries using this in their game. Feel free to reach out if you have any questions. Crate: https://crates.io/crates/bevy-trait-query |
Any reason why this was closed? |
I'd still like this, but the 3rd party implementation is quite good. If we adopt this, it won't need to go through an RFC IMO. |
Is there an issue tracking this adoption ? I can't seem to find it |
@Inspirateur no issue yet: can you please open one? |
done here bevyengine/bevy#15970 |
RENDERED
This RFC introduces trait queries, which use runtime trait information about components (powered by reflection) to enable more direct and extensible methods for abstracting over component types that share a common trait.
Trait queries allow users to query and filter for all (or a single) components that implement a given trait.
Many thanks to @Davier for the initial prototype and idea.