type_data/type_data.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
//! The example demonstrates what type data is, how to create it, and how to use it.
use bevy::{
prelude::*,
reflect::{FromType, TypeRegistry},
};
// It's recommended to read this example from top to bottom.
// Comments are provided to explain the code and its purpose as you go along.
fn main() {
trait Damageable {
type Health;
fn damage(&mut self, damage: Self::Health);
}
#[derive(Reflect, PartialEq, Debug)]
struct Zombie {
health: u32,
}
impl Damageable for Zombie {
type Health = u32;
fn damage(&mut self, damage: Self::Health) {
self.health -= damage;
}
}
// Let's say we have a reflected value.
// Here we know it's a `Zombie`, but for demonstration purposes let's pretend we don't.
// Pretend it's just some `Box<dyn Reflect>` value.
let mut value: Box<dyn Reflect> = Box::new(Zombie { health: 100 });
// We think `value` might contain a type that implements `Damageable`
// and now we want to call `Damageable::damage` on it.
// How can we do this without knowing in advance the concrete type is `Zombie`?
// This is where type data comes in.
// Type data is a way of associating type-specific data with a type for use in dynamic contexts.
// This type data can then be used at runtime to perform type-specific operations.
// Let's create a type data struct for `Damageable` that we can associate with `Zombie`!
// Firstly, type data must be cloneable.
#[derive(Clone)]
// Next, they are usually named with the `Reflect` prefix (we'll see why in a bit).
struct ReflectDamageable {
// Type data can contain whatever you want, but it's common to include function pointers
// to the type-specific operations you want to perform (such as trait methods).
// Just remember that we're working with `Reflect` data,
// so we can't use `Self`, generics, or associated types.
// In those cases, we'll have to use `dyn Reflect` trait objects.
damage: fn(&mut dyn Reflect, damage: Box<dyn Reflect>),
}
// Now, we can create a blanket implementation of the `FromType` trait to construct our type data
// for any type that implements `Reflect` and `Damageable`.
impl<T: Reflect + Damageable<Health: Reflect>> FromType<T> for ReflectDamageable {
fn from_type() -> Self {
Self {
damage: |reflect, damage| {
// This requires that `reflect` is `T` and not a dynamic representation like `DynamicStruct`.
// We could have the function pointer return a `Result`, but we'll just `unwrap` for simplicity.
let damageable = reflect.downcast_mut::<T>().unwrap();
let damage = damage.take::<T::Health>().unwrap();
damageable.damage(damage);
},
}
}
}
// It's also common to provide convenience methods for calling the type-specific operations.
impl ReflectDamageable {
pub fn damage(&self, reflect: &mut dyn Reflect, damage: Box<dyn Reflect>) {
(self.damage)(reflect, damage);
}
}
// With all this done, we're ready to make use of `ReflectDamageable`!
// It starts with registering our type along with its type data:
let mut registry = TypeRegistry::default();
registry.register::<Zombie>();
registry.register_type_data::<Zombie, ReflectDamageable>();
// Then at any point we can retrieve the type data from the registry:
let type_id = value.reflect_type_info().type_id();
let reflect_damageable = registry
.get_type_data::<ReflectDamageable>(type_id)
.unwrap();
// And call our method:
reflect_damageable.damage(value.as_reflect_mut(), Box::new(25u32));
assert_eq!(value.take::<Zombie>().unwrap(), Zombie { health: 75 });
// This is a simple example, but type data can be used for much more complex operations.
// Bevy also provides some useful shorthand for working with type data.
// For example, we can have the type data be automatically registered when we register the type
// by using the `#[reflect(MyTrait)]` attribute when defining our type.
#[derive(Reflect)]
// Notice that we don't need to type out `ReflectDamageable`.
// This is why we named it with the `Reflect` prefix:
// the derive macro will automatically look for a type named `ReflectDamageable` in the current scope.
#[reflect(Damageable)]
struct Skeleton {
health: u32,
}
impl Damageable for Skeleton {
type Health = u32;
fn damage(&mut self, damage: Self::Health) {
self.health -= damage;
}
}
// This will now register `Skeleton` along with its `ReflectDamageable` type data.
registry.register::<Skeleton>();
// And for object-safe traits (see https://doc.rust-lang.org/reference/items/traits.html#object-safety),
// Bevy provides a convenience macro for generating type data that converts `dyn Reflect` into `dyn MyTrait`.
#[reflect_trait]
trait Health {
fn health(&self) -> u32;
}
impl Health for Skeleton {
fn health(&self) -> u32 {
self.health
}
}
// Using the `#[reflect_trait]` macro we're able to automatically generate a `ReflectHealth` type data struct,
// which can then be registered like any other type data:
registry.register_type_data::<Skeleton, ReflectHealth>();
// Now we can use `ReflectHealth` to convert `dyn Reflect` into `dyn Health`:
let value: Box<dyn Reflect> = Box::new(Skeleton { health: 50 });
let type_id = value.reflect_type_info().type_id();
let reflect_health = registry.get_type_data::<ReflectHealth>(type_id).unwrap();
// Type data generated by `#[reflect_trait]` comes with a `get`, `get_mut`, and `get_boxed` method,
// which convert `&dyn Reflect` into `&dyn MyTrait`, `&mut dyn Reflect` into `&mut dyn MyTrait`,
// and `Box<dyn Reflect>` into `Box<dyn MyTrait>`, respectively.
let value: &dyn Health = reflect_health.get(value.as_reflect()).unwrap();
assert_eq!(value.health(), 50);
// Lastly, here's a list of some useful type data provided by Bevy that you might want to register for your types:
// - `ReflectDefault` for types that implement `Default`
// - `ReflectFromWorld` for types that implement `FromWorld`
// - `ReflectComponent` for types that implement `Component`
// - `ReflectResource` for types that implement `Resource`
// - `ReflectSerialize` for types that implement `Serialize`
// - `ReflectDeserialize` for types that implement `Deserialize`
//
// And here are some that are automatically registered by the `Reflect` derive macro:
// - `ReflectFromPtr`
// - `ReflectFromReflect` (if not `#[reflect(from_reflect = false)]`)
}