💾 Archived View for gem.soaringotter.xyz › gemfeed › 2024-12-01-adventures-of-bevy.gmi captured on 2024-12-17 at 09:35:00. Gemini links have been rewritten to link to archived content

View Raw

More Information

-=-=-=-=-=-=-

Adventures with Bevy

What is Bevy?

Bevy is newer game engine written in Rust. It isn't like most other game engines like Unreal or Godot where you have a GUI and is more similar to Mono in that it is code only. There are some plugins that add GUIs but they aren't required and I haven't used them before. Bevy follows the ECS architecture and I love it. You create components that contain some kind of data, like health, and then you use these components to make entities, or most things you interact with in a game. Entities are just collections of components and nothing more. Finally, you have systems which are just functions that act on components. So you can have a 'check_dead' system that gets all entities that have the 'Health' component and checks the health of that entity.

Why am I using Bevy?

There isn't a particular reason. I have been trying to figure out what to fill my free time with. I have a good friend who is just getting into programming and game dev and is currently trying to learn Unreal Engine. For fun, I challenged him to a two week game jam to get out of the research forever hole and start actually implementing something. I wanted to even the playing field a little bit, as well as learn a cool new framework. We decided to have the general theme of 'Platformer'. The game jam ends next week and I have a lot of work left to do.

My thoughts

The game I am doing is a Celeste/Chained Together kind of mix. The concept is basically Celeste and instead of climbing a mountain, you are trying to escape Hell. I also want to reiterate, this is a two week game jam and I do not plan on getting this finished and don't plan on publishing the game, it is more for fun and learning. I have been really enjoying it so far. I absolutely love the ECS system and how Bevy organizes all the code in plugins and bundles. It is very intuitive and can make some really clean code. However, not everything is straight forward. By far, the hardest part was animations, mainly because I was too lazy to actually create my own animation system. Bevy has a built in TextureAtlas that allows you to use spritesheets. The first problem was, the sprite that I got online, was split into multiple spritesheets for each animation. Theree can only be a single TextureAtlas per entity. One work around I saw was to have multiple entities, one for each animation. I really do not like this idea. It is redundent, unnecessary, you have to manage multiple entities, which, even with Bundles, is still always bad, and it is inefficient. Luckily, I found a way that you can use a TextureAtlasBuilder to combine multiple spritesheets into one. I can then use this as a single atlas to hold all my animations.

fn load_textures(mut commands: Commands, asset_server: Res<AssetServer>) {
    commands.insert_resource(SpriteFolder(
        asset_server.load_folder("textures/characters/player/"),
    ));
}

pub fn setup_texture_atlas(
    sprite_handles: Res<SpriteFolder>,
    loaded_folders: Res<Assets<LoadedFolder>>,
    mut textures: ResMut<Assets<Image>>,
    mut texture_atlases: ResMut<Assets<TextureAtlasLayout>>,
    mut library: ResMut<AnimationLibrary>,
) -> (Handle<TextureAtlasLayout>, Handle<Image>, AnimationId) {
    let loaded_folder = loaded_folders.get(&sprite_handles.0).unwrap();

    let mut texture_atlas_builder = TextureAtlasBuilder::default();

    for handle in loaded_folder.handles.iter() {
        let id = handle.id().typed_unchecked::<Image>();
        let Some(texture) = textures.get(id) else {
            warn!(
                "{:?} did not resolve to an `Image` asset.",
                handle.path().unwrap()
            );
            continue;
        };

        texture_atlas_builder.add_texture(Some(id), texture);
    }

    let (_, texture) = texture_atlas_builder.build().unwrap();

    let layout = TextureAtlasLayout::from_grid(
        UVec2::new(128, 128),
        16,
        5,
        None,
        Some(UVec2::new(0, 32)),
    );
    let layout_handle = texture_atlases.add(layout);
    let texture = textures.add(texture);

The next issue was finding an easy way to manage animations without creating a whole complex system. Luckily, I found crate to handle this for me [1]. It allows me to define specific squares in the spritesheet for an animation, give it a name, set the speed of the animation and how many times it loops. And most importantly, allows me to change the animation by simply doing

animation.switch(idle_animation);

After getting all this setup, I was able to quickly and easily implement falling and jumping and started making progress again.

Going forward

I have one more week to go, a lot to do, and a couple ideas on what to implement. I'm excited to be learning more about Bevy and have been enjoying the project so far.

Links

Animation Crate