NiklasEi / bevy_asset_loader

Bevy plugin helping with asset loading and organization
Apache License 2.0
474 stars 52 forks source link

Loading TextureAtlas as a dynamic asset #39

Closed thebluefish closed 2 years ago

thebluefish commented 2 years ago

I would like to configure which texture atlas to use at runtime. Each is currently defined in their own .assets resource like:

TextureAtlas (
    path: "textures/tilesets/standard_128.png",
    tile_size_x: 96.0,
    tile_size_y: 128,
    columns: 10,
    rows: 4
)

However I cannot seem to load these as a normal dynamic asset. I'm attempting to do the following:

use bevy::prelude::*;
use bevy_asset_loader::{AssetCollection, AssetLoader, DynamicAsset, DynamicAssets};

fn main() {
    let mut app = App::new();

    app.add_state(MyStates::Setup)
        .insert_resource(Msaa { samples: 1 })
        .add_plugins(DefaultPlugins)
        .add_system_set(
            SystemSet::on_enter(MyStates::Setup)
                .with_system(setup)
        )
        .add_system_set(
            SystemSet::on_enter(MyStates::Next)
                .with_system(spawn)
        )
    ;

    AssetLoader::new(MyStates::AssetLoading)
        .continue_to_state(MyStates::Next)
        .with_collection::<MyAssets>()
        .build(&mut app);

    app.run();
}

#[derive(Clone, Eq, PartialEq, Debug, Hash)]
enum MyStates {
    Setup,
    AssetLoading,
    Next,
}

#[derive(AssetCollection)]
struct MyAssets {
    #[asset(key = "tileset")]
    tileset: Handle<TextureAtlas>,
}

#[derive(Component, Deref, DerefMut)]
struct AnimationTimer(Timer);

fn setup(
    mut state: ResMut<State<MyStates>>,
    mut asset_keys: ResMut<DynamicAssets>,
) {
    asset_keys.register_asset(
        "tileset",
        DynamicAsset::File {
            path: "textures/tilesets/standard_128.assets".to_owned(),
        },
    );
    state
        .set(MyStates::AssetLoading)
        .expect("Failed to change state");
}

fn spawn(mut commands: Commands, assets: Res<MyAssets>) {
    commands.spawn_bundle(OrthographicCameraBundle::new_2d());

    commands
        .spawn_bundle(SpriteSheetBundle {
            texture_atlas: assets.tileset.clone(),
            ..default()
        })
        .insert(AnimationTimer(Timer::from_seconds(1., true)));
}
WARN bevy_asset::asset_server: encountered an error while loading an asset: 1:1: Expected struct

However it works fine when I define it as part of a dynamic collection:

.with_asset_collection_file("textures/tilesets/tileset.assets")
({
    "tileset": TextureAtlas (
        path: "textures/tilesets/standard_128.png",
        tile_size_x: 96.0,
        tile_size_y: 128,
        columns: 10,
        rows: 4
    )
})

The dynamic loading stops throwing a warning when I use the same format as a collection file, but fails to properly load the TextureAtlas.

NiklasEi commented 2 years ago

You can either manually configure the dynamic TextureAtlas, or define it in a .assets file.

Currently, in your first code example, you are trying to define the .assets file as a file asset on the key tileset, which is supposed to be the TextureAtlas. For this kind of manual registration of the key, you would need to do this instead:

    asset_keys.register_asset(
        "tileset",
        DynamicAsset::TextureAtlas {
            path: "textures/tilesets/standard_128.png".to_owned(),
            tile_size_x: 96.,
            tile_size_y: 128.,
            columns: 10,
            rows: 4,
            padding_x: None,
            padding_y: None
        },
    );

This would be the point in code where you can change the file path based on which one you want to be loaded.

NiklasEi commented 2 years ago

On current main, you can now also register .assets files in a system. You could for example run the following system before the loading state MyStates::AssetLoading

fn setup_loading_state(mut dynamic_asset_collections: ResMut<DynamicAssetCollections<MyStates>>) {
    dynamic_asset_collections
        .files
        .get_mut(&MyStates::AssetLoading)
        .unwrap()
        .push("textures/tilesets/standard_128.assets".to_owned());
}

This would then replace .with_asset_collection_file("textures/tilesets/tileset.assets") and allow you to decide which .assets file to load at run-time.

NiklasEi commented 2 years ago

I will close this for now, because I think this was more of a misunderstanding about how dynamic assets should work.

If you still run into issues or have suggestions on how I could make the plugin better, don't hesitate to reopen or start a new issue.