ggez Tutorial

Lesson 0 - Setting up ggez

Let's set up a project that uses ggez and does nothing more than displaying a blank window.

Declare the dependency

Create a new project with cargo new hello-ggez.

Add ggez to Cargo.toml:

[dependencies]
ggez = "0.6.0-rc1"

We need to use the release candidate because the latest stable version (0.5.1) doesn't work with the latest Rust .

Run cargo build.

What to expect

The first build will take a few minutes because there are a few dependencies, and they all have dependencies of their own and so on. All these need to be built. Subsequent builds will be faster.

The target directory of your project will be about 3.0G in size if you build for both debug and release. The binary will be about 127M for debug and about 9.5M for release (5.2M if you strip it, and some further optimizations might be possible), but it is self-contained and you don't need to distribute any dependencies along with it.

Basic Game Structure

A typical game loop goes like this:

  • handle input events (from keyboard, mouse, joystick...)
  • update the internal game state (position of the hero, monster, ball, ...)
  • render the new state of the game to the screen

For the game state, define a struct. It may be empty for now; we'll add fields as needed.

struct MyGame {}

From ggez's side, we'll need a Context, which is an object that provides access to the hardware: video for displaying the game, audio for playing sounds, keyboard for input and so on. A Context can be obtained from a ContextBuilder. To make a ContextBuilder you'll need a game ID. This will be used for the name of a directory in ~/.local/share on the player's computer, where game resources can be stored (see the filesystem module). You also need to provide a name for the game's author, but that's not used for GNU/Linux.

use ggez::ContextBuilder;

fn main() {
    let (ctx, event_loop) = ContextBuilder::new("game_id", "author")
        .build()
        .expect("Could not create ggez context!");
}

To use your game state struct in the game loop, implement the EventHandler trait. Two methods are required: update() and draw(). You'll notice they both take a Context as an input parameter.

In the update() method we don't do anything for now, since all we want to do is to display a window and we don't care about any events.

In the draw() method, let's clear() the window to the white color, and then call present() to send its contents to the screen.

use ggez::event::EventHandler;
use ggez::graphics;
use ggez::{Context, GameResult};

impl EventHandler for MyGame {
    fn update(&mut self, _ctx: &mut Context) -> GameResult<()> {
        Ok(())
    }

    fn draw(&mut self, ctx: &mut Context) -> GameResult<()> {
        graphics::clear(ctx, graphics::Color::WHITE);
        graphics::present(ctx)
    }
}

Finally, to link your EventHandler-enabled game state with the ggez Context, let's call event::run().

use ggez::event;

fn main() {
    // --snip--
    event::run(ctx, event_loop, my_game)
}

Putting It All Together

This is how everything looks in the end:

use ggez::event::{self, EventHandler};
use ggez::graphics;
use ggez::{Context, ContextBuilder, GameResult};

fn main() {
    let (ctx, event_loop) = ContextBuilder::new("game_id", "author")
        .build()
        .expect("Could not create ggez context!");
    let my_game = MyGame {};
    event::run(ctx, event_loop, my_game)
}

struct MyGame {}

impl EventHandler for MyGame {
    fn update(&mut self, _ctx: &mut Context) -> GameResult<()> {
        Ok(())
    }

    fn draw(&mut self, ctx: &mut Context) -> GameResult<()> {
        graphics::clear(ctx, graphics::Color::WHITE);
        graphics::present(ctx)
    }
}

Run this with cargo run and a white window should appear on the screen.