Expand description
§Bevy Queryable Reflect
Bevy’s ReflectComponent allows extracting a value from a EntityRef or
EntityMut, this can be useful, but generally not enough.
ReflectComponent is missing ways to query for the reflected thing.
Without this ability, you would be stuck iterating over all EntityRef and
checking if it contains the component in question. This is O(n) (where n is
the total count of entities) while querying just for a single entity is O(1).
We introduce ReflectQueryable to fill this gap.
§Should I use this crate?
In short, if you are asking this question, you most likely shouldn’t!
If the second paragraph of this README means anything to you, as in: this is not complete technobable, and you are like “Wow! It’s possible!” then yeah babe, that’s for you!
§Usage
First, add this crate as dependency to your Cargo.toml:
[dependencies]
cuicui_reflect_query = "<current version>"If you need to use ReflectQueryable on pre-existing bevy components, check
the Features section.
Then, you would use ReflectQueryable in combination with the TypeRegistry
and an exclusive access World as follow:
use std::any::TypeId;
use bevy::prelude::{Reflect, ReflectComponent, Component, World};
use bevy::reflect::TypeRegistryInternal as TypeRegistry;
use cuicui_reflect_query::{ReflectQueryable, Ref};
#[derive(Debug, Clone, PartialEq, Component, Reflect, Default)]
#[reflect(Component, Queryable)]
struct Zoobazee {
bee: u32,
baboo: String,
}
fn reflect_query<'w>(world: &'w mut World, registry: &TypeRegistry) -> Ref<'w, dyn Reflect> {
let type_data = registry
.get_type_data::<ReflectQueryable>(TypeId::of::<Zoobazee>())
.unwrap();
let mut query = type_data.query(world);
for element in query.iter(world) {
println!("{element:?}");
}
type_data.get_single_ref(world).unwrap()
}
fn main() {
let mut world = bevy::prelude::World::new();
let mut type_registry = TypeRegistry::new();
type_registry.register::<Zoobazee>();
let component = Zoobazee {
bee: 32,
baboo: "zoobalong".to_string(),
};
world.spawn(component.clone());
let single_result = reflect_query(&mut world, &type_registry);
assert_eq!(single_result.downcast_ref(), Some(&component));
}§Details
ReflectQueryable adds methods to query from a dynamic value:
reflect_ref: This is similar toReflectComponent::reflect, but also includes change tick data. This allows reading change ticks on the reflected component in an immutable way.
Furthermore, thereflect_mutmethod has lifetime limitations, this might be a good way to work around them.query{,_entities,_ref,_mut}: Iterate over all entities with the reflected component. Like withworld.query, you need to call.query(world)on the return value to iterate over the query results.get_single{,_entity,_ref,_mut}: similar toworld.get_single, it will return a value only if there is exactly one entity containing the reflected component.
A bit of precision:
- The
_entityvariants return anEntityor an iterator of entities instead of thedyn Reflectversion of the component. - The
_refvariants return aRef<_>over_, this let you read change information in an immutable maner. Note thatRefis not the bevyRefbut an implementation incuicui_reflect_query. It is currently impossible to use bevy’sReffor this.
pub struct ReflectQueryableFns {
pub reflect_ref: fn(EntityRef) -> Option<Ref<dyn Reflect>>,
pub get_single: fn(&mut World) -> SingleResult<&dyn Reflect>,
pub get_single_entity: fn(&mut World) -> SingleResult<Entity>,
pub get_single_ref: fn(&mut World) -> SingleResult<Ref<dyn Reflect>>,
pub get_single_mut: fn(&mut World) -> SingleResult<Mut<dyn Reflect>>,
pub query: fn(&mut World) -> Querydyn,
pub query_entities: fn(&mut World) -> EntityQuerydyn,
pub query_ref: fn(&mut World) -> RefQuerydyn,
pub query_mut: fn(&mut World) -> MutQuerydyn,
}§Implementations for base bevy components
Since this is not part of bevy, we need to add those to the bevy components.
To add ReflectQueryable implementations for bevy components, use
predefined::QueryablePlugin as follow:
use bevy::prelude::*;
use cuicui_reflect_query::predefined::QueryablePlugin;
fn main() {
let mut app = App::new();
app.add_plugins(DefaultPlugins)
// … bunch of plugins …
.add_plugin(QueryablePlugin);
// … bunch of other things
}This crate exposes one feature per bevy features, they are off by default, you
must explicitly enable them to register ReflectQueryable for bevy components:
register_core_pipelineregister_pbrregister_spriteregister_renderregister_uiregister_text
You’d then add them as follow:
[dependencies]
cuicui_reflect_query = { version = "<current version>", features = ["register_…" ] }Beware that you can add them yourself. But please, if anything is missing open an issue, it’s hard to make sure I didn’t forget anything.
§Implementing for your own types
Like with ReflectComponent, you need to register the trait information
for your own types. It will look like this:
+ use cuicui_reflect_query::ReflectQueryable;
#[derive(Reflect, Component, Default)]
- #[reflect(Component)]
+ #[reflect(Component, Queryable)]
struct Zoobaroo {
bidoo: u32,
bubble: String,
padiwoop: AlphaMode,
}Make sure to app.register_type::<Zoobaroo>()! and you should be good to go.
§Version matrix
| bevy | latest supported version |
|---|---|
| 0.10 | <current version> |
§License
Copyright © 2023 Nicola Papale
This software is licensed under either MIT or Apache 2.0 at your leisure.
See licenses directory at the root of the cuicui
repository for details.
Modules§
- predefined
- Add
ReflectQueryableto the app for all pre-existing bevy components. - queries
- Define wrapper types & traits for bevy’s
QueryStateandQueryIter.
Structs§
- Ref
- A custom version of bevy’s
Refthat can be constructed fromEntityRefand mapped over. - Reflect
Queryable - A reflect trait extending
ReflectComponentwith query methods. - Reflect
Queryable Fns - The function pointers used by
ReflectQueryable.