nightshade 0.8.2

A cross-platform data-oriented game engine.
Documentation
use nalgebra_glm::Vec2;

#[derive(Debug, Clone, Copy, PartialEq, Default, serde::Serialize, serde::Deserialize)]
pub enum LoopMode {
    Once,
    #[default]
    Loop,
    PingPong,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct SpriteFrame {
    pub uv_min: Vec2,
    pub uv_max: Vec2,
    pub duration: f32,
    pub texture_index: Option<u32>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct SpriteAnimator {
    pub frames: Vec<SpriteFrame>,
    pub current_frame: usize,
    pub elapsed: f32,
    pub loop_mode: LoopMode,
    pub playing: bool,
    pub speed: f32,
    pub ping_pong_forward: bool,
}

impl Default for SpriteAnimator {
    fn default() -> Self {
        Self {
            frames: Vec::new(),
            current_frame: 0,
            elapsed: 0.0,
            loop_mode: LoopMode::Loop,
            playing: true,
            speed: 1.0,
            ping_pong_forward: true,
        }
    }
}

impl SpriteAnimator {
    pub fn from_grid(columns: u32, rows: u32, frame_count: u32, frame_duration: f32) -> Self {
        let mut frames = Vec::with_capacity(frame_count as usize);
        let cell_width = 1.0 / columns as f32;
        let cell_height = 1.0 / rows as f32;

        for index in 0..frame_count {
            let column = index % columns;
            let row = index / columns;
            frames.push(SpriteFrame {
                uv_min: Vec2::new(column as f32 * cell_width, row as f32 * cell_height),
                uv_max: Vec2::new(
                    (column + 1) as f32 * cell_width,
                    (row + 1) as f32 * cell_height,
                ),
                duration: frame_duration,
                texture_index: None,
            });
        }

        Self {
            frames,
            playing: true,
            ..Default::default()
        }
    }

    pub fn with_loop_mode(mut self, mode: LoopMode) -> Self {
        self.loop_mode = mode;
        self
    }

    pub fn with_speed(mut self, speed: f32) -> Self {
        self.speed = speed;
        self
    }

    pub fn play(&mut self) {
        self.playing = true;
    }

    pub fn pause(&mut self) {
        self.playing = false;
    }

    pub fn reset(&mut self) {
        self.current_frame = 0;
        self.elapsed = 0.0;
        self.ping_pong_forward = true;
    }
}