--- name: bevy-scenes description: Reference for Bevy scenes — file-based world serialization via reflection, DynamicWorld, WorldInstanceSpawner, and DynamicWorldRoot. metadata: crate: bevy_scene bevy: "0.19" --- Bevy ships two scene systems: in-code Bevy Scene Notation (BSN, see the `bevy-bsn` skill) and file-based world serialization. This skill covers world serialization. ## World serialization The older format stores whole worlds in a file that is reinitialized when it is loaded. Worlds are saved into a `.scn` or `.scn.ron` file, based on [Rusty Object Notation (RON)](https://crates.io/crates/ron). `DynamicWorld` is the serializable representation: resources plus entities. ## Saving a world Save with `DynamicWorld::serialize`: ```rust fn save_scene_system(world: &mut World) { let dynamic_world = DynamicWorld::from_world(world); let type_registry = world.resource::(); let type_registry = type_registry.read(); let serialized_world = dynamic_world.serialize(&type_registry).unwrap(); info!("{}", serialized_world); // Writing to file: use a task so the filesystem call does not block a system. // This cannot work on WASM (no filesystem access). #[cfg(not(target_arch = "wasm32"))] IoTaskPool::get() .spawn(async move { File::create(format!("assets/{NEW_SCENE_FILE_PATH}")) .and_then(|mut file| file.write(serialized_world.as_bytes())) .expect("Error while writing world to file"); }) .detach(); } ``` ## Loading a world When Bevy loads the file it deserializes it into components and entities. There are two ways to instantiate a `DynamicWorld`: 1. Using the `WorldInstanceSpawner` resource with `spawn_dynamic` (deferred), `spawn_dynamic_sync` (immediate), or `spawn_dynamic_as_child` 2. Adding the `DynamicWorldRoot` component to an entity, which spawns the world's entities as children of that entity `DynamicWorldRoot` uses the `WorldAssetLoader` to deserialize everything: ```rust const SCENE_FILE_PATH: &str = "scene.scn.ron"; fn load_scene_system(mut commands: Commands, asset_server: Res) { commands.spawn(DynamicWorldRoot(asset_server.load(SCENE_FILE_PATH))); } ``` Once loaded, a `WorldInstance` component is added to the entity holding the scene root. It dereferences to the `InstanceId` of the spawned world, which can be passed to methods such as `WorldInstanceSpawner::despawn_instance_sync`. `WorldInstanceReady` fires once the world is fully loaded: ```rust fn despawn_scene( trigger: On, mut spawner: ResMut, world: &mut World, ) { spawner.despawn_instance_sync(world, &trigger.instance_id); } ``` ## Component construction on load When a component is deserialized, Bevy constructs it by trying, in order: 1. Reflected `FromReflect` (generated by `#[derive(Reflect)]`) 2. Reflected `Default` plus `apply` 3. Finally reflected `FromWorld` `FromWorld` is a fallback that lets you customize initialization using the current world's resources, and it only participates if you add `#[reflect(FromWorld)]` to the type: ```rust impl FromWorld for ComponentB { fn from_world(world: &mut World) -> Self { let time = world.resource::