TypeRegistry
derive(Reflect) 让类型有了反射能力,但光有能力还不够——引擎需要一个中央目录来管理所有反射类型的元信息。这就是 TypeRegistry:按 TypeId 或类型路径字符串查找注册信息,拿回 TypeRegistration 对象,从中读取 TypeInfo 和附加的 TypeData。
创建与注册
#[derive(Reflect, Default)]
#[reflect(Default)]
struct Score {
value: u32,
combo: u8,
}
#[derive(Reflect)]
struct Enemy {
name: String,
hp: i32,
}let mut registry = TypeRegistry::default();
registry.register::<Score>();
registry.register::<Enemy>();TypeRegistry::new() 会预注册所有基本类型(bool、i32、f32、String 等);TypeRegistry::default() 等价。register::<T>() 把你的自定义类型加进去——它会递归注册字段类型(比如 Score 的 u32 和 u8),并把 #[reflect(...)] 声明的 TypeData 一并录入。重复注册同一类型不会覆盖。
按 TypeId 查找
let score_id = std::any::TypeId::of::<Score>();
let registration = registry.get(score_id).unwrap();
println!("TypePath: {}", registration.type_info().type_path());
println!("ShortPath: {}", registration.type_info().type_path_table().short_path());registry.get(TypeId) 返回 Option<&TypeRegistration>。TypeRegistration 持有该类型的 TypeInfo——包括类型路径、字段信息、种类(Struct/Enum/Opaque 等)。
按字符串路径查找
当你只有类型名字符串(比如从场景文件读到的 "my_game::Player"),用 get_with_type_path:
let found = registry.get_with_type_path("listing_31_02::Enemy");
assert!(found.is_some());
println!("按路径找到: {}", found.unwrap().type_info().type_path());注册时会自动索引完整路径和短名。短名(如 "Player")如果有歧义(两个模块都叫 Player),查找会返回 None——必须用完整路径。这是避免静默错误的安全设计。
TypeData 机制
TypeData 是附加在类型注册信息上的运行时元数据。#[reflect(Default)] 注册的是 ReflectDefault,#[reflect(Component)] 注册的是 ReflectComponent。它们都是 TypeData trait 的实现者。
let score_reg = registry.get(std::any::TypeId::of::<Score>()).unwrap();
let reflect_default = score_reg.data::<ReflectDefault>();
println!("Score 有 ReflectDefault: {}", reflect_default.is_some());registration.data::<ReflectDefault>() 返回 Option<&ReflectDefault>。如果有值,你就能通过它在运行时调用 Default::default() 创建实例——即使你手头只有 dyn PartialReflect,不知道具体类型。
你也可以手动注册 TypeData:
registry.register_type_data::<MyType, ReflectSerialize>();这在泛型类型上特别有用——泛型的 derive 无法在编译期确定所有具体参数,需要你在知道具体类型后手动补注册。
AppTypeRegistry 资源
在 Bevy 应用里,TypeRegistry 不是裸着用的——引擎把它包在 AppTypeRegistry 资源里,DefaultPlugins 会自动创建它。你的游戏类型需要在 startup 系统里注册:
fn register_types(registry: Res<AppTypeRegistry>) {
let mut registry = registry.write();
registry.register::<Player>();
registry.register::<Enemy>();
}AppTypeRegistry 内部是 TypeRegistryArc(Arc<RwLock<TypeRegistry>>),所以可以安全地跨系统共享。Scene 加载、Inspector 面板、BRP 远程协议都是通过读这个资源来获取类型信息的。
运行:
cargo run -p ch31-reflect --example listing-31-02下一节看拿到类型信息后,怎么在运行时真正读写字段。