世界序列化
BSN 用于代码中的声明式场景描述。当需要把场景保存到文件或从文件加载时,使用 DynamicWorld——基于反射的序列化系统。
DynamicWorld
DynamicWorld 是一个可序列化的实体与资源集合。每个实体存储为 DynamicEntity,持有 Entity ID 和反射组件列表。
rust
fn save_scene(world: &mut World) {
let mut scene_world = World::new();
scene_world.insert_resource(world.resource::<AppTypeRegistry>().clone());
scene_world.spawn((
Position { x: 10.0, y: 20.0 },
Health(100),
));
scene_world.spawn((
Position { x: 50.0, y: 80.0 },
Health(30),
));
let scene = DynamicWorld::from_world(&scene_world);
let type_registry = world.resource::<AppTypeRegistry>().read();
let ron_string = scene.serialize(&type_registry).unwrap();
std::fs::create_dir_all("assets/scenes").unwrap();
std::fs::write("assets/scenes/level.scn.ron", &ron_string).unwrap();
println!("场景已保存");
}Listing 32-6(节选):序列化场景到文件
关键步骤:
- 创建临时 World,复制
AppTypeRegistry - 用反射组件(
#[derive(Reflect)]+#[reflect(Component)])生成实体 DynamicWorld::from_world提取所有已注册组件serialize输出 RON 格式字符串- 写入
assets/scenes/目录
DynamicWorldBuilder
需要精细控制提取内容时,用 DynamicWorldBuilder:
rust
let scene = DynamicWorldBuilder::from_world(&world, &type_registry)
.extract_entities(entities.into_iter())
.deny_component::<Velocity>()
.build();DynamicWorldBuilder 支持:
| 方法 | 说明 |
|---|---|
extract_entity(entity) | 提取指定实体 |
extract_entities(iter) | 提取多个实体 |
extract_resources() | 提取资源 |
allow_component::<T>() | 只提取该组件类型 |
deny_component::<T>() | 排除该组件类型 |
allow_resource::<T>() / deny_resource::<T>() | 资源过滤 |
从文件加载
DynamicWorldRoot 组件配合 AssetServer 自动加载并实例化场景:
rust
fn load_scene(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn(DynamicWorldRoot(asset_server.load("scenes/level.scn.ron")));
println!("场景已加载");
}
fn log_loaded(query: Query<&Health, Added<Health>>) {
for health in &query {
println!(" 发现实体,生命值:{}", health.0);
}
}Listing 32-6(节选):从文件加载场景
DynamicWorldRoot(Handle<DynamicWorld>) 挂到实体上后,Bevy 的场景调度系统(SpawnScene schedule)自动把文件内容实例化为该实体的子实体。
运行完整的保存-加载流程:
console
cargo run -p ch32-scenes --example listing-32-06text
场景已保存
场景已加载
发现实体,生命值:100
发现实体,生命值:30RON 格式
DynamicWorld::serialize 输出的 RON 大致如下:
text
(
entities: {
0: (
components: {
"my_crate::Position": (x: 10.0, y: 20.0),
"my_crate::Health": (100),
},
),
1: (
components: {
"my_crate::Position": (x: 50.0, y: 80.0),
"my_crate::Health": (30),
},
),
},
)每个实体以数字 ID 为键,组件以完整类型路径为键。资源单独列在 resources 块里。
BSN 与 DynamicWorld 的关系
两者解决不同问题:
BSN (bsn!) | DynamicWorld | |
|---|---|---|
| 用途 | 代码中的声明式场景 | 文件序列化/反序列化 |
| 格式 | Rust 宏,编译时检查 | RON 文件,运行时解析 |
| 组件要求 | Default + Clone 或 FromTemplate | Reflect + #[reflect(Component)] |
| 资产 | 自动通过 HandleTemplate 解析 | 反射提取,需要已注册 |
| 适用场景 | UI 构建、场景组合、代码复用 | 关卡编辑器、存档、热重载 |
实际项目中两者常配合使用:用 bsn! 构建 UI 和场景模板,用 DynamicWorld 做运行时保存/加载。