diff --git a/src/anim/sprite.rs b/src/anim/sprite.rs index fddd96e..385ae7f 100644 --- a/src/anim/sprite.rs +++ b/src/anim/sprite.rs @@ -3,74 +3,114 @@ use std::{collections::HashMap, time::Duration}; use bevy::prelude::*; - -use crate::timer::duration_from_bpm; +use bevy_ecs::system::EntityEntryCommands; /// Data for [AnimatedSprite] -#[derive(Clone, PartialEq, Debug, Reflect)] -#[reflect(Clone, PartialEq, Debug)] +#[derive(Clone, PartialEq, Debug, Default, Reflect)] +#[reflect(Clone, PartialEq, Debug, Default)] pub struct AnimationData { /// Handle to sprite image pub image: Handle, - /// Animation duration in beats - pub duration: f32, + /// Sprite frames + pub frames: Handle, + /// [AnimatedSprite] duration multiplier, usually 1. + pub duration_multiplier: f32, } impl AnimationData { - /// Construct new [AnimationData] - pub fn new(image: Handle, duration: f32) -> Self { - Self { image, duration } + /// Construct new [AnimationData] from given [Handle] + pub fn new(image: Handle, frames: Handle, duration_multiplier: f32) -> Self { + Self { image, frames, duration_multiplier } } } /// Animated sprite with states -#[derive(Clone, PartialEq, Debug, Reflect)] -#[reflect(Clone, PartialEq, Debug, Default)] +#[derive(Component, Clone, PartialEq, Debug, Reflect)] +#[reflect(Component, Clone, PartialEq, Debug)] +#[require(Sprite)] pub struct AnimatedSprite { data: HashMap, current: Option, timer: Timer, - bpm: Duration, -} - -impl Default for AnimatedSprite { - fn default() -> Self { - Self { - data: HashMap::new(), - current: None, - timer: Timer::new(Duration::default(), TimerMode::Repeating), - bpm: Duration::default(), - } - } + duration: Duration, } impl AnimatedSprite { + /// Constructs new AnimatedSprite + pub fn new(data: HashMap, duration: Duration) -> Self { + let timer = Timer::new(duration, TimerMode::Once); + + Self { data, current: None, timer, duration } + } + /// Add new animation to sprite pub fn add(&mut self, data: AnimationData, label: String) { self.data.insert(label, data); } - /// Sets internal BPM state - pub fn set_bpm(&mut self, bpm: f32) { - self.bpm = duration_from_bpm(bpm); + /// Sets internal duration + pub fn set_duration(&mut self, duration: Duration) { + self.duration = duration; } - /// Finds animation by its label and returns true if it started playing - pub fn play(&mut self, label: &str, mode: TimerMode) -> bool { - match self.data.get(label) { - Some(data) => { - self.current = Some(data.clone()); - self.timer.set_duration(self.bpm.mul_f32(data.duration)); - self.timer.set_mode(mode); + /// Finds animation by its label and returns it if started playing + pub fn play( + &mut self, + label: &str, + mode: TimerMode, + mut sprite: EntityEntryCommands, + ) -> 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); - true - }, - None => false, - } + let sprite_image = data.image.clone(); + let layout = data.frames.clone(); + + sprite.and_modify(move |mut sprite| { + sprite.image = sprite_image; + sprite.texture_atlas = Some(TextureAtlas { layout, index: 0 }); + }); + + data + }) } - /// Updates animation and returns true if animation should stop - pub fn tick(&mut self, dt: Duration) -> bool { - self.timer.tick(dt).just_finished() + /// Updates animation timer + pub fn tick(&mut self, dt: Duration) { + self.timer.tick(dt); + } + + /// Returns inner [Timer::fraction] + pub fn fraction(&self) -> f32 { + self.timer.fraction() + } +} + +/// Updates [AnimatedSprite] entities +pub fn update_animated_sprites( + time: Res