Open-Source Wikis

/

Bevy

/

Features

/

Scenes

bevyengine/bevy

Scenes

Saving and loading worlds. Bevy supports two scene formats: BSN (Bevy Scene Notation) and reflection-driven RON. Both are reflection-aware — any #[derive(Reflect)] component round-trips correctly.

The pieces

  • bevy_scene — the runtime: spawn / despawn, SceneRoot, SceneSpawner.
  • bevy_world_serialization — the format: BSN parser, RON serializer, asset loaders.
  • bevy_reflect — the substrate: every reflectable component flows through here.
  • bevy_gltf — the largest scene producer: glTF imports as a Scene.

Spawning a scene

use bevy::prelude::*;

fn setup(mut commands: Commands, server: Res<AssetServer>) {
    commands.spawn(SceneRoot(server.load("models/level.gltf#Scene0")));
}

SceneRoot is just (Handle<Scene>). A system in bevy_scene sees the new component, resolves the handle, and spawns the scene's entities as children of the root entity.

Hot reload of the underlying asset re-spawns the scene's children automatically.

Saving a scene

use bevy::prelude::*;
use bevy::scene::DynamicScene;

fn save(world: &mut World) {
    let scene = DynamicScene::from_world(world);
    let registry = world.resource::<AppTypeRegistry>().read();
    let serialized = scene.serialize(&registry).unwrap();
    std::fs::write("save.scn.ron", serialized).unwrap();
}

DynamicScene::from_world walks every entity, asks the type registry which components are reflectable, and produces a serializable representation. Components without #[derive(Reflect)] (or not registered with the type registry) are silently skipped — this is the most common gotcha when scenes "lose" data.

BSN

BSN (Bevy Scene Notation) is Bevy's bespoke scene format introduced in 0.18. It's structurally similar to RON but specialized for component composition:

Spawn Player {
    Transform { translation: (0, 1, 0) },
    Health { current: 100, max: 100 },
    children: [
        Spawn Sword {
            Transform { translation: (0.5, 0, 0) },
            Damage(25)
        }
    ]
}

Parser implementation in crates/bevy_world_serialization/src/bsn/. The format is still evolving as of 0.19-dev; consult the in-tree examples and tests for the latest syntax.

DynamicScene vs Scene

Scene DynamicScene
Strictly typed (each entity has a fixed component set). Loose: entities can carry arbitrary reflected component blobs.
Spawned with SceneRoot. Spawned with DynamicSceneRoot.
Used by glTF imports. Used for save/load and editor tooling.

Most user code uses DynamicScene for save games and Scene for level data baked from glTF.

Type registration

A type that should appear in scenes needs:

  1. #[derive(Reflect)].
  2. #[reflect(Component)] (or #[reflect(Resource)]).
  3. Registration with the world: app.register_type::<MyComponent>().

The third step is automatic when the reflect_auto_register feature is on (it's in default_app). Without it, you have to register every type by hand. Forgetting to register is the #1 reason a scene "loses" data.

Filtering

SceneFilter (in bevy_scene) controls which components serialize:

DynamicScene::from_world(world)
    .filter(SceneFilter::Allowlist(vec!["Transform", "MyComponent"]));

Useful for save games where you don't want to serialize transient runtime-only state.

Cross-references

See also

  • Assets — scenes are loaded as assets.
  • bevy_remote — the BRP serializes individual components via the same reflection path.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Scenes – Bevy wiki | Factory