2 releases
0.1.1 | Jun 11, 2023 |
---|---|
0.1.0 | Jun 11, 2023 |
#2174 in Game dev
31KB
496 lines
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_mut
method 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
_entity
variants return anEntity
or an iterator of entities instead of thedyn Reflect
version of the component. - The
_ref
variants return aRef<_>
over_
, this let you read change information in an immutable maner. Note thatRef
is not the bevyRef
but an implementation incuicui_reflect_query
. It is currently impossible to use bevy'sRef
for 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_pipeline
register_pbr
register_sprite
register_render
register_ui
register_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.
Dependencies
~19–55MB
~1M SLoC