Simon Heath

(aka icefox, icefoz, icefoxen, whatever username is free)

Last updated: June 18th, 2017

Outline

  • Rust game engines
  • Goals for ggez
  • Structure of ggez
  • Code!
  • Future plans

ggez is

  • a lightweight 2D game framework
  • made my Sven-Hendrik Haase, Simon Heath, and contributers
  • MIT licensed
  • built on lots of existing crates
  • probably mostly stable by now

Rust game engines

Roll your own

Our Goals

svenstaro:

  • Simple
  • Useful for game jams
  • Actually exists

Me:

  • Easy to start using
  • Good documentation
  • Actually exists

Good artists copy, great artists steal

  • Wanted an API like LÖVE
  • So I copied it
  • Literally function by function

So what is LÖVE

  • Game framework for Lua, written mostly in C++
  • 10 major releases starting in 2008
  • Designed for 2D games, very easy to use
  • Mostly stable since 0.7 (2010), additions and alterations to the same core

What makes ggez

  • Graphics (gfx-rs, lyon, rusttype)
  • Sound (rodio)
  • Windowing + events (SDL2)
  • Filesystem (appdirs, std)
  • Configuration (serde)
  • Timing (std)

And documentation

What DOESN'T make ggez

  • 3d rendering system
  • Entity-component system
  • Animation
  • Complicated shader stuff
  • A million billion majillion interconnected modules
  • A math library
  • Thread safety (sorta)
  • The kitchen sink

KISS

  • Makes it possible to actually build ggez
  • Include the stuff that any game will need
  • Nothing unnecessary
  • Flexible

The EventHandler trait

pub trait EventHandler {
    fn update(&mut self, 
                ctx: &mut Context, 
                dt: Duration) -> GameResult<()>;
    fn draw(&mut self, ctx: &mut Context) -> GameResult<()>;

    fn mouse_button_down_event(
        &mut self, 
        button: MouseButton, 
        x: i32, 
        y: i32
    ) { ... }
    fn mouse_button_up_event(&mut self, 
        button: MouseButton, 
        x: i32, y: i32) { ... }
    fn mouse_motion_event(
        &mut self, 
        state: MouseState, 
        x: i32, 
        y: i32, 
        xrel: i32, 
        yrel: i32
    ) { ... }
    fn mouse_wheel_event(&mut self, 
        x: i32, y: i32) { ... }
    fn key_down_event(&mut self, 
        keycode: Keycode, 
        keymod: Mod, 
        repeat: bool) { ... }
    fn key_up_event(&mut self, 
        keycode: Keycode, 
        keymod: Mod, 
        repeat: bool) { ... }
    fn controller_button_down_event(&mut self, 
        btn: Button) { ... }
    fn controller_button_up_event(&mut self, 
        btn: Button) { ... }
    fn controller_axis_event(&mut self, 
        axis: Axis, value: i16) { ... }
    fn focus_event(&mut self, gained: bool) { ... }
    fn quit_event(&mut self) -> bool { ... }
}
extern crate ggez;
use ggez::{GameResult};

struct MainState {
    frames: usize,
}

impl MainState {
    fn new() -> GameResult<MainState> {
        let s = MainState {
            frames: 0,
        };
        Ok(s)
    }
}

fn main() {
    let state = &mut MainState::new()
        .expect("Failed to create MainState");
}
extern crate ggez;
use ggez::conf;
use ggez::event;
use ggez::{GameResult, Context};
use std::time::Duration;

struct MainState {
    frames: usize,
}

impl MainState {
    fn new() -> GameResult<MainState> {
        let s = MainState {
            frames: 0,
        };
        Ok(s)
    }
}


impl event::EventHandler for MainState {
    fn update(&mut self, _ctx: &mut Context, _dt: Duration) 
        -> GameResult<()> {
        if (self.frames % 100) == 0 {
            println!("FPS: {}", ggez::timer::get_fps(ctx));
        }
        self.frames += 1;
        Ok(())
    }

    fn draw(&mut self, ctx: &mut Context) 
        -> GameResult<()> {
        Ok(())
    }
}

fn main() {
    let c = conf::Conf::new();
    let ctx = &mut Context::load_from_conf(
            "helloworld", 
            "ggez", c)
        .expect("Failed to create Context");
    let state = &mut MainState::new(ctx)
        .expect("Failed to create MainState");
    event::run(ctx, state)
        .expect("Runtime error")
}
extern crate ggez;
use ggez::conf;
use ggez::event;
use ggez::{GameResult, Context};
use ggez::graphics;
use std::time::Duration;

struct MainState {
    text: graphics::Text,
    frames: usize,
}

impl MainState {
    fn new(ctx: &mut Context) -> GameResult<MainState> {
        let font = graphics::Font::new(
            ctx, "/DejaVuSerif.ttf", 48)?;
        let text = graphics::Text::new(
            ctx, "Hello world!", &font)?;

        let s = MainState {
            text: text,
            frames: 0,
        };
        Ok(s)
    }
}


impl event::EventHandler for MainState {
    fn update(&mut self, ctx: &mut Context, _dt: Duration) 
        -> GameResult<()> {
        if (self.frames % 100) == 0 {
            println!("FPS: {}", ggez::timer::get_fps(ctx));
        }
        self.frames += 1;
        Ok(())
    }

    fn draw(&mut self, ctx: &mut Context) 
        -> GameResult<()> {
        graphics::clear(ctx);
        let dest_point = graphics::Point::new(
            320.0,
            240.0);
        graphics::draw(ctx, &self.text, dest_point, 0.0)?;
        graphics::present(ctx);
        Ok(())
    }
}

fn main() {
    let c = conf::Conf::new();
    let ctx = &mut Context::load_from_conf(
            "helloworld", "ggez", c)
        .expect("Failed to create Context");
    let state = &mut MainState::new(ctx)
        .expect("Failed to create MainState");
    event::run(ctx, state)
        .expect("Runtime error")
}

The future!

Future work

  • Fix stuff
  • Make stuff better
  • Add stuff
  • Make it rad

Oh you wanted specifics

Fix stuff

  • It all works perfectly, of course

Make stuff better

  • Enhance sound -- rodio
  • Enhance drawing -- lyon

Add stuff

  • Sprite batches
  • Shaders
  • Expose gfx-rs

Make it rad

  • Support more platforms
  • Make it pure Rust

Getting stuff done in rust

  • It's fast enough, get on with life
  • The compiler is really good at optimizing
  • unsafe is your friend
  • Explicit lifetimes are your enemy
  • KISS
  • Writing Rust libraries is hard

Questions?