diff --git a/src/anim/sprite.rs b/src/anim/sprite.rs index 385ae7f..14053fc 100644 --- a/src/anim/sprite.rs +++ b/src/anim/sprite.rs @@ -5,6 +5,8 @@ use std::{collections::HashMap, time::Duration}; use bevy::prelude::*; use bevy_ecs::system::EntityEntryCommands; +type SpriteCommands<'a> = EntityEntryCommands<'a, Sprite>; + /// Data for [AnimatedSprite] #[derive(Clone, PartialEq, Debug, Default, Reflect)] #[reflect(Clone, PartialEq, Debug, Default)] @@ -19,6 +21,7 @@ pub struct AnimationData { impl AnimationData { /// Construct new [AnimationData] from given [Handle] + #[inline(always)] pub fn new(image: Handle, frames: Handle, duration_multiplier: f32) -> Self { Self { image, frames, duration_multiplier } } @@ -33,36 +36,41 @@ pub struct AnimatedSprite { current: Option, timer: Timer, duration: Duration, + backwards: bool, } impl AnimatedSprite { /// Constructs new AnimatedSprite + #[inline(always)] pub fn new(data: HashMap, duration: Duration) -> Self { let timer = Timer::new(duration, TimerMode::Once); - Self { data, current: None, timer, duration } + Self { data, current: None, timer, duration, backwards: false } } /// Add new animation to sprite + #[inline(always)] pub fn add(&mut self, data: AnimationData, label: String) { self.data.insert(label, data); } /// Sets internal duration + #[inline(always)] pub fn set_duration(&mut self, duration: Duration) { self.duration = duration; } - /// Finds animation by its label and returns it if started playing - pub fn play( + fn play_inner( &mut self, label: &str, mode: TimerMode, - mut sprite: EntityEntryCommands, + mut sprite: SpriteCommands, + backwards: bool, ) -> Option<&AnimationData> { self.data.get(label).map(|data| { self.current = Some(data.clone()); self.timer = Timer::new(self.duration.mul_f32(data.duration_multiplier), mode); + self.backwards = backwards; let sprite_image = data.image.clone(); let layout = data.frames.clone(); @@ -75,15 +83,55 @@ impl AnimatedSprite { data }) } + + /// Finds animation by its label, plays it and returns it if started playing + #[inline(always)] + pub fn play( + &mut self, + label: &str, + mode: TimerMode, + sprite: SpriteCommands, + ) -> Option<&AnimationData> { + self.play_inner(label, mode, sprite, false) + } + + /// Same as [play](AnimatedSprite::play), but animation plays from the end to the beginning + #[inline(always)] + pub fn play_backwards( + &mut self, + label: &str, + mode: TimerMode, + sprite: SpriteCommands, + ) -> Option<&AnimationData> { + self.play_inner(label, mode, sprite, true) + } + + /// Stops the animation + #[inline(always)] + pub fn stop(&mut self) { + self.current = None; + self.timer.reset(); + } /// Updates animation timer + #[inline(always)] pub fn tick(&mut self, dt: Duration) { self.timer.tick(dt); } - /// Returns inner [Timer::fraction] + /// Returns inner [Timer::fraction] or [Timer::fraction_remaining] if playing backwards + #[inline(always)] pub fn fraction(&self) -> f32 { - self.timer.fraction() + match self.backwards { + true => self.timer.fraction_remaining(), + false => self.timer.fraction(), + } + } + + /// Returns [Timer::mode] + #[inline(always)] + pub fn mode(&self) -> TimerMode { + self.timer.mode() } } @@ -107,7 +155,7 @@ pub fn update_animated_sprites( let max_frames = atlas.len() as f32; - let current_frame = (max_frames * animation.fraction()) as usize; + let current_frame = (max_frames * animation.fraction()).min(max_frames - 1.) as usize; if let Some(atlas) = &mut sprite.texture_atlas { atlas.index = current_frame;