NiklasEi / bevy_asset_loader

Bevy plugin helping with asset loading and organization
Apache License 2.0
452 stars 53 forks source link

Use enum for key in asset map #140

Closed mpowell90 closed 11 months ago

mpowell90 commented 1 year ago

Hi @NiklasEi, I'm interested in using your library as it seems to do exactly what I need.

My current asset resource implementation uses an enum as the key of a hashmap, which I would like to retain as the enum variants tie in with my system design. I'd like to achieve something like the following, is it possible?

#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum MilitaryUnitType {
    Basic,
    Defensive,
    Offensive,
    Elite,
}

#[derive(AssetCollection, Debug, Deref, DerefMut, Resource)]
pub struct MilitaryUnitAssets(pub HashMap<MilitaryUnitType, Handle<Scene>>);

Many thanks!

NiklasEi commented 1 year ago

It is possible, but requires a bit more code. The enum needs to be manually mapped, but I am sure there is a lot of potential for improvement using macros to auto generate the conversion from enum variant to file name and creating a list of all enum variants (fyi the States derive macro from Bevy also does that).

Please ignore that I used images instead of scenes. I just wanted to test this and have no scene files lying around.

use bevy::prelude::*;
use bevy::utils::HashMap;
use bevy_asset_loader::prelude::*;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_state::<MyStates>()
        .add_loading_state(
            LoadingState::new(MyStates::AssetLoading).continue_to_state(MyStates::Next),
        )
        .add_collection_to_loading_state::<_, UnitAssets>(MyStates::AssetLoading)
        .init_resource_after_loading_state::<_, MilitaryUnitAssets>(MyStates::AssetLoading)
        .insert_resource(Msaa::Off)
        .add_system(draw.in_schedule(OnEnter(MyStates::Next)))
        .run();
}

#[derive(AssetCollection, Resource)]
struct UnitAssets {
    #[asset(path = "images", collection(typed, mapped))]
    unit_folder: HashMap<String, Handle<Image>>,
}

#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum MilitaryUnitType {
    Basic,
    Defensive,
    Offensive,
    Elite,
}

impl MilitaryUnitType {
    fn file_path(&self) -> &'static str{
        match self {
            MilitaryUnitType::Basic => "background.png",
            MilitaryUnitType::Defensive => "tree.png",
            MilitaryUnitType::Offensive => "zombie.png",
            MilitaryUnitType::Elite => "player.png"
        }
    }
}

#[derive(Debug, Deref, DerefMut, Resource)]
pub struct MilitaryUnitAssets(pub HashMap<MilitaryUnitType, Handle<Image>>);

impl FromWorld for MilitaryUnitAssets {
    fn from_world(world: &mut World) -> Self {
        let units = world
            .get_resource_mut::<UnitAssets>()
            .expect("Failed to get UnitAssets");
        let mut map: HashMap<MilitaryUnitType, Handle<Image>> = default();
        for unit_type in [MilitaryUnitType::Basic, MilitaryUnitType::Elite, MilitaryUnitType::Defensive, MilitaryUnitType::Offensive] {
            let path = format!("images/{}", unit_type.file_path());
            let unit = units.unit_folder.get(&path).unwrap_or_else(|| panic!("Asset file for {:?} is missing at {}", unit_type, path));
            map.insert(unit_type, unit.clone());
        }

        MilitaryUnitAssets(map)
    }
}

fn draw(
    combined_texture: Res<MilitaryUnitAssets>,
) {
    println!("{:?}", combined_texture);
}

#[derive(Clone, Eq, PartialEq, Debug, Hash, Default, States)]
enum MyStates {
    #[default]
    AssetLoading,
    Next,
}
mpowell90 commented 11 months ago

@NiklasEi thanks for the code example, this has helped achieve what I needed!