Refactor animations (#5)
- `SpriteRef` impls `Animation`, `Sprite` does not - Animation duration and delta time now lives on the struct that handles the animation "state machine" - Tweak the player animation to 100ms frame duration (was 50ms)
This commit is contained in:
parent
d0a16f9a71
commit
31bc8c7614
|
@ -8,7 +8,7 @@ use std::time::Duration;
|
|||
|
||||
pub use controls::{Controls, Direction};
|
||||
use loader::{load_assets, Assets};
|
||||
use sprites::{blit, Drawable, Frame, Sprite, SpriteRef};
|
||||
use sprites::{blit, Animation, Frame, Sprite, SpriteRef};
|
||||
|
||||
mod controls;
|
||||
mod loader;
|
||||
|
@ -35,8 +35,7 @@ pub struct World {
|
|||
score: u32,
|
||||
assets: Assets,
|
||||
screen: Vec<u8>,
|
||||
timing: Duration,
|
||||
frame_count: usize,
|
||||
dt: Duration,
|
||||
}
|
||||
|
||||
/// A tiny position vector
|
||||
|
@ -124,7 +123,7 @@ impl World {
|
|||
bounds: Bounds::default(),
|
||||
};
|
||||
let player = Player {
|
||||
sprite: SpriteRef::new(&assets, Player1),
|
||||
sprite: SpriteRef::new(&assets, Player1, Duration::from_millis(100)),
|
||||
pos: Point::new(80, 216),
|
||||
last_update: 0,
|
||||
};
|
||||
|
@ -148,8 +147,7 @@ impl World {
|
|||
score: 0,
|
||||
assets,
|
||||
screen,
|
||||
timing: Duration::default(),
|
||||
frame_count: 0,
|
||||
dt: Duration::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -163,17 +161,16 @@ impl World {
|
|||
let one_frame = Duration::new(0, 16_666_667);
|
||||
|
||||
// Advance the timer by the delta time
|
||||
self.timing += dt;
|
||||
self.dt += dt;
|
||||
|
||||
// Step the invaders one by one
|
||||
while self.timing >= one_frame {
|
||||
self.frame_count += 1;
|
||||
self.timing -= one_frame;
|
||||
while self.dt >= one_frame {
|
||||
self.dt -= one_frame;
|
||||
self.step_invaders();
|
||||
}
|
||||
|
||||
// Handle player movement and animation
|
||||
self.step_player(controls);
|
||||
self.step_player(controls, dt);
|
||||
|
||||
// TODO: Handle lasers and bullets
|
||||
// Movements can be multiplied by the delta-time frame count, instead of looping
|
||||
|
@ -216,34 +213,25 @@ impl World {
|
|||
// TODO: Move the invader
|
||||
|
||||
// Animate the invader
|
||||
invader.sprite.animate(&self.assets);
|
||||
invader.sprite.step_frame(&self.assets);
|
||||
}
|
||||
|
||||
fn step_player(&mut self, controls: &Controls) {
|
||||
let animate_player = match controls.direction {
|
||||
fn step_player(&mut self, controls: &Controls, dt: Duration) {
|
||||
match controls.direction {
|
||||
Direction::Left => {
|
||||
if self.player.pos.x > 0 {
|
||||
self.player.pos.x -= 1;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
self.player.sprite.animate(&self.assets, dt);
|
||||
}
|
||||
}
|
||||
|
||||
Direction::Right => {
|
||||
if self.player.pos.x < 224 - 16 {
|
||||
self.player.pos.x += 1;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
self.player.sprite.animate(&self.assets, dt);
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if animate_player && self.frame_count - self.player.last_update >= 3 {
|
||||
self.player.last_update = self.frame_count;
|
||||
self.player.sprite.animate(&self.assets);
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -331,7 +319,7 @@ fn make_invader_grid(assets: &Assets) -> Vec<Vec<Option<Invader>>> {
|
|||
(0..COLS)
|
||||
.map(|x| {
|
||||
Some(Invader {
|
||||
sprite: SpriteRef::new(assets, Blipjoy1),
|
||||
sprite: SpriteRef::new(assets, Blipjoy1, Duration::default()),
|
||||
pos: START + BLIPJOY_OFFSET + Point::new(x, y) * GRID,
|
||||
score: 10,
|
||||
})
|
||||
|
@ -342,7 +330,7 @@ fn make_invader_grid(assets: &Assets) -> Vec<Vec<Option<Invader>>> {
|
|||
(0..COLS)
|
||||
.map(|x| {
|
||||
Some(Invader {
|
||||
sprite: SpriteRef::new(assets, Ferris1),
|
||||
sprite: SpriteRef::new(assets, Ferris1, Duration::default()),
|
||||
pos: START + FERRIS_OFFSET + Point::new(x, y) * GRID,
|
||||
score: 10,
|
||||
})
|
||||
|
@ -353,7 +341,7 @@ fn make_invader_grid(assets: &Assets) -> Vec<Vec<Option<Invader>>> {
|
|||
(0..COLS)
|
||||
.map(|x| {
|
||||
Some(Invader {
|
||||
sprite: SpriteRef::new(assets, Cthulhu1),
|
||||
sprite: SpriteRef::new(assets, Cthulhu1, Duration::default()),
|
||||
pos: START + CTHULHU_OFFSET + Point::new(x, y) * GRID,
|
||||
score: 10,
|
||||
})
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use std::rc::Rc;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::loader::Assets;
|
||||
use crate::{Point, SCREEN_WIDTH};
|
||||
|
@ -26,13 +27,15 @@ pub(crate) enum Frame {
|
|||
// Laser2,
|
||||
}
|
||||
|
||||
/// Sprites can be drawn and animated.
|
||||
/// Sprites can be drawn and procedurally generated.
|
||||
///
|
||||
/// A `Sprite` owns its pixel data, and cannot be animated. Use a `SpriteRef` if you need
|
||||
/// animations.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Sprite {
|
||||
width: usize,
|
||||
height: usize,
|
||||
pixels: Vec<u8>,
|
||||
frame: Frame,
|
||||
}
|
||||
|
||||
/// SpriteRefs can be drawn and animated.
|
||||
|
@ -44,6 +47,8 @@ pub(crate) struct SpriteRef {
|
|||
height: usize,
|
||||
pixels: Rc<Vec<u8>>,
|
||||
frame: Frame,
|
||||
duration: Duration,
|
||||
dt: Duration,
|
||||
}
|
||||
|
||||
/// Drawables can be blitted to the pixel buffer and animated.
|
||||
|
@ -51,15 +56,43 @@ pub(crate) trait Drawable {
|
|||
fn width(&self) -> usize;
|
||||
fn height(&self) -> usize;
|
||||
fn pixels(&self) -> &[u8];
|
||||
fn update_pixels(&mut self, pixels: Rc<Vec<u8>>);
|
||||
fn frame(&self) -> &Frame;
|
||||
fn update_frame(&mut self, frame: Frame);
|
||||
}
|
||||
|
||||
fn animate(&mut self, assets: &Assets) {
|
||||
pub(crate) trait Animation {
|
||||
fn animate(&mut self, assets: &Assets, dt: Duration);
|
||||
}
|
||||
|
||||
impl Sprite {
|
||||
pub(crate) fn new(assets: &Assets, frame: Frame) -> Sprite {
|
||||
let (width, height, pixels) = assets.sprites().get(&frame).unwrap();
|
||||
|
||||
Sprite {
|
||||
width: *width,
|
||||
height: *height,
|
||||
pixels: pixels.to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SpriteRef {
|
||||
pub(crate) fn new(assets: &Assets, frame: Frame, duration: Duration) -> SpriteRef {
|
||||
let (width, height, pixels) = assets.sprites().get(&frame).unwrap();
|
||||
|
||||
SpriteRef {
|
||||
width: *width,
|
||||
height: *height,
|
||||
pixels: pixels.clone(),
|
||||
frame,
|
||||
duration,
|
||||
dt: Duration::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn step_frame(&mut self, assets: &Assets) {
|
||||
use Frame::*;
|
||||
|
||||
let assets = assets.sprites();
|
||||
let (pixels, frame) = match self.frame() {
|
||||
let (pixels, frame) = match self.frame {
|
||||
Blipjoy1 => (assets.get(&Blipjoy2).unwrap().2.clone(), Blipjoy2),
|
||||
Blipjoy2 => (assets.get(&Blipjoy1).unwrap().2.clone(), Blipjoy1),
|
||||
|
||||
|
@ -78,34 +111,8 @@ pub(crate) trait Drawable {
|
|||
// Laser2 => (assets.get(&Laser1).unwrap().2.clone(), Laser1),
|
||||
};
|
||||
|
||||
self.update_pixels(pixels);
|
||||
self.update_frame(frame);
|
||||
}
|
||||
}
|
||||
|
||||
impl Sprite {
|
||||
pub(crate) fn new(assets: &Assets, frame: Frame) -> Sprite {
|
||||
let (width, height, pixels) = assets.sprites().get(&frame).unwrap();
|
||||
|
||||
Sprite {
|
||||
width: *width,
|
||||
height: *height,
|
||||
pixels: pixels.to_vec(),
|
||||
frame,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SpriteRef {
|
||||
pub(crate) fn new(assets: &Assets, frame: Frame) -> SpriteRef {
|
||||
let (width, height, pixels) = assets.sprites().get(&frame).unwrap();
|
||||
|
||||
SpriteRef {
|
||||
width: *width,
|
||||
height: *height,
|
||||
pixels: pixels.clone(),
|
||||
frame,
|
||||
}
|
||||
self.pixels = pixels;
|
||||
self.frame = frame;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,18 +128,6 @@ impl Drawable for Sprite {
|
|||
fn pixels(&self) -> &[u8] {
|
||||
&self.pixels
|
||||
}
|
||||
|
||||
fn update_pixels(&mut self, pixels: Rc<Vec<u8>>) {
|
||||
self.pixels = pixels.to_vec();
|
||||
}
|
||||
|
||||
fn frame(&self) -> &Frame {
|
||||
&self.frame
|
||||
}
|
||||
|
||||
fn update_frame(&mut self, frame: Frame) {
|
||||
self.frame = frame;
|
||||
}
|
||||
}
|
||||
|
||||
impl Drawable for SpriteRef {
|
||||
|
@ -147,17 +142,20 @@ impl Drawable for SpriteRef {
|
|||
fn pixels(&self) -> &[u8] {
|
||||
&self.pixels
|
||||
}
|
||||
}
|
||||
|
||||
fn update_pixels(&mut self, pixels: Rc<Vec<u8>>) {
|
||||
self.pixels = pixels;
|
||||
}
|
||||
impl Animation for SpriteRef {
|
||||
fn animate(&mut self, assets: &Assets, dt: Duration) {
|
||||
if self.duration.subsec_nanos() == 0 {
|
||||
self.step_frame(assets);
|
||||
} else {
|
||||
self.dt += dt;
|
||||
|
||||
fn frame(&self) -> &Frame {
|
||||
&self.frame
|
||||
}
|
||||
|
||||
fn update_frame(&mut self, frame: Frame) {
|
||||
self.frame = frame;
|
||||
while self.dt >= self.duration {
|
||||
self.dt -= self.duration;
|
||||
self.step_frame(assets);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue