sprite_animation/
sprite_animation.rs1use std::time::Duration;
6
7use bevy::{input::common_conditions::input_just_pressed, prelude::*};
8
9fn main() {
10 App::new()
11 .add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest())) .add_systems(Startup, setup)
13 .add_systems(Update, execute_animations)
14 .add_systems(
15 Update,
16 (
17 trigger_animation::<RightSprite>.run_if(input_just_pressed(KeyCode::ArrowRight)),
19 trigger_animation::<LeftSprite>.run_if(input_just_pressed(KeyCode::ArrowLeft)),
21 ),
22 )
23 .run();
24}
25
26fn trigger_animation<S: Component>(mut animation: Single<&mut AnimationConfig, With<S>>) {
28 animation.frame_timer = AnimationConfig::timer_from_fps(animation.fps);
30}
31
32#[derive(Component)]
33struct AnimationConfig {
34 first_sprite_index: usize,
35 last_sprite_index: usize,
36 fps: u8,
37 frame_timer: Timer,
38}
39
40impl AnimationConfig {
41 fn new(first: usize, last: usize, fps: u8) -> Self {
42 Self {
43 first_sprite_index: first,
44 last_sprite_index: last,
45 fps,
46 frame_timer: Self::timer_from_fps(fps),
47 }
48 }
49
50 fn timer_from_fps(fps: u8) -> Timer {
51 Timer::new(Duration::from_secs_f32(1.0 / (fps as f32)), TimerMode::Once)
52 }
53}
54
55fn execute_animations(time: Res<Time>, mut query: Query<(&mut AnimationConfig, &mut Sprite)>) {
58 for (mut config, mut sprite) in &mut query {
59 config.frame_timer.tick(time.delta());
61
62 if config.frame_timer.just_finished()
64 && let Some(atlas) = &mut sprite.texture_atlas
65 {
66 if atlas.index == config.last_sprite_index {
67 atlas.index = config.first_sprite_index;
69 } else {
70 atlas.index += 1;
72 config.frame_timer = AnimationConfig::timer_from_fps(config.fps);
74 }
75 }
76 }
77}
78
79#[derive(Component)]
80struct LeftSprite;
81
82#[derive(Component)]
83struct RightSprite;
84
85fn setup(
86 mut commands: Commands,
87 asset_server: Res<AssetServer>,
88 mut texture_atlas_layouts: ResMut<Assets<TextureAtlasLayout>>,
89) {
90 commands.spawn(Camera2d);
91
92 commands.spawn((
94 Text::new("Left Arrow: Animate Left Sprite\nRight Arrow: Animate Right Sprite"),
95 Node {
96 position_type: PositionType::Absolute,
97 top: px(12),
98 left: px(12),
99 ..default()
100 },
101 ));
102
103 let texture = asset_server.load("textures/rpg/chars/gabe/gabe-idle-run.png");
105
106 let layout = TextureAtlasLayout::from_grid(UVec2::splat(24), 7, 1, None, None);
108 let texture_atlas_layout = texture_atlas_layouts.add(layout);
109
110 let animation_config_1 = AnimationConfig::new(1, 6, 10);
112
113 commands.spawn((
115 Sprite {
116 image: texture.clone(),
117 texture_atlas: Some(TextureAtlas {
118 layout: texture_atlas_layout.clone(),
119 index: animation_config_1.first_sprite_index,
120 }),
121 ..default()
122 },
123 Transform::from_scale(Vec3::splat(6.0)).with_translation(Vec3::new(-70.0, 0.0, 0.0)),
124 LeftSprite,
125 animation_config_1,
126 ));
127
128 let animation_config_2 = AnimationConfig::new(1, 6, 20);
130
131 commands.spawn((
133 Sprite {
134 image: texture.clone(),
135 texture_atlas: Some(TextureAtlas {
136 layout: texture_atlas_layout.clone(),
137 index: animation_config_2.first_sprite_index,
138 }),
139 ..Default::default()
140 },
141 Transform::from_scale(Vec3::splat(6.0)).with_translation(Vec3::new(70.0, 0.0, 0.0)),
142 RightSprite,
143 animation_config_2,
144 ));
145}