docs/bevy/examples/reflection/custom_attributes.rs 2.9 K · 89 lines · raw · history

1 //! Demonstrates how to register and access custom attributes on reflected types.
2
3 use bevy::reflect::{Reflect, TypeInfo, Typed};
4 use std::{any::TypeId, ops::RangeInclusive};
5
6 fn main() {
7 // Bevy supports statically registering custom attribute data on reflected types,
8 // which can then be accessed at runtime via the type's `TypeInfo`.
9 // Attributes are registered using the `#[reflect(@...)]` syntax,
10 // where the `...` is any expression that resolves to a value which implements `Reflect`.
11 // Note that these attributes are stored based on their type:
12 // if two attributes have the same type, the second one will overwrite the first.
13
14 // Here is an example of registering custom attributes on a type:
15 #[derive(Reflect)]
16 struct Slider {
17 #[reflect(@RangeInclusive::<f32>::new(0.0, 1.0))]
18 // Alternatively, we could have used the `0.0..=1.0` syntax,
19 // but remember to ensure the type is the one you want!
20 #[reflect(@0.0..=1.0_f32)]
21 value: f32,
22 }
23
24 // Now, we can access the custom attributes at runtime:
25 let TypeInfo::Struct(type_info) = Slider::type_info() else {
26 panic!("expected struct");
27 };
28
29 let field = type_info.field("value").unwrap();
30
31 let range = field.get_attribute::<RangeInclusive<f32>>().unwrap();
32 assert_eq!(*range, 0.0..=1.0);
33
34 // And remember that our attributes can be any type that implements `Reflect`:
35 #[derive(Reflect)]
36 struct Required;
37
38 #[derive(Reflect, PartialEq, Debug)]
39 struct Tooltip(String);
40
41 impl Tooltip {
42 fn new(text: &str) -> Self {
43 Self(text.to_string())
44 }
45 }
46
47 #[derive(Reflect)]
48 #[reflect(@Required, @Tooltip::new("An ID is required!"))]
49 struct Id(u8);
50
51 let TypeInfo::TupleStruct(type_info) = Id::type_info() else {
52 panic!("expected struct");
53 };
54
55 // We can check if an attribute simply exists on our type:
56 assert!(type_info.has_attribute::<Required>());
57
58 // We can also get attribute data dynamically:
59 let some_type_id = TypeId::of::<Tooltip>();
60
61 let tooltip: &dyn Reflect = type_info.get_attribute_by_id(some_type_id).unwrap();
62 assert_eq!(
63 tooltip.downcast_ref::<Tooltip>(),
64 Some(&Tooltip::new("An ID is required!"))
65 );
66
67 // And again, attributes of the same type will overwrite each other:
68 #[derive(Reflect)]
69 enum Status {
70 // This will result in `false` being stored:
71 #[reflect(@true)]
72 #[reflect(@false)]
73 Disabled,
74 // This will result in `true` being stored:
75 #[reflect(@false)]
76 #[reflect(@true)]
77 Enabled,
78 }
79
80 let TypeInfo::Enum(type_info) = Status::type_info() else {
81 panic!("expected enum");
82 };
83
84 let disabled = type_info.variant("Disabled").unwrap();
85 assert!(!disabled.get_attribute::<bool>().unwrap());
86
87 let enabled = type_info.variant("Enabled").unwrap();
88 assert!(enabled.get_attribute::<bool>().unwrap());
89 }