Galactica/crates/content/src/animautomaton.rs

235 lines
6.2 KiB
Rust

use crate::{AnimSectionHandle, Content, SectionEdge, SpriteHandle};
/// A single frame's state
#[derive(Debug, Clone)]
pub struct AnimationState {
/// 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 AnimationState {
/// 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) -> AnimationState {
return AnimationState {
texture_a: self.last_texture,
texture_b: self.next_texture,
fade: self.current_fade,
};
}
}