---
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::<AppTypeRegistry>();
  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<AssetServer>) {
  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<bevy::world_serialization::WorldInstanceReady>,
  mut spawner: ResMut<WorldInstanceSpawner>,
  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::<Time>();
    ComponentB {
      _time_since_startup: time.elapsed(),
      value: "Default Value".to_string(),
    }
  }
}
```

## Required setup

- Components must `#[derive(Reflect)]` for auto-registration
- `#[reflect(skip_serializing)]` excludes fields from serialization
