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.