Add delta time steps and a placeholder for control inputs

This commit is contained in:
Jay Oster 2019-10-07 01:00:33 -07:00
parent 960bfb9b3f
commit 348533ee9a
3 changed files with 83 additions and 22 deletions

View file

@ -1,5 +1,7 @@
use std::time::Instant;
use pixels::{Error, Pixels, SurfaceTexture}; use pixels::{Error, Pixels, SurfaceTexture};
use simple_invaders::{World, SCREEN_HEIGHT, SCREEN_WIDTH}; use simple_invaders::{Controls, Direction, World, SCREEN_HEIGHT, SCREEN_WIDTH};
use winit::event; use winit::event;
use winit::event_loop::{ControlFlow, EventLoop}; use winit::event_loop::{ControlFlow, EventLoop};
@ -26,6 +28,7 @@ fn main() -> Result<(), Error> {
let surface_texture = SurfaceTexture::new(width, height, &surface); let surface_texture = SurfaceTexture::new(width, height, &surface);
let mut fb = Pixels::new(224, 256, surface_texture)?; let mut fb = Pixels::new(224, 256, surface_texture)?;
let mut invaders = World::new(); let mut invaders = World::new();
let mut last = Instant::now();
event_loop.run(move |event, _, control_flow| match event { event_loop.run(move |event, _, control_flow| match event {
event::Event::WindowEvent { event, .. } => match event { event::Event::WindowEvent { event, .. } => match event {
@ -43,7 +46,18 @@ fn main() -> Result<(), Error> {
_ => (), _ => (),
}, },
event::Event::EventsCleared => { event::Event::EventsCleared => {
invaders.update(); // Get a new delta time.
let now = Instant::now();
let dt = now.duration_since(last);
last = now;
// TODO: Keyboard and controller input.
let controls = Controls {
direction: Direction::Still,
fire: false,
};
invaders.update(dt, controls);
window.request_redraw(); window.request_redraw();
} }
_ => (), _ => (),

View file

@ -0,0 +1,17 @@
/// Player control inputs.
pub struct Controls {
/// Move the player.
pub direction: Direction,
/// Shoot the cannon.
pub fire: bool,
}
/// The player can only move left or right, but can also be stationary.
pub enum Direction {
/// Do not move the player.
Still,
/// Move to the left.
Left,
/// Move to the right.
Right,
}

View file

@ -1,9 +1,19 @@
mod loader; //! A simple Space Invaders clone to demonstrate `pixels`.
mod sprites; //!
//! This doesn't use anything fancy like a game engine, so you may not want to build a game like
//! this in practice. That said, the game is fully functional, and it should not be too difficult
//! to understand the code.
use std::time::Duration;
pub use controls::{Controls, Direction};
use loader::{load_assets, Assets}; use loader::{load_assets, Assets};
use sprites::{blit, Sprite, SpriteRef, Sprites}; use sprites::{blit, Sprite, SpriteRef, Sprites};
mod controls;
mod loader;
mod sprites;
/// The screen width is constant (units are in pixels) /// The screen width is constant (units are in pixels)
pub const SCREEN_WIDTH: usize = 224; pub const SCREEN_WIDTH: usize = 224;
/// The screen height is constant (units are in pixels) /// The screen height is constant (units are in pixels)
@ -25,6 +35,7 @@ pub struct World {
score: u32, score: u32,
assets: Assets, assets: Assets,
screen: Vec<u8>, screen: Vec<u8>,
timing: Duration,
} }
/// A tiny position vector /// A tiny position vector
@ -132,11 +143,47 @@ impl World {
score: 0, score: 0,
assets, assets,
screen, screen,
timing: Duration::default(),
} }
} }
/// Update the internal state. /// Update the internal state.
pub fn update(&mut self) { ///
/// # Arguments
///
/// * `dt`: The time delta since last update.
/// * `controls`: The player inputs.
pub fn update(&mut self, dt: Duration, controls: Controls) {
let one_frame = Duration::from_secs_f64(1.0 / 60.0);
// Advance the timer by the delta time
self.timing += dt;
// Step the game logic by one frame at a time
while self.timing >= one_frame {
self.timing -= one_frame;
self.step(&controls);
}
}
/// Draw the internal state to the screen.
pub fn draw(&mut self) -> &[u8] {
// Clear the screen
self.clear();
// Draw the invaders
for row in &self.invaders.grid {
for col in row {
if let Some(invader) = col {
blit(&mut self.screen, &invader.pos, &invader.sprite);
}
}
}
&self.screen
}
fn step(&mut self, _controls: &Controls) {
// Find the next invader // Find the next invader
let mut invader = None; let mut invader = None;
while let None = invader { while let None = invader {
@ -159,23 +206,6 @@ impl World {
invader.sprite.update_frame(frame); invader.sprite.update_frame(frame);
} }
/// Draw the internal state to the screen.
pub fn draw(&mut self) -> &[u8] {
// Clear the screen
self.clear();
// Draw the invaders
for row in &self.invaders.grid {
for col in row {
if let Some(invader) = col {
blit(&mut self.screen, &invader.pos, &invader.sprite);
}
}
}
&self.screen
}
/// Clear the screen /// Clear the screen
fn clear(&mut self) { fn clear(&mut self) {
for (i, byte) in self.screen.iter_mut().enumerate() { for (i, byte) in self.screen.iter_mut().enumerate() {