use crate::{AnimSectionHandle, Content, SectionEdge, SpriteHandle}; /// A single frame's state #[derive(Debug, Clone)] pub struct SpriteAnimationFrame { /// The index of the texture we're fading from pub texture_a: u32, /// The index of the texture we're fading to pub texture_b: u32, /// Between 0.0 and 1.0, denoting how far we are between /// texture_a and texture_b /// 0.0 means fully show texture_a; /// 1.0 means fully show texture_b. pub fade: f32, } impl SpriteAnimationFrame { /// Convenience method. /// Get texture index as an array pub fn texture_index(&self) -> [u32; 2] { [self.texture_a, self.texture_b] } } /// What direction are we playing our animation in? #[derive(Debug, Clone)] enum AnimDirection { /// Top to bottom, with increasing frame indices /// (normal) Up, /// Bottom to top, with decreasing frame indices /// (reverse) Down, /// Stopped, no animation Stop, } /// Manages a single sprite's animation state. #[derive(Debug, Clone)] pub struct AnimAutomaton { /// The sprite we're animating sprite: SpriteHandle, /// Which animation section we're on /// This MUST be a section from this Automaton's sprite current_section: AnimSectionHandle, /// Which frame we're on current_frame: usize, /// Where we are between frames. /// Always between zero and one. current_fade: f32, /// In what direction are we playing the current section? current_direction: AnimDirection, /// The texture we're fading from /// (if we're moving downwards) last_texture: u32, /// The texture we're fading to /// (if we're moving downwards) next_texture: u32, } impl AnimAutomaton { /// Create a new AnimAutomaton pub fn new(ct: &Content, sprite_handle: SpriteHandle) -> Self { let sprite = ct.get_sprite(sprite_handle); Self { current_direction: AnimDirection::Down, sprite: sprite.handle, current_frame: 0, current_fade: 0.0, current_section: sprite.default_section, last_texture: *sprite .get_section(sprite.default_section) .frames .first() .unwrap(), next_texture: *sprite .get_section(sprite.default_section) .frames .first() .unwrap(), } } /// Reset this animation pub fn reset(&mut self, ct: &Content) { let sprite = ct.get_sprite(self.sprite); self.current_fade = 0.0; self.current_frame = 0; self.current_section = sprite.default_section } /// Reverse this animation's direction pub fn reverse(&mut self) { match self.current_direction { AnimDirection::Stop => {} AnimDirection::Up => { self.current_direction = AnimDirection::Down; } AnimDirection::Down => { self.current_direction = AnimDirection::Up; } } } /// Step this animation by `t` seconds pub fn step(&mut self, ct: &Content, t: f32) { let sprite = ct.get_sprite(self.sprite); let current_section = sprite.get_section(self.current_section); // Current_fade and current_frame keep track of where we are in the current section. // current_frame indexes this section frames. When it exceeds the number of frames // or falls below zero (when moving in reverse), we switch to the next section. // // current_fade keeps track of our state between frames. It is zero once a frame starts, // and we switch to the next frame when it hits 1.0. If we are stepping foward, it increases, // and if we are stepping backwards, it decreases. // If this is zero, this section isn't animated. if current_section.frame_duration == 0.0 { return; } match self.current_direction { AnimDirection::Down => self.current_fade += t / current_section.frame_duration, AnimDirection::Up => self.current_fade -= t / current_section.frame_duration, AnimDirection::Stop => {} } // We're stepping foward and finished this frame // (implies we're travelling downwards) if self.current_fade > 1.0 { while self.current_fade > 1.0 { self.current_fade -= 1.0; } if self.current_frame < current_section.frames.len() - 1 { self.current_frame += 1; } else { match current_section.edge_bot { SectionEdge::Stop => { self.current_fade = 0.0; self.current_frame = current_section.frames.len() - 1; self.current_direction = AnimDirection::Stop; } SectionEdge::Top { section } => { self.current_section = section; self.current_frame = 0; } SectionEdge::Bot { section } => { let s = sprite.get_section(section); self.current_section = section; self.current_frame = s.frames.len() - 1; self.reverse(); } SectionEdge::Restart => { self.current_frame = 0; } SectionEdge::Reverse => { // Jump to SECOND frame, since we've already shown the // first during the fade transition self.current_frame = current_section.frames.len() - 1; self.reverse() } } } let current_section = sprite.get_section(self.current_section); self.last_texture = self.next_texture; self.next_texture = current_section.frames[self.current_frame]; } // We're stepping backward and finished this frame // (implies we're travelling upwards) if self.current_fade < 0.0 { while self.current_fade < 0.0 { self.current_fade += 1.0; } if self.current_frame > 0 { self.current_frame -= 1; } else { match current_section.edge_top { SectionEdge::Stop => { self.current_fade = 0.0; self.current_frame = 0; self.current_direction = AnimDirection::Stop; } SectionEdge::Top { section } => { self.current_section = section; self.current_frame = 0; self.reverse(); } SectionEdge::Bot { section } => { let s = sprite.get_section(section); self.current_section = section; self.current_frame = s.frames.len() - 1; } SectionEdge::Reverse => { self.current_frame = 0; self.reverse(); } SectionEdge::Restart => { self.current_frame = current_section.frames.len() - 1; } } } let current_section = sprite.get_section(self.current_section); self.next_texture = self.last_texture; self.last_texture = current_section.frames[self.current_frame]; } } /// Get the current frame of this animation pub fn get_texture_idx(&self) -> SpriteAnimationFrame { return SpriteAnimationFrame { texture_a: self.last_texture, texture_b: self.next_texture, fade: self.current_fade, }; } }