Last active
January 7, 2018 12:01
-
-
Save kwyse/1d6be3de1c95d05502e10b6dba3cc6be to your computer and use it in GitHub Desktop.
Demo application for Specs and SDL2 for https://www.kwyse.com/posts/grokking-ecs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extern crate sdl2; | |
extern crate specs; | |
#[macro_use] | |
extern crate specs_derive; | |
use sdl2::event::Event; | |
use sdl2::keyboard::Keycode; | |
use sdl2::rect::Rect; | |
use specs::{DispatcherBuilder, Join, System, World}; | |
use specs::{Fetch, ReadStorage, WriteStorage}; | |
use std::time::{Duration, Instant}; | |
const PIXELS_PER_UNIT: f64 = 100.0; | |
fn main() { | |
let context = sdl2::init().unwrap(); | |
let video = context.video().unwrap(); | |
let window = video.window("Grokking ECSs", 800, 600) | |
.position_centered() | |
.opengl() | |
.build() | |
.unwrap(); | |
let mut canvas = window.into_canvas().build().unwrap(); | |
let mut events = context.event_pump().unwrap(); | |
let mut world = World::new(); | |
world.add_resource(Duration::new(0, 0)); | |
world.register::<Position>(); | |
world.register::<Velocity>(); | |
world.register::<Sprite>(); | |
let initial_pos = Position { x: 2.0, y: 2.0 }; | |
let initial_vel = Velocity { x: 1.0, y: 0.0 }; | |
let sprite = Sprite(Rect::new( | |
(initial_pos.x * PIXELS_PER_UNIT) as i32, | |
(initial_pos.y * PIXELS_PER_UNIT) as i32, | |
32, | |
32 | |
)); | |
world.create_entity() | |
.with(initial_pos) | |
.with(initial_vel) | |
.with(sprite) | |
.build(); | |
let mut dispatcher = DispatcherBuilder::new() | |
.add(MovementSystem, "movement_system", &[]) | |
.add(RenderSystem, "render_system", &["movement_system"]) | |
.build(); | |
let mut prev = Instant::now(); | |
let mut elapsed = Duration::new(0, 0); | |
let fps = 60.0_f64; | |
let frame_duration = Duration::from_millis((1.0 / fps * 1_000.0) as u64); | |
'running: loop { | |
let curr = Instant::now(); | |
let dt = curr - prev; | |
prev = curr; | |
elapsed += dt; | |
if elapsed >= frame_duration { | |
elapsed -= frame_duration; | |
for event in events.poll_iter() { | |
if let Event::KeyDown { keycode, .. } = event { | |
if let Some(Keycode::Escape) = keycode { | |
break 'running; | |
} | |
} | |
} | |
dispatcher.dispatch(&mut world.res); | |
let mut delta_time = world.write_resource::<Duration>(); | |
*delta_time = dt; | |
canvas.clear(); | |
let sprites = world.read::<Sprite>(); | |
for sprite in sprites.join() { | |
canvas.fill_rect(sprite.0).unwrap(); | |
} | |
canvas.present(); | |
} else { | |
std::thread::sleep(frame_duration - elapsed); | |
} | |
} | |
} | |
#[derive(Component)] | |
struct Position { | |
x: f64, | |
y: f64, | |
} | |
#[derive(Component)] | |
struct Velocity { | |
x: f64, | |
y: f64, | |
} | |
#[derive(Component)] | |
struct Sprite(Rect); | |
struct MovementSystem; | |
impl<'a> System<'a> for MovementSystem { | |
type SystemData = ( | |
Fetch<'a, Duration>, | |
ReadStorage<'a, Velocity>, | |
WriteStorage<'a, Position> | |
); | |
fn run(&mut self, data: Self::SystemData) { | |
let (dt, velocities, mut positions) = data; | |
for (vel, pos) in (&velocities, &mut positions).join() { | |
pos.x += vel.x * dt.subsec_nanos() as f64 / 1_000_000_000.0; | |
pos.y += vel.y * dt.subsec_nanos() as f64 / 1_000_000_000.0; | |
} | |
} | |
} | |
struct RenderSystem; | |
impl<'a> System<'a> for RenderSystem { | |
type SystemData = (ReadStorage<'a, Position>, WriteStorage<'a, Sprite>); | |
fn run(&mut self, data: Self::SystemData) { | |
let (positions, mut sprites) = data; | |
for (pos, sprite) in (&positions, &mut sprites).join() { | |
sprite.0.set_x((pos.x * PIXELS_PER_UNIT) as i32); | |
sprite.0.set_y((pos.y * PIXELS_PER_UNIT) as i32); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment