Bevy uses reflection to provide a dynamic way of interacting with Rust types. For example, accessing fields by their string name.
The Reflect
trait enables serialization, deserialization, and dynamic property
access.
// Deriving `Reflect` implements the relevant reflection traits. In this case, it implements the
// `Reflect` trait and the `Struct` trait `derive(Reflect)` assumes that all fields also implement
// Reflect.
#[derive(Reflect)]
pub struct Foo {
a: usize,
nested: Bar,
#[reflect(ignore)]
_ignored: NonReflectedValue,
}
#[derive(Component, Reflect, Default)]
#[reflect(Component)] // this tells the reflect derive to also reflect component behaviors
struct ComponentA {
pub x: f32,
pub y: f32,
}
This lets us dynamically access the fields:
fn some_system() {
let mut value = Foo {
a: 1,
_ignored: NonReflectedValue { _a: 10 },
nested: Bar { b: 8 },
};
// You can set field values like this. The type must match exactly or this will fail.
*value.get_field_mut("a").unwrap() = 2usize;
assert_eq!(value.a, 2);
assert_eq!(*value.get_field::<usize>("a").unwrap(), 2);
// You can also get the &dyn Reflect value of a field like this
let field = value.field("a").unwrap();
// you can downcast Reflect values like this:
assert_eq!(*field.downcast_ref::<usize>().unwrap(), 2);
}
Traits can also be setup for reflection using the reflect_trait
attribute
macro:
#[reflect_trait]
pub trait DoThing {
fn do_thing(&self) -> String;
}
This will generate a ReflectDoThing
type we can use to dynamically access our
types from a trait:
fn some_system(type_registry: Res<AppTypeRegistry>) {
// First, lets box our type as a Box<dyn Reflect>
let reflect_value: Box<dyn Reflect> = Box::new(MyType {
value: "Hello".to_string(),
});
// This means we no longer have direct access to MyType or its methods. We can only call Reflect
// methods on reflect_value. What if we want to call `do_thing` on our type? We could
// downcast using reflect_value.downcast_ref::<MyType>(), but what if we don't know the type
// at compile time?
// Normally in rust we would be out of luck at this point. Lets use our new reflection powers to
// do something cool!
let type_registry = type_registry.read();
let reflect_do_thing = type_registry
.get_type_data::<ReflectDoThing>(reflect_value.type_id())
.unwrap();
// We can use this generated type to convert our `&dyn Reflect` reference to a `&dyn DoThing`
// reference
let my_trait: &dyn DoThing = reflect_do_thing.get(&*reflect_value).unwrap();
// Which means we can now call do_thing(). Magic!
info!("{}", my_trait.do_thing());
}