Skip to content

世界序列化

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(节选):序列化场景到文件

关键步骤:

  1. 创建临时 World,复制 AppTypeRegistry
  2. 用反射组件(#[derive(Reflect)] + #[reflect(Component)])生成实体
  3. DynamicWorld::from_world 提取所有已注册组件
  4. serialize 输出 RON 格式字符串
  5. 写入 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-06
text
场景已保存
场景已加载
  发现实体,生命值:100
  发现实体,生命值:30

RON 格式

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 + CloneFromTemplateReflect + #[reflect(Component)]
资产自动通过 HandleTemplate 解析反射提取,需要已注册
适用场景UI 构建、场景组合、代码复用关卡编辑器、存档、热重载

实际项目中两者常配合使用:用 bsn! 构建 UI 和场景模板,用 DynamicWorld 做运行时保存/加载。