Skip to content

Commit 651a539

Browse files
committed
wip
1 parent 6dbbfa5 commit 651a539

37 files changed

Lines changed: 3040 additions & 2487 deletions

CHANGELOG.md

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,30 @@
22

33
## 5.0.0 - 2025-??-??
44

5+
This release comes with a significant rework of the high-level API that tightens the relationships between spritesheets, clips and animations.
6+
7+
It also removes the `AnimationLibrary` resource in favor of registering animations in `Assets<Animation>` like other Bevy assets.
8+
9+
As a consequence, it's no longer possible to name clips, animations or markers to retrieve them by name across systems.
10+
An alternative (and safer!) way to retrieve animation data is to save it in custom resources, as demonstrated in the examples (eg. `examples/character.rs`).
11+
If you still prefer to use names, you may want to define such a custom resource as -- for instance -- a `HashMap<String, Handle<Animation>>`.
12+
13+
Migrating to this new version should be pretty straightforward. Please check out the updated examples and documentation!
14+
15+
### Added
16+
17+
- `Spritesheet` can now create animation-ready Bevy Sprites and TextureAtlases to reduce boilerplate
18+
519
### Changed
620

7-
- ???
21+
- Animations are now exclusively created with `Spritesheet::create_animation()` which returns an `AnimationBuilder`
22+
- Clips are now exclusively created with `AnimationBuilder::start_clip()`/`AnimationBuilder::copy_clip()`
23+
- Rename `AnimationMarkerId` to `Marker`
24+
- Optimize playback TODO (x? speedup)
25+
26+
### Removed
27+
28+
- Remove the `AnimationLibrary` resource (you can now register new animations in `Assets<Animation>`)
829

930
## 4.0.0 - 2025-10-09
1031

@@ -159,7 +180,7 @@ To create a variant of a clip, just clone and reconfigure it before registering
159180

160181
### Fixed
161182

162-
- Switch `new_clip`/`new_animatio`n closures to FnMut to allow mutations
183+
- Switch `new_clip`/`new_animation` closures to FnMut to allow mutations
163184

164185
## 0.1.0 - 2024-04-10
165186

README.md

Lines changed: 145 additions & 150 deletions
Large diffs are not rendered by default.

TODO.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Make events optional
2+
3+
Events are always emitted, even when users never use them.
4+
This becomes costly when many sprites are spawned.
5+
Put them behind a feature flag or add a runtime option to toggle them.
6+
7+
# Add the current animation repetition to events
8+
9+
We have it available so might as well use it.
10+
11+
# Optimize playback
12+
13+
There's currently a lot of allocations during playback (esp. for events).
14+
Use a single storage for all sprites instead of allocating one at each sprite update.

examples/3d.rs

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
// This example shows how to create 3D sprites.
22

3-
#[path = "./common/mod.rs"]
4-
pub mod common;
5-
63
use bevy::{prelude::*, sprite::Anchor};
74
use bevy_spritesheet_animation::prelude::*;
85
use rand::{Rng, seq::IndexedRandom as _};
@@ -20,9 +17,9 @@ fn main() {
2017

2118
fn spawn_sprites(
2219
mut commands: Commands,
23-
mut atlas_layouts: ResMut<Assets<TextureAtlasLayout>>,
24-
mut animations: ResMut<Assets<Animation>>,
2520
assets: Res<AssetServer>,
21+
mut animations: ResMut<Assets<Animation>>,
22+
mut atlas_layouts: ResMut<Assets<TextureAtlasLayout>>,
2623
) {
2724
// 3D sprites require a 3D camera
2825

@@ -33,22 +30,19 @@ fn spawn_sprites(
3330

3431
// Create an animation as usual
3532

36-
let spritesheet = Spritesheet::new(8, 8);
33+
let image = assets.load("character.png");
3734

38-
let clip = Clip::from_frames(spritesheet.row(3));
35+
let spritesheet = Spritesheet::new(&image, 8, 8);
3936

40-
let animation = Animation::from_clip(clip);
37+
let animation = spritesheet.create_animation().add_row(3).build();
4138

4239
let animation_handle = animations.add(animation);
4340

4441
// Create an image and a texture atlas like you would for 2D sprites
4542

46-
let image = assets.load("character.png");
47-
48-
let atlas = TextureAtlas {
49-
layout: atlas_layouts.add(spritesheet.atlas_layout(96, 96)),
50-
..default()
51-
};
43+
let atlas = spritesheet
44+
.with_size_hint(768, 768)
45+
.atlas(&mut atlas_layouts);
5246

5347
// Spawn 3D sprites
5448

examples/basic.rs

Lines changed: 34 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,63 @@
11
// This example shows how to create a simple animated sprite.
22

3-
#[path = "./common/mod.rs"]
4-
pub mod common;
5-
63
use bevy::prelude::*;
74
use bevy_spritesheet_animation::prelude::*;
85

96
fn main() {
107
App::new()
118
.add_plugins(DefaultPlugins)
12-
// Add the plugin to enable animations.
13-
// This makes the Assets<Animation> resource available to your systems.
9+
// Add the plugin to enable animations
10+
//
11+
// This configures the app to play animations for entities with a SpritesheetAnimation component.
12+
// This also makes the Assets<Animation> resource available to your systems.
1413
.add_plugins(SpritesheetAnimationPlugin)
15-
// For that example's sake, we split the setup in two separate systems to show how to retrieve animations with animation sets
16-
.add_systems(
17-
Startup,
18-
(create_animation, spawn_sprite.after(create_animation)),
19-
)
14+
.add_systems(Startup, create_animated_sprite)
2015
.run();
2116
}
2217

23-
// Define an animation set to refer to our animation across systems.
24-
//
25-
// This is not mandatory at all.
26-
// However it is convenient for accessing
27-
animation_set!(MyAnimation [
28-
anim run
29-
]);
30-
31-
fn create_animation(mut commands: Commands, mut animations: ResMut<Assets<Animation>>) {
18+
fn create_animated_sprite(
19+
mut commands: Commands,
20+
assets: Res<AssetServer>,
21+
mut animations: ResMut<Assets<Animation>>,
22+
mut atlas_layouts: ResMut<Assets<TextureAtlasLayout>>,
23+
) {
3224
commands.spawn(Camera2d);
3325

34-
// Create a clip that references some frames from a spritesheet
35-
36-
let spritesheet = Spritesheet::new(8, 8);
37-
38-
let clip = Clip::from_frames(spritesheet.row(3));
39-
40-
// Create an animation that uses the clip
26+
// Create an animation from a row of an 8x8 spritesheet
4127
//
42-
// This is a simple animation with a single clip but we can create more sophisticated
43-
// animations with multiple clips, each one having different parameters.
28+
// This is a simple animation made of a single clip but we can create more sophisticated animations with multiple clips, each one having different parameters.
4429
//
4530
// See the `composition` example for more details.
4631

47-
let animation = Animation::from_clip(clip);
48-
49-
let animation_handle = animations.add(animation);
32+
let image = assets.load("character.png");
5033

51-
// Store the animation to retrieve it from other systems
34+
let spritesheet = Spritesheet::new(&image, 8, 8);
5235

53-
commands.insert_resource(MyAnimation {
54-
run: animation_handle,
55-
});
56-
}
36+
let animation = spritesheet
37+
.create_animation()
38+
.add_row(3)
39+
.set_duration(AnimationDuration::PerFrame(100))
40+
.build();
5741

58-
fn spawn_sprite(
59-
mut commands: Commands,
60-
assets: Res<AssetServer>,
61-
mut atlas_layouts: ResMut<Assets<TextureAtlasLayout>>,
62-
my_animation: Res<MyAnimation>,
63-
) {
64-
// Create an image and a texture atlas like you would for any Bevy sprite
65-
//
66-
// Here we use the Spritesheet helper to easily generate the atlas.
67-
// This is optional and you may prefer to build the atlas manually.
42+
// Register the animation as an asset
6843

69-
let image = assets.load("character.png");
44+
let animation_handle = animations.add(animation);
7045

71-
let spritesheet = Spritesheet::new(8, 8); // TODO weird to have to recreate it
46+
// Create a regular Bevy sprite
47+
//
48+
// Here we use the spritesheet to automatically generate the animation-ready Bevy sprite.
49+
// This is optional and you may prefer to build the sprite manually.
7250

73-
let atlas = TextureAtlas {
74-
layout: atlas_layouts.add(spritesheet.atlas_layout(96, 96)),
75-
..default()
76-
};
51+
let sprite = spritesheet
52+
.with_size_hint(768, 768)
53+
.sprite(&mut atlas_layouts);
7754

78-
// Spawn a sprite with a SpritesheetAnimation component that references our animation
55+
// Spawn the sprite with a SpritesheetAnimation component that references our animation
7956

8057
commands.spawn((
81-
Sprite::from_atlas_image(image, atlas),
82-
SpritesheetAnimation::new(my_animation.run.clone()),
58+
// This is a regular Bevy sprite
59+
sprite,
60+
// This is the component that animates the sprite
61+
SpritesheetAnimation::new(animation_handle),
8362
));
8463
}

examples/character.rs

Lines changed: 24 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@
33
// - We create a few animations for our character (idle, run, shoot) in a Startup system
44
// - We move the character with the keyboard and switch between animations in an Update system
55

6-
#[path = "./common/mod.rs"]
7-
pub mod common;
8-
96
use bevy::prelude::*;
107
use bevy_spritesheet_animation::prelude::*;
118

@@ -20,45 +17,46 @@ fn main() {
2017
.run();
2118
}
2219

23-
animation_set!(MyAnimations [
24-
anim idle,
25-
anim run,
26-
anim shoot
27-
]);
20+
#[derive(Resource)]
21+
struct MyAnimations {
22+
idle: Handle<Animation>,
23+
run: Handle<Animation>,
24+
shoot: Handle<Animation>,
25+
}
2826

2927
fn spawn_character(
3028
mut commands: Commands,
3129
assets: Res<AssetServer>,
32-
mut atlas_layouts: ResMut<Assets<TextureAtlasLayout>>,
3330
mut animations: ResMut<Assets<Animation>>,
31+
mut atlas_layouts: ResMut<Assets<TextureAtlasLayout>>,
3432
) {
35-
commands.spawn(Camera2d);
36-
3733
// Create the animations
3834

39-
let spritesheet = Spritesheet::new(8, 8);
35+
let image = assets.load("character.png");
4036

41-
// Idle
37+
let spritesheet = Spritesheet::new(&image, 8, 8);
4238

43-
let idle_clip = Clip::from_frames(spritesheet.horizontal_strip(0, 0, 5));
39+
// Idle
4440

45-
let idle_animation = Animation::from_clip(idle_clip);
41+
let idle_animation = spritesheet
42+
.create_animation()
43+
.add_horizontal_strip(0, 0, 5)
44+
.build();
4645

4746
let idle_animation_handle = animations.add(idle_animation);
4847

4948
// Run
5049

51-
let run_clip = Clip::from_frames(spritesheet.row(3));
52-
53-
let run_animation = Animation::from_clip(run_clip);
50+
let run_animation = spritesheet.create_animation().add_row(3).build();
5451

5552
let run_animation_handle = animations.add(run_animation);
5653

5754
// Shoot
5855

59-
let shoot_clip = Clip::from_frames(spritesheet.horizontal_strip(0, 5, 5));
60-
61-
let shoot_animation = Animation::from_clip(shoot_clip);
56+
let shoot_animation = spritesheet
57+
.create_animation()
58+
.add_horizontal_strip(0, 5, 5)
59+
.build();
6260

6361
let shoot_animation_handle = animations.add(shoot_animation);
6462

@@ -72,17 +70,13 @@ fn spawn_character(
7270

7371
// Spawn the character
7472

75-
let image = assets.load("character.png");
73+
let sprite = spritesheet
74+
.with_size_hint(768, 768)
75+
.sprite(&mut atlas_layouts);
7676

77-
let atlas = TextureAtlas {
78-
layout: atlas_layouts.add(spritesheet.atlas_layout(96, 96)),
79-
..default()
80-
};
77+
commands.spawn((sprite, SpritesheetAnimation::new(idle_animation_handle)));
8178

82-
commands.spawn((
83-
Sprite::from_atlas_image(image, atlas),
84-
SpritesheetAnimation::new(idle_animation_handle),
85-
));
79+
commands.spawn(Camera2d);
8680
}
8781

8882
// Component to mark that a character is currently shooting

0 commit comments

Comments
 (0)