micro_games_kit/
animation.rs1use spitfire_draw::utils::TextureRef;
2use std::{
3 collections::{HashMap, HashSet},
4 ops::Range,
5};
6use vek::Rect;
7
8#[derive(Debug, Clone, PartialEq)]
9pub struct FrameAnimation {
10 frames: Range<usize>,
11 current: Option<usize>,
12 accumulator: f32,
13 events: HashMap<usize, HashSet<String>>,
14 pub fps: f32,
15 pub is_playing: bool,
16 pub looping: bool,
17}
18
19impl FrameAnimation {
20 pub fn new(frames: Range<usize>) -> Self {
21 Self {
22 frames,
23 current: None,
24 accumulator: 0.0,
25 events: Default::default(),
26 fps: 30.0,
27 is_playing: false,
28 looping: false,
29 }
30 }
31
32 pub fn event(mut self, frame: usize, id: impl ToString) -> Self {
33 self.events.entry(frame).or_default().insert(id.to_string());
34 self
35 }
36
37 pub fn fps(mut self, value: f32) -> Self {
38 self.fps = value;
39 self
40 }
41
42 pub fn playing(mut self) -> Self {
43 self.play();
44 self
45 }
46
47 pub fn looping(mut self) -> Self {
48 self.looping = true;
49 self
50 }
51
52 pub fn play(&mut self) {
53 if self.frames.is_empty() {
54 return;
55 }
56 self.is_playing = true;
57 self.accumulator = 0.0;
58 self.current = Some(self.frames.start);
59 }
60
61 pub fn stop(&mut self) {
62 self.is_playing = false;
63 self.accumulator = 0.0;
64 self.current = None;
65 }
66
67 pub fn update(&mut self, delta_time: f32) -> HashSet<&str> {
68 if self.frames.is_empty() || !self.is_playing {
69 return Default::default();
70 }
71 let Some(mut current) = self.current else {
72 return Default::default();
73 };
74 let mut result = HashSet::default();
75 self.accumulator += (delta_time * self.fps).max(0.0);
76 while self.accumulator >= 1.0 {
77 self.accumulator -= 1.0;
78 if let Some(events) = self.events.get(¤t) {
79 result.extend(events.iter().map(|id| id.as_str()));
80 }
81 current += 1;
82 if current >= self.frames.end {
83 if self.looping {
84 current = self.frames.start;
85 } else {
86 self.is_playing = false;
87 self.accumulator = 0.0;
88 self.current = None;
89 return result;
90 }
91 }
92 }
93 self.current = Some(current);
94 result
95 }
96
97 pub fn current_frame(&self) -> Option<usize> {
98 self.current
99 }
100}
101
102#[derive(Debug, Clone)]
103pub struct NamedAnimation {
104 pub animation: FrameAnimation,
105 pub id: String,
106}
107
108#[derive(Debug, Clone)]
109pub struct SpriteAnimationFrame {
110 pub texture: TextureRef,
111 pub region: Rect<f32, f32>,
112 pub page: f32,
113}
114
115#[derive(Debug, Clone)]
116pub struct SpriteAnimation {
117 pub animation: FrameAnimation,
118 pub frames: HashMap<usize, SpriteAnimationFrame>,
119}
120
121impl SpriteAnimation {
122 pub fn current_frame(&self) -> Option<&SpriteAnimationFrame> {
123 self.frames.get(&self.animation.current_frame()?)
124 }
125}