1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
//! Animation management
//!
//! To create custom animation and use it you will need an image that can be represented as tiles.
//! Each tile contains specific frame of animation.
//! Every specific animation should be placed in separate row.
//!
//! # Examples
//! Let's say we have an image of our character where every frame is 15x20 rect and we have this animations:
//! - Idle animation with 20 frames at 12 fps
//! - Run animation with 15 frames at 15 fps
//!
//! ```no_run
//! use macroquad::experimental::animation::*;
//! use macroquad::prelude::*;
//!
//! #[macroquad::main("Animation")]
//! async fn main() {
//! // Define animations
//! let mut sprite = AnimatedSprite::new(
//! 15,
//! 20,
//! &[
//! Animation {
//! name: "idle".to_string(),
//! row: 0,
//! frames: 20,
//! fps: 12,
//! },
//! Animation {
//! name: "run".to_string(),
//! row: 1,
//! frames: 15,
//! fps: 15,
//! },
//! ],
//! true,
//! );
//! let image = load_texture("some_path.png").await.unwrap();
//! loop {
//! clear_background(WHITE);
//! // Now we can draw our character
//! draw_texture_ex(
//! image,
//! 10.,
//! 10.,
//! WHITE,
//! DrawTextureParams {
//! source: Some(sprite.frame().source_rect),
//! dest_size: Some(sprite.frame().dest_size),
//! ..Default::default()
//! }
//! );
//! // Update frame
//! sprite.update();
//! next_frame().await;
//! }
//! }
use crate::{
math::{vec2, Rect, Vec2},
time::get_frame_time,
};
/// Specification of animation
#[derive(Clone, Debug)]
pub struct Animation {
pub name: String,
pub row: u32,
pub frames: u32,
pub fps: u32,
}
/// Specific animation frame
pub struct AnimationFrame {
/// Area of current frame in source image
pub source_rect: Rect,
/// Size of frame
pub dest_size: Vec2,
}
/// Main definition of all animations for specific image
#[derive(Clone)]
pub struct AnimatedSprite {
tile_width: f32,
tile_height: f32,
animations: Vec<Animation>,
current_animation: usize,
time: f32,
frame: u32,
/// Controls if frame should be updated on [update][Self::update]
pub playing: bool,
}
impl AnimatedSprite {
pub fn new(
tile_width: u32,
tile_height: u32,
animations: &[Animation],
playing: bool,
) -> AnimatedSprite {
AnimatedSprite {
tile_width: tile_width as f32,
tile_height: tile_height as f32,
animations: animations.to_vec(),
current_animation: 0,
time: 0.0,
frame: 0,
playing,
}
}
/// Choose animation to display
///
/// **Note:** the animations is not reset when switching, for this use [set_frame][Self::set_frame]
pub fn set_animation(&mut self, animation: usize) {
self.current_animation = animation;
let animation = &self.animations[self.current_animation];
self.frame %= animation.frames;
}
/// Currently chosen animation
pub fn current_animation(&self) -> usize {
self.current_animation
}
/// Set specific frame for animation
pub fn set_frame(&mut self, frame: u32) {
self.frame = frame;
}
/// Returns whether the last frame is being displayed
pub fn is_last_frame(&self) -> bool {
let animation = &self.animations[self.current_animation];
self.frame == animation.frames - 1
}
/// Update current frame
///
/// Switches to the next frame every `1. / current_animation.fps` seconds
pub fn update(&mut self) {
let animation = &self.animations[self.current_animation];
if self.playing {
self.time += get_frame_time();
if self.time > 1. / animation.fps as f32 {
self.frame += 1;
self.time = 0.0;
}
}
self.frame %= animation.frames;
}
/// Get current frame
pub fn frame(&self) -> AnimationFrame {
let animation = &self.animations[self.current_animation];
AnimationFrame {
source_rect: Rect::new(
self.tile_width * self.frame as f32,
self.tile_height * animation.row as f32,
self.tile_width,
self.tile_height,
),
dest_size: vec2(self.tile_width, self.tile_height),
}
}
}