sprite_sheet/
sprite_sheet.rs

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
4use bevy::prelude::*;
5
6fn 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)]
15struct AnimationIndices {
16    first: usize,
17    last: usize,
18}
19
20#[derive(Component, Deref, DerefMut)]
21struct AnimationTimer(Timer);
22
23fn 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
42fn 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}