bevy_spritesheet_animation is a Bevy plugin for animating sprites that are backed by spritesheets.
- Animate 2D and 3D sprites! π
- A single Bevy component to add to your entities to play animations.
- Tunable parameters: duration, repetitions, direction, easing.
- Composable animations from multiple clips.
- Events to react to animations ending or reaching specific points.
- A convenient API to select frames in spritesheets.
- Add the SpritesheetAnimationPlugin to your app
- Use the SpritesheetLibrary resource to create new clips and animations
- Add a SpritesheetAnimation component to your entity
fn main() {
App::new()
.add_plugins(DefaultPlugins)
// Add the plugin to enable animations.
// This makes the SpritesheetLibrary resource available to your systems.
.add_plugins(SpritesheetAnimationPlugin)
.add_systems(Startup, setup)
.run();
}
fn setup(
mut commands: Commands,
mut library: ResMut<SpritesheetLibrary>,
mut atlas_layouts: ResMut<Assets<TextureAtlasLayout>>,
assets: Res<AssetServer>,
) {
// Create an animation
let clip_id = library.new_clip(|clip| {
// You can configure this clip here (duration, number of repetitions, etc...)
// This clip will use all the frames in row 3 of the spritesheet
clip.push_frame_indices(Spritesheet::new(8, 8).row(3));
});
let animation_id = library.new_animation(|animation| {
// You can configure this animation here (duration, number of repetitions, etc...)
animation.add_stage(clip_id.into());
// 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(TextureAtlasLayout::from_grid(
UVec2::new(96, 96),
8,
8,
None,
None,
));
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());
}
An animation clip is a reusable sequence of frames.
It is the most basic building block for creating animations.
Use the SpritesheetLibrary resource to create and configure a new clip.
The clip can then be referenced in any number of animations.
fn setup(mut commands: Commands, mut library: ResMut<SpritesheetLibrary>) {
// Create a clip that uses some frames from a spritesheet
let clip_id = library.new_clip(|clip| {
clip
.push_frame_indices(Spritesheet::new(8, 8).column(2))
.set_default_duration(AnimationDuration::PerCycle(1500))
.set_default_repeat(5);
});
// Add this clip to an animation
let animation_id = library.new_animation(|animation| {
animation.add_stage(clip_id.into());
});
// ... Assign the animation to an entity with the SpritesheetAnimation component ...
}
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 by tuning the animation parameters.
Use the SpritesheetLibrary resource to create a new animation.
The animation can then be referenced in any number of SpritesheetAnimation component.
fn setup(mut commands: Commands, mut library: ResMut<SpritesheetLibrary>) {
// ...
let animation_id = library.new_animation(|animation| {
let mut stage1 = AnimationStage::from_clip(some_clip_id);
stage1
.set_repeat(5)
.set_easing(Easing::InOut(EasingVariety::Quadratic));
let mut stage2 = AnimationStage::from_clip(another_clip_id);
stage2
.set_duration(AnimationDuration::PerFrame(120))
.set_direction(Animation::Direction::Backwards);
animation
.add_stage(stage1)
.add_stage(stage2)
.set_duration(AnimationDuration::PerCycle(1000))
.set_direction(Animation::Direction::PingPong);
});
// ... Assign the animation to an entity with the SpritesheetAnimation component ...
}
Clips and animations should be created once. You can then assign them to many entities.
You should not create the same clip/animation for each entity that plays it.
fn spawn_enemies(mut commands: Commands, mut library: ResMut<SpritesheetLibrary>) {
// Creating identical animations gives more work to the plugin and degrades performance!
for _ in 0..100 {
let clip_id = library.new_clip(|clip| { /* ... */ });
let animation_id = library.new_animation(|animation| { /* ... */ });
commands.spawn((
SpriteBundle { /* .... */ },
SpritesheetAnimation::from_id(animation_id),
));
}
}
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<SpritesheetLibrary>) {
let clip_id = library.new_clip(|clip| { /* ... */ });
let animation_id = library.new_animation(|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<SpritesheetLibrary>) {
// 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),
));
}
}
}
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<SpritesheetLibrary>) {
let animation_id = library.new_animation(|animation| { /* ... */ });
commands.spawn((
Sprite3dBuilder::from_image(texture.clone())
.with_atlas(atlas_layout_handle)
.with_anchor(Anchor::BottomRight)
.build(),
SpritesheetAnimation::from_id(animation_id)
));
}
For more examples, browse the examples/ directory.
Example | Description |
---|---|
basic | Shows how to create an animated sprite |
3d | Shows how to create 3D sprites |
composition | Shows how to create an animation with multiple stages |
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 |
stress | Stress test with thousands of animated sprites |
bevy | bevy_spritesheet_animation |
---|---|
0.14 | 0.2.0 |
0.13 | 0.1.0 |
- The character spritesheet used for the examples is CC0 from thekingphoenix