Skip to main content

fantasy_craft/graphics/
animations.rs

1use std::sync::Arc;
2use macroquad::math::Vec2;
3
4use crate::core::plugins::Plugin;
5use crate::prelude::{GameState, Stage, System};
6use crate::scene::scene_loader::ComponentLoader;
7use crate::{graphics::sprites::Spritesheet, prelude::Context};
8use crate::physics::components::Transform;
9
10pub struct AnimationKeyFrame {
11    pub column: u32,
12    pub row: u32
13}
14
15impl AnimationKeyFrame {
16    pub fn new(column: u32, row: u32) -> Self {
17        Self { column, row }
18    }
19}
20
21pub struct Animation {
22    pub spritesheet: Arc<Spritesheet>,
23    pub frames: Vec<AnimationKeyFrame>,
24    pub current_index: f32,
25    pub speed: f32,
26    pub flip: bool
27}
28
29impl Animation {
30    pub fn new(spritesheet: Arc<Spritesheet>, frames: Vec<AnimationKeyFrame>, speed: f32, flip: bool) -> Self {
31        Self {
32            spritesheet,
33            frames,
34            current_index: 0.0,
35            speed,
36            flip
37        }
38    }
39
40    pub fn update(&mut self, dt: f32) {
41        self.current_index += self.speed * dt;
42        let num_frames = self.frames.len() as f32;
43
44        if self.current_index >= num_frames {
45            self.current_index = self.current_index.rem_euclid(num_frames);
46        }
47    }
48
49    pub fn draw(&self, x: f32, y: f32, scale: Vec2) {
50        let frame_index = self.current_index.floor() as usize;
51        let key_frame = self.frames.get(frame_index).expect("AnimationKeyFrame index out of bounds");
52        self.spritesheet.draw_sprite(key_frame.column, key_frame.row, x, y, scale, self.flip);
53    }
54}
55
56#[derive(Debug)]
57pub struct AnimationComponent(pub String);
58
59pub struct AnimationComponentLoader;
60
61impl ComponentLoader for AnimationComponentLoader {
62    fn load(&self, ctx: &mut Context, entity: hecs::Entity, data: &serde_json::Value) {
63        let animation_name: String = serde_json::from_value(data.clone())
64            .unwrap_or_default();
65
66        let component = AnimationComponent(animation_name);
67
68        ctx.world.insert_one(entity, component).expect("Failed to insert AnimationComponent");
69    }
70}
71
72pub fn update_animations(ctx: &mut Context) {
73    let dt = ctx.dt().clone();
74
75    for (_, animation_comp) in ctx.world.query::<&AnimationComponent>().iter() {
76        if let Some(animation) = ctx.asset_server.get_animation_mut(&animation_comp.0) {
77            animation.update(dt);
78        }
79    }
80}
81
82pub fn animation_render_system(ctx: &mut Context) {
83    for (_, (animation_comp, transform)) in ctx.world.query::<(&AnimationComponent, &Transform)>().iter() {
84        if let Some(animation) = ctx.asset_server.get_animation_mut(&animation_comp.0) {
85            animation.draw(transform.position.x, transform.position.y, transform.scale);
86        }
87    }
88}
89
90pub struct AnimationPlugin;
91
92impl Plugin for AnimationPlugin {
93    fn build(&self, app: &mut crate::prelude::App) {
94        app.scene_loader
95            .register("AnimationComponent", Box::new(AnimationComponentLoader));
96
97        app
98            .add_system(Stage::Update, System::new(
99                update_animations,
100                vec![GameState::Playing]
101            ))
102            .add_system(Stage::Render, System::new(
103                animation_render_system,
104                vec![GameState::Playing, GameState::Menu]
105            ));
106    }
107}