docs/bevy/examples/2d/sprite_sheet.rs 2 K · 67 lines · raw · history

1 //! Renders an animated sprite by loading all animation frames from a single image (a sprite sheet)
2 //! into a texture atlas, and changing the displayed image periodically.
3
4 use bevy::prelude::*;
5
6 fn main() {
7 App::new()
8 .add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest())) // prevents blurry sprites
9 .add_systems(Startup, setup)
10 .add_systems(Update, animate_sprite)
11 .run();
12 }
13
14 #[derive(Component)]
15 struct AnimationIndices {
16 first: usize,
17 last: usize,
18 }
19
20 #[derive(Component, Deref, DerefMut)]
21 struct AnimationTimer(Timer);
22
23 fn animate_sprite(
24 time: Res<Time>,
25 mut query: Query<(&AnimationIndices, &mut AnimationTimer, &mut Sprite)>,
26 ) {
27 for (indices, mut timer, mut sprite) in &mut query {
28 timer.tick(time.delta());
29
30 if timer.just_finished()
31 && let Some(atlas) = &mut sprite.texture_atlas
32 {
33 atlas.index = if atlas.index == indices.last {
34 indices.first
35 } else {
36 atlas.index + 1
37 };
38 }
39 }
40 }
41
42 fn setup(
43 mut commands: Commands,
44 asset_server: Res<AssetServer>,
45 mut texture_atlas_layouts: ResMut<Assets<TextureAtlasLayout>>,
46 ) {
47 let texture = asset_server.load("textures/rpg/chars/gabe/gabe-idle-run.png");
48 let layout = TextureAtlasLayout::from_grid(UVec2::splat(24), 7, 1, None, None);
49 let texture_atlas_layout = texture_atlas_layouts.add(layout);
50 // Use only the subset of sprites in the sheet that make up the run animation
51 let animation_indices = AnimationIndices { first: 1, last: 6 };
52
53 commands.spawn(Camera2d);
54
55 commands.spawn((
56 Sprite::from_atlas_image(
57 texture,
58 TextureAtlas {
59 layout: texture_atlas_layout,
60 index: animation_indices.first,
61 },
62 ),
63 Transform::from_scale(Vec3::splat(6.0)),
64 animation_indices,
65 AnimationTimer(Timer::from_seconds(0.1, TimerMode::Repeating)),
66 ));
67 }