#animation #bevy-plugin #sprite #bevy

bevy_spritesheet_animation

A Bevy plugin for animating sprites

10 releases (1 stable)

1.0.0 Nov 20, 2024
0.4.3 Nov 11, 2024
0.4.2 Sep 2, 2024
0.4.1 Aug 30, 2024
0.1.0 Apr 10, 2024

#86 in Game dev

Download history 2/week @ 2024-08-19 431/week @ 2024-08-26 198/week @ 2024-09-02 9/week @ 2024-09-09 21/week @ 2024-09-16 51/week @ 2024-09-23 37/week @ 2024-09-30 18/week @ 2024-10-07 15/week @ 2024-10-14 7/week @ 2024-10-21 8/week @ 2024-10-28 75/week @ 2024-11-04 154/week @ 2024-11-11 189/week @ 2024-11-18 126/week @ 2024-11-25 113/week @ 2024-12-02

584 downloads per month

MIT license

170KB
2K SLoC

Crates.io Docs Build License

bevy_spritesheet_animation is a Bevy plugin for animating sprites that are backed by spritesheets.

An animated character walking from the left to the right and shooting their gun

Features

[!TIP] This crate is under active development. Please regularly check the CHANGELOG for recent changes.

Quick start

  1. Add the SpritesheetAnimationPlugin to your app
  2. Use the AnimationLibrary resource to create new clips and animations
  3. Add a SpritesheetAnimation component to your entity
use bevy::prelude::*;
use bevy_spritesheet_animation::prelude::*;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        // Add the plugin to enable animations.
        // This makes the AnimationLibrary resource available to your systems.
        .add_plugins(SpritesheetAnimationPlugin::default())
        .add_systems(Startup, setup)
        .run();
}

fn setup(
    mut commands: Commands,
    mut library: ResMut<AnimationLibrary>,
    mut atlas_layouts: ResMut<Assets<TextureAtlasLayout>>,
    assets: Res<AssetServer>,
) {
    // Create a clip

    let spritesheet = Spritesheet::new(8, 8);

    let clip = Clip::from_frames(spritesheet.row(3))
        .with_duration(AnimationDuration::PerFrame(150));

    let clip_id = library.register_clip(clip);

    // Add this clip to an animation

    let animation = Animation::from_clip(clip_id)
        .with_repetitions(AnimationRepetition::Times(3));

    let animation_id = library.register_animation(animation);

    // This is a simple animation with a single clip but we can create more sophisticated
    // animations with multiple clips, each one having different parameters.
    //
    // See the `composition` example for more details.

    // Spawn a sprite using Bevy's built-in SpriteBundle

    let texture = assets.load("character.png");

    let layout = atlas_layouts.add(spritesheet.atlas_layout(96, 96));

    commands.spawn((
        SpriteBundle {
            texture,
            ..default()
        },
        TextureAtlas {
            layout,
            ..default()
        },
        // Add a SpritesheetAnimation component that references our newly created animation
        SpritesheetAnimation::from_id(animation_id),
    ));

    commands.spawn(Camera2dBundle::default());
}

Overview

Clips

A clip is a sequence of frames.

It is the most basic building block for creating animations. Simple animations may contain a single clip while more complex animations may contain a sequence of clips.

Parameters like duration, repetitions, direction and easing can be specified.

Use the AnimationLibrary resource to register new clips.

The clip can then be referenced in any number of animations.

fn setup(mut commands: Commands, mut library: ResMut<AnimationLibrary>) {

    // Create a clip that uses some frames from a spritesheet

    let spritesheet = Spritesheet::new(8, 8);

    let clip = Clip::from_frames(spritesheet.column(2))
        .with_duration(AnimationDuration::PerRepetition(1500))
        .with_repetitions(5);

    let clip_id = library.register_clip(clip);

    // Add this clip to an animation

    let animation = Animation::from_clip(clip_id);

    // ...
}

Animations

In its simplest form, an animation is composed of a single clip that loops endlessly.

However, you're free to compose more sophisticated animations by chaining multiple clips and tuning their parameters.

Use the AnimationLibrary resource to register new animation.

The animation can then be referenced in any number of SpritesheetAnimation component.

fn setup(mut commands: Commands, mut library: ResMut<AnimationLibrary>) {

    // ... omitted: create and register a few clips

    let animation = Animation::from_clips([
            running_clip_id,
            shooting_clip_id,
            running_clip_id
        ])
        .with_duration(AnimationDuration::PerRepetition(3000))
        .with_direction(Animation::Direction::Backwards);

    let animation_id = library.register_animation(animation);

    // Assign the animation to an entity with the SpritesheetAnimation component

    // ... omitted: load the sprite's texture and create an atlas

    commands.spawn((
        SpriteBundle {
            texture,
            ..default()
        },
        TextureAtlas {
            layout,
            ..default()
        },
        SpritesheetAnimation::from_id(animation_id),
    ));
}

Think of clips and animations as assets!

Clips and animations should be created once. You can then assign them to many entities.

❌ BAD

Do not create the same clip/animation for each entity that plays it.

fn spawn_enemies(mut commands: Commands, mut library: ResMut<AnimationLibrary>) {

    // Creating identical animations gives more work to the plugin and degrades performance!

    for _ in 0..100 {
        let clip = Clip::from_frames([1, 2, 3]);
        let clip_id = library.register_clip(clip);

        let animation = Animation::from_clip(clip_id);
        let animation_id = library.register_animation();

        commands.spawn((
            SpriteBundle { /* .... */ },
            SpritesheetAnimation::from_id(animation_id),
        ));
    }
}

👍 GOOD

Instead, create clips/animations once and then reference them when needed.

For instance, you can create all your animations in a setup system, give them unique names and then assign them to entities at a later stage.

fn create_animation(mut library: ResMut<AnimationLibrary>) {
    let clip = Clip::from_frames([1, 2, 3]);
    let clip_id = library.register_clip(clip);

    let animation = Animation::from_clip(clip_id);
    let animation_id = library.register_animation();

    // Here, we name the animation to make it easy to retrieve it in other systems.
    //
    // Alternatively, you may prefer to store the animation ID yourself.
    // For instance, in a Bevy Resource that contains the IDs of all your clips/animations.
    //
    // Something like:
    //
    // #[derive(Resource)]
    // struct GameAnimations {
    //     enemy_running: AnimationId,
    //     enemy_firing: AnimationId,
    //     ... and so on ...
    // }

    library.name_animation(animation_id, "enemy running");
}

fn spawn_enemies(mut commands: Commands, library: Res<AnimationLibrary>) {

    // Retrieve our animation and assign it to many entities

    if let Some(animation_id) = libray.animation_with_name("enemy running") {
        for _ in 0..100 {
            commands.spawn((
                SpriteBundle { /* .... */ },
                SpritesheetAnimation::from_id(animation_id),
            ));
        }
    }
}

3D sprites

A dozen of 3D sprites moving in 3D space

This crate also makes it easy to integrate 3D sprites into your games, which is not supported by Bevy out of the box.

Sprite3dBundle contains all the necesary components to enable 3D sprites. Use Sprite3dBuilder to easily create one of those.

Animating a 3D sprite is the same as animating 2D sprites: simply attach a SpritesheetAnimation component to your entity.

fn spawn_character(mut commands: Commands, mut library: ResMut<AnimationLibrary>) {

    // ...

    let animation_id = library.register_animation(animation);

    commands.spawn((
        Sprite3dBuilder::from_image(texture.clone())
            .with_atlas(atlas_layout)
            .with_anchor(Anchor::BottomRight)
            .build(),
        SpritesheetAnimation::from_id(animation_id)
    ));
}

More examples

For more examples, browse the examples/ directory.

Example Description
basic Shows how to create an animated sprite
3d Shows how to create 3D sprites
progress Shows how to control an animation
composition Shows how to create an animation with multiple clips
parameters Shows the effect of each animation parameter
character Shows how to create a controllable character with multiple animations
events Shows how to react to animations reaching points of interest with events
headless Shows how to setup a headless Bevy app without rendering
stress Stress test with thousands of animated sprites (either 2D or 3D)

Compatibility

bevy bevy_spritesheet_animation
0.14 0.2.0
0.13 0.1.0

Credits

  • The character spritesheet used for the examples is CC0 from thekingphoenix

Dependencies

~39–76MB
~1.5M SLoC