--- name: bevy-assets description: Reference for loading, managing, and tracking assets in Bevy — AssetServer, handles, loading states, events, custom loaders, and hot reloading. metadata: crate: bevy_asset bevy: "0.19" --- ## Core resources - `Assets` — stores loaded assets of each type - `AssetServer` — loads assets from files asynchronously ```rust fn load(mut image: ResMut, server: Res) { image.0 = server.load("images/foo.png"); } ``` By default Bevy looks in the `assets` folder. The base directory depends on how the app is run: `cargo run` sets `CARGO_MANIFEST_DIR` to the crate root, while a standalone executable falls back to the directory containing the executable. Override with the `file_path` field on `AssetPlugin`, or the `BEVY_ASSET_ROOT` / `CARGO_MANIFEST_DIR` environment variables. ## Creating assets procedurally ```rust fn spawn(mut meshes: ResMut>, mut materials: ResMut>) { let mesh = meshes.add(Circle::new(50.)); let material = materials.add(Color::BLACK); commands.spawn((Mesh2d(mesh), MeshMaterial2d(material))); } ``` ## Handle types by extension | Extension | Handle | |-----------|--------| | `ttf`, `otf` | `Font` | | `png`, `jpg`, `webp`, `ktx2`, `bmp`, `gif`, `tga`, `tiff` | `Image` | | `mp3`, `flac`, `ogg`, `wav` | `AudioSource` | | `gltf`, `glb` | `Gltf` (use `GltfAssetLabel::Scene(0)` for a scene) | | `scn`, `scn.ron` | `DynamicWorld` | ## Loading 3D models ```rust let scene: Handle = server.load(GltfAssetLabel::Scene(0).from_asset("models/car.glb")); commands.spawn((SceneRoot(scene), Transform::default())); ``` ## Loading state ```rust use bevy::asset::LoadState; match server.get_load_state(&handle) { Some(LoadState::Loaded) => {}, Some(LoadState::Loading) => {}, Some(LoadState::Failed(_)) => {}, _ => {} } ``` ## Asset events ```rust use bevy::asset::AssetEvent; fn react(mut events: MessageReader>) { for event in events.read() { match event { AssetEvent::Added { id } => {}, AssetEvent::LoadedWithDependencies { id } => {}, AssetEvent::Modified { id } => {}, AssetEvent::Removed { id } => {}, AssetEvent::Unused { id } => {}, } } } ``` ## Loading a folder ```rust use bevy::asset::LoadedFolder; let _folder: Handle = server.load_folder("models/monkey"); ``` ## Custom asset source ```rust app.register_asset_source("custom", AssetSourceBuilder::platform_default("assets/custom", None)); // Then: server.load("custom://file.png") ``` ## Custom asset loader ```rust #[derive(Asset, TypePath)] struct TextFile { content: String } impl AssetLoader for TextFile { type Asset = TextFile; type Settings = (); type Error = std::io::Error; async fn load(&self, reader: &mut dyn Reader, _settings: &(), _ctx: &mut LoadContext<'_>) -> Result { let mut content = String::new(); reader.read_to_string(&mut content).await?; Ok(TextFile { content }) } fn extensions(&self) -> &[&str] { &["txt"] } } // Then: app.init_asset::() ``` ## Hot reloading Enable via feature `file_watcher` in Cargo.toml, or: ```rust app.add_plugins(DefaultPlugins.set(AssetPlugin { watch_for_changes_override: Some(true), ..default() })); ``` ## Handle lifecycle - Cloning a `Handle` creates a strong reference - Assets are unloaded when all strong handles are dropped - `Handle::Weak` does not keep the asset alive