Tainted \\ Coders

Bevy Reflection

Last updated:

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());
}