Search code examples
rustasset-pipelineassetsbevy

Access bevy asset right after loading via AssetServer


Is it possible to access a bevy asset right after it was loaded from the AssetServer?

I've read actual loading happens in the background but couldn't find in either official documentation or the bevy cheat book if it is possible to wait for the actual loading to happen inside the same function?

Example of what I'm trying to do

fn f(asset_server: Res<AssetServer>, image_assets: Res<Assets<Image>>,) {
  let image: Handle<Image> = assert_server.load("img.png");

  // wait for `image` to be loaded...
  // NOTE: that this doesn't work - it just goes into an infinite loop
  while asset_server.get_load_state(&image) != LoadState::Loaded { }


  let image_asset = image_assets.get(&image).unwrap();
}

The reason I need this is to check some of the image's data, for example it's size.


Solution

  • As you probably shouldn't be halting the function for the asynchronous load of the file. Maybe you can create some Entity with the handle. You could have another system running a query for this entity and consume the entity when the handle shows the image is loaded.

    Looking it up this appears to be a suggested pattern in the unoffical bevy book.

    #[derive(Resource)]
    struct AssetsLoading(Vec<HandleUntyped>);
    
    fn setup(server: Res<AssetServer>, mut loading: ResMut<AssetsLoading>) {
        // we can have different asset types
        let font: Handle<Font> = server.load("my_font.ttf");
        let menu_bg: Handle<Image> = server.load("menu.png");
        let scene: Handle<Scene> = server.load("level01.gltf#Scene0");
    
        // add them all to our collection for tracking
        loading.0.push(font.clone_untyped());
        loading.0.push(menu_bg.clone_untyped());
        loading.0.push(scene.clone_untyped());
    }
    
    fn check_assets_ready(
        mut commands: Commands,
        server: Res<AssetServer>,
        loading: Res<AssetsLoading>
    ) {
        use bevy::asset::LoadState;
    
        match server.get_group_load_state(loading.0.iter().map(|h| h.id)) {
            LoadState::Failed => {
                // one of our assets had an error
            }
            LoadState::Loaded => {
                // all assets are now ready
    
                // this might be a good place to transition into your in-game state
    
                // remove the resource to drop the tracking handles
                commands.remove_resource::<AssetsLoading>();
                // (note: if you don't have any other handles to the assets
                // elsewhere, they will get unloaded after this)
            }
            _ => {
                // NotLoaded/Loading: not fully ready yet
            }
        }
    }