Bevy asset scenes
When to use
Load this when content comes from disk or a scene description should spawn entities. AssetServer loads files. bsn! describes a Scene that spawn_scene or queue_spawn_scene turns into entities.
ECS shape
- Assets:
AssetServeris the resource that loads from disk.AssetServer::loadreturns aHandleand does not reload while a strong handle still exists. Loaded data lives inAssets<T>.AssetServer::is_loaded_with_dependenciesis true when the asset and its dependencies are ready.LoadStateis the status type named on that page. - Handles: strong handles are reference counted. Dropping the last strong handle removes the asset. Weak handles do not keep it alive.
Assets::addinserts a procedural asset and does not deduplicate. - Hot reload: the
file_watcherCargo feature, andAssetPlugin'swatch_for_changes_overridefield, are the toggles named on the asset index. - Scenes:
bevy::scenedescribes aScene(entity, components, children, assets), built with thebsn!macro.World::spawn_sceneandCommands::spawn_scenespawn immediately and error if asset dependencies are not loaded.World::queue_spawn_sceneandCommands::queue_spawn_scenewait, then spawn in theSpawnSceneschedule betweenUpdateandPostUpdate. - Hierarchy in a scene:
Children [ ... ]insidebsn!. Separate sibling entities with--.#Nameassigns aNameinside that macro's scope.
Asset choice
| Need | Do this |
|---|---|
| Load a file | AssetServer::load returns a Handle |
| Wait until ready | is_loaded_with_dependencies before a state change |
| Spawn a scene now | spawn_scene only after dependencies are loaded |
| Spawn when ready | queue_spawn_scene (SpawnScene sits between Update and PostUpdate) |
| Children in that scene | Children inside bsn! |
| Change one entity's mesh | Swap the Handle on the component that stores it |
.bsn files on disk are still ahead of the shipping scene API. Author scenes with bsn!. Free a previous level by despawning its root, then queue the next scene. Register a custom AssetLoader with App::register_asset_loader once that loader type exists.
[dependencies]
bevy = { version = "0.20", features = ["file_watcher"] }
#[derive(Resource)]
struct PropMesh(Handle<Mesh>);
fn begin_load(mut commands: Commands, assets: Res<AssetServer>) {
let handle: Handle<Mesh> = assets.load("models/crate.glb#Mesh0/Primitive0");
commands.insert_resource(PropMesh(handle));
}
fn mesh_is_ready(assets: Res<AssetServer>, mesh: Option<Res<PropMesh>>) -> bool {
match mesh {
Some(mesh) => assets.is_loaded_with_dependencies(&mesh.0),
None => false,
}
}
fn store_generated_material(
mut materials: ResMut<Assets<StandardMaterial>>,
mut commands: Commands,
) {
let handle = materials.add(StandardMaterial {
base_color: Color::srgb(0.8, 0.8, 0.82),
..default()
});
commands.insert_resource(GeneratedMaterial(handle));
}
#[derive(Resource)]
struct GeneratedMaterial(Handle<StandardMaterial>);
#[derive(Component, Default, Clone)]
struct CrateProp;
#[derive(Component, Default, Clone)]
struct Lid;
#[derive(Component, Default, Clone)]
struct LevelRoot;
fn spawn_crate(mut commands: Commands) {
commands.spawn_scene(bsn! {
#SupplyCrate
CrateProp
Children [
Lid
]
});
}
fn queue_level(mut commands: Commands) {
commands.queue_spawn_scene(bsn! {
:"levels/yard.bsn"
LevelRoot
});
}
fn replace_level(mut commands: Commands, roots: Query<Entity, With<LevelRoot>>) {
for entity in &roots {
commands.entity(entity).despawn();
}
commands.queue_spawn_scene(bsn! {
LevelRoot
});
}
Expert Knowledge Delta
Target: Bevy 0.20+.
Core ECS types: Component, Resource, Query, Commands, Startup, Update, Fixed / FixedMain (bevy::time), .chain(), Plugin, DefaultPlugins, MinimalPlugins. Messages and observers are an ownership slot. See bevy-events-messaging for their APIs. Systems run in parallel unless .chain() orders the ones that conflict.
Who owns state
| Piece | Owner | Who may mutate | Schedule |
|---|---|---|---|
| Data on one entity | Component |
One system via Query |
Update or FixedMain |
| One value for the whole app | Resource |
One system | Startup to insert, then the schedule that reads it |
| Spawn | Commands |
The spawning system | Startup for the initial world; later schedules for runtime spawn |
| Order that must not race | .chain() |
The chained systems only | Same schedule |
| Plugin boundary | Plugin |
build registers systems and resources |
Before the app runs |
Borrow rule: two systems that both mutably query the same component must be ordered (.chain()) or split so only one writes. Unrelated systems stay parallel. That is the ECS page rule, not a guess at a system-set API.
Decision
| If you need | Do this | Do not |
|---|---|---|
| Per-entity data | Component + Query |
One script that owns the whole actor |
| App-wide config or clock | Resource |
Copy the value onto every entity |
| Stable simulation step | FixedMain and the Fixed clock |
Tie simulation to raw frame Update |
| Startup spawn | Commands in Startup |
Spawn from a render system |
| Cross-system notice that is not shared state | Message or observer slot in bevy-events-messaging |
A new event type invented in this file |
| An API outside the documented set | Check the official docs first | A made-up crate or API |
Load references/ecs-ownership.md for the ownership table.
NEVER
-
Do not pin
/0.20.0/in docs URLs. WHY: links stay onlatest; the 0.20 target is stated once in prose. -
Do not mutably query the same component from two unordered systems. WHY: the schedule runs systems in parallel unless
.chain()orders them. -
Do not invent a physics, navigation, networking, or XR crate here. WHY: this skill only names documented Bevy crates and modules.
-
No packed-scene or resource-loader types from another engine. WHY: readiness is
is_loaded_with_dependencies, and spawn isspawn_sceneorqueue_spawn_scene, not a node instance. -
No scene format this file did not open. WHY: Bevy code stays idiomatic ECS and uses only documented Bevy APIs.
-
No third-party scene crates. WHY: Bevy code stays idiomatic ECS and uses only documented Bevy APIs.
Reference
The library pin on bevy-master stays 0.20.0. Load references/ecs-ownership.md when a handle is a resource and a scene root is a component. Do not load it to write a custom loader body.
Official Documentation
- Assets - Official asset catalog.
AssetServer,Handle, andAssets<T>are the load path.Scene,bsn!,spawn_scene,queue_spawn_scene, andSpawnSceneare the spawn path. - Examples - Example tree on the
latestbranch.
Related Skills
Prerequisites
- bevy-master - Who owns which piece of state.
- bevy-project-foundations - Cargo features, including asset watcher flags.
- bevy-state-machines - The asset page points at app states for load gates.
Complements
- bevy-audio-systems - Clips loaded through
AssetServer. - bevy-save-load - Player saves, separate from scene spawn.
Downstream / consumers
- bevy-camera-systems - Camera entities a scene may spawn.
Master
- bevy-master - Bevy router.
