ggez Tutorial

Lesson 1 - Displaying an Image

Given a bare ggez project like the one built in Lesson 0 let's load an image and display it in the game window.

The resource path

In order to display an image in the game, we need to have it stored somewhere. At runtime, ggez will look for resources in the resources/ directory next to the executable (not in $PWD), or in resources.zip in the same place or in ~/.local/share/<gameid>/. See also the documentation of the filesystem module and the FAQ entry about resource paths. You'll probably want to have a resources directory in the project's root and create a symbolic link to it in target/debug.

    
.
├── Cargo.toml
├── resources
│   └── my_image.png
├── src
│   └── main.rs
└── target
    └── debug
        └── resources -> ../../resources/
    
      

Extra dependencies

We will need the Vec2 struct from the glam crate to hold (x, y) points.

Add glam to the dependencies list in Cargo.toml. The same version that ggez uses is a safe bet.

Loading the image

To load an image resource we'll create a new Image using a mutable reference to the Context and an absolute path to the image (as expected by the filesystem module).

let image1 = graphics::Image::new(ctx, "/my_image.png")?;

The image might be needed for the entire duration of the game, so a good owner for it might be a field of the game state struct. Also, we want to load the image as soon as the game starts, so we could do it in the new() function of the game state. Since Image::new() takes a &mut Context, so will the new() function.

struct MyGame {
    image1: graphics::Image,
}

impl MyGame {
    pub fn new(ctx: &mut Context) -> GameResult<MyGame> {
        let image1 = graphics::Image::new(ctx, "/my_image.png")?;
        let g = MyGame { image1 };
        Ok(g)
    }
}

Then in main(), where we create MyGame, we can replace:

let my_game = MyGame {};

with:

let my_game = MyGame::new(&mut ctx).unwrap();

Of course, in order to borrow ctx as mutable, we need to declare it as mutable, so we need to also change:

let (ctx, event_loop) = ContextBuilder::new(//...

to:

let (mut ctx, event_loop) = ContextBuilder::new(//...

Blitting the image

In the draw() function, let's blit the image at a fixed position of (20, 10) (20 pixels from the left and 10 pixels from the top). In a real game, we would probably want a dynamic position, computed inside the update() function, but this is for a future lesson. For now, let's keep it simple.

Once we have the position variable set up, we can call graphics::draw with the context, the image and the destination position.

use ggez::graphics::{self, DrawParam};

use glam::Vec2;

impl EventHandler for MyGame {
    // --snip--
    fn draw(&mut self, ctx: &mut Context) -> GameResult<()> {
        let my_dest = Vec2::new(20.0, 10.0);

        graphics::clear(ctx, graphics::Color::WHITE);
        graphics::draw(ctx, &self.image1, DrawParam::default().dest(my_dest))?;
        graphics::present(ctx)
    }
}

And now we should have the image displayed on the window.