Skip to content

Commit

Permalink
Invaders Part 2: Rendering & Multithreading
Browse files Browse the repository at this point in the history
  • Loading branch information
CleanCut committed Jun 18, 2020
1 parent 0d3ca7b commit a78221c
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 2 deletions.
19 changes: 19 additions & 0 deletions src/frame.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use crate::{NUM_COLS, NUM_ROWS};

pub type Frame = Vec<Vec<&'static str>>;

pub fn new_frame() -> Frame {
let mut cols = Vec::with_capacity(NUM_COLS);
for _ in 0..NUM_COLS {
let mut col = Vec::with_capacity(NUM_ROWS);
for _ in 0..NUM_ROWS {
col.push(" ");
}
cols.push(col);
}
cols
}

pub trait Drawable {
fn draw(&self, frame: &mut Frame);
}
5 changes: 5 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pub mod frame;
pub mod render;

pub const NUM_ROWS: usize = 20;
pub const NUM_COLS: usize = 40;
63 changes: 61 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
use std::error::Error;
use crossterm::cursor::{Hide, Show};
use crossterm::event::{Event, KeyCode};
use crossterm::terminal::{EnterAlternateScreen, LeaveAlternateScreen};
use crossterm::{event, terminal, ExecutableCommand};
use invaders::frame::new_frame;
use invaders::{frame, render};
use rusty_audio::Audio;
use std::error::Error;
use std::sync::mpsc;
use std::time::Duration;
use std::{io, thread};

fn main() -> Result <(), Box<dyn Error>> {
fn main() -> Result<(), Box<dyn Error>> {
let mut audio = Audio::new();
audio.add("explode", "explode.wav");
audio.add("lose", "lose.wav");
Expand All @@ -11,7 +20,57 @@ fn main() -> Result <(), Box<dyn Error>> {
audio.add("win", "win.wav");
audio.play("startup");

// Terminal
let mut stdout = io::stdout();
terminal::enable_raw_mode()?;
stdout.execute(EnterAlternateScreen)?;
stdout.execute(Hide)?;

// Render loop in a separate thread
let (render_tx, render_rx) = mpsc::channel();
let render_handle = thread::spawn(move || {
let mut last_frame = frame::new_frame();
let mut stdout = io::stdout();
render::render(&mut stdout, &last_frame, &last_frame, true);
loop {
let curr_frame = match render_rx.recv() {
Ok(x) => x,
Err(_) => break,
};
render::render(&mut stdout, &last_frame, &curr_frame, false);
last_frame = curr_frame;
}
});

// Game loop
'gameloop: loop {
// Per-frame init
let mut curr_frame = new_frame();

// Input
while event::poll(Duration::default())? {
if let Event::Key(key_event) = event::read()? {
match key_event.code {
KeyCode::Esc | KeyCode::Char('q') => {
audio.play("lose");
break 'gameloop;
}
_ => {}
}
}
}

// Draw & render
let _ = render_tx.send(curr_frame);
thread::sleep(Duration::from_millis(1));
}

// Cleanup
drop(render_tx);
render_handle.join().unwrap();
audio.wait();
stdout.execute(Show)?;
stdout.execute(LeaveAlternateScreen)?;
terminal::disable_raw_mode()?;
Ok(())
}
23 changes: 23 additions & 0 deletions src/render.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use crate::frame::Frame;
use crossterm::cursor::MoveTo;
use crossterm::style::{Color, SetBackgroundColor};
use crossterm::terminal::{Clear, ClearType};
use crossterm::QueueableCommand;
use std::io::{Stdout, Write};

pub fn render(stdout: &mut Stdout, last_frame: &Frame, curr_frame: &Frame, force: bool) {
if force {
stdout.queue(SetBackgroundColor(Color::Blue)).unwrap();
stdout.queue(Clear(ClearType::All)).unwrap();
stdout.queue(SetBackgroundColor(Color::Black)).unwrap();
}
for (x, col) in curr_frame.iter().enumerate() {
for (y, s) in col.iter().enumerate() {
if *s != last_frame[x][y] || force {
stdout.queue(MoveTo(x as u16, y as u16)).unwrap();
print!("{}", *s);
}
}
}
stdout.flush().unwrap();
}

0 comments on commit a78221c

Please sign in to comment.