Skip to content

Instantly share code, notes, and snippets.

@kwyse
Last active January 7, 2018 12:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kwyse/1d6be3de1c95d05502e10b6dba3cc6be to your computer and use it in GitHub Desktop.
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
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