235 lines
6.3 KiB
Rust
235 lines
6.3 KiB
Rust
|
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,
|
||
|
};
|
||
|
}
|
||
|
}
|