Watch the associated video on YouTube or MakerTube

Introduction #
In this tutorial we will add the Ebitengine game engine as a dependency and use it to create a graphical application.
The code for this tutorial is available here. Please consider donating if you find this tutorial helpful.
The Ebitengine Game Engine #
Ebitengine is a game engine created in 2013 by Hajime Hoshi.
It provides APIs which greatly simplify the process of developing graphical applications using Go.
Ebitengine supports:
- Drawing 2D graphics
- Handling user input
- Playing audio
Reasons why I prefer to use Ebitengine to develop games include:
- Stability
- Simplicity
- Performance
- Wide platform support
Create a Go Module #
Follow the steps outlined in Getting Started with Go to create a Go module.
If you have completed the previous tutorial, you may use your existing Go module for this tutorial.
Add Ebitengine as a Dependency #
Open a terminal window and cd
to the directory of your Go module, then execute
the following command:
go get github.com/hajimehoshi/ebiten/v2
This will add some lines to go.mod
, such as:
require (
github.com/hajimehoshi/ebiten/v2 v2.8.8
)
In the above example, our module now depends on version 2.8.8 of Ebitengine.
Your module may depend on a newer version of Ebitengine.
Game Interface #
Ebitengine defines the following Game interface:
type Game interface {
Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int)
Update() error
Draw(screen *Image)
}
Layout #
Layout is called nearly every frame to determine the size of the application.
The current size of the application window is provided as outsideWidth
and outsideHeight
.
When the application is running in fullscreen mode, the size of the screen is provided.
The return value of Layout
is the size of the drawable screen area, or canvas size.
If the window is smaller or larger than the canvas, the application is scaled while maintaining the correct aspect ratio.
Update #
Update is called every tick to update the game state.
The rate at which Update
is called is determined by the tick rate.
The default tick rate is 60 ticks per second.
The tick rate is controlled by calling SetTPS.
Draw #
Draw is called every frame to draw the game onto the screen.
Layout
will always be called at least once before Draw
is called for the first time.
The default frame rate is unlimited, because the game runs without vertical synchronization enabled.
The frame rate is controlled by calling SetVsyncEnabled.
When a value of true
is provided, the frame rate is limited to the screen’s refresh rate.
Enabling vsync also prevents visual artifacts
from appearing on the screen.
Implementing the Game Interface #
To create a game using Ebitengine, we must implement the Game
interface.
To do this, we create a struct
which implements the three required methods.
Create a file named game.go
and write the following:
package main
This defines our package name as main
. All packages which produce an
executable, meaning they have a main
function, must be named main
.
When we create a package which other packages may import, such as a library, we will name the package based on the name of our library.
import (
"image/color"
"github.com/hajimehoshi/ebiten/v2"
)
Import the image/color and ebiten packages.
The ebiten
package import path ends in v2
to differentiate it from the
earlier v1
version of Ebitengine.
This package versioning scheme enables us to import different version of packages at the same time.
We will only be importing and using the v2
version of the Ebitengine package.
type game struct {
backgroundColor color.RGBA
}
Define a struct
named game
with one field named backgroundColor
.
The backgroundColor
field has the type color.RGBA.
color.RGBA
is a type which represents a color by specifying a red, green, blue and alpha value ranging from 0 to 255.
func newGame() *game {
return &game{
backgroundColor: color.RGBA{0, 0, 255, 255},
}
}
Define a constructor
function which creates and initializes a new game
.
The default value of a color.RGBA
is a color with red, green, blue and alpha set to zero.
Because the default alpha value is zero, the default value of color.RGBA
is a completely transparent color.
When initialing the game, we specify a blue value of 255 to produce a completely blue color.
We also specify an alpha value of to 255 to produce a fully opaque (non-transparent) color.
// Layout returns the size of the application. If the window is smaller or larger,
// the application is scaled while maintaining the correct aspect ratio.
func (g *game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) {
return 480, 270
}
Define a method named Layout
, which is called with the current window size,
and return the size of the canvas.
In this application, we specify a canvas size of 480x270. The canvas will be scaled to fit the application window size.
// Update updates the game state.
func (g *game) Update() error {
return nil
}
Define a method named Update
, which updates the game state.
We don’t have any game state to update yet, so this method simply returns nil
.
// Draw draws the game onto the screen.
func (g *game) Draw(screen *ebiten.Image) {
// Draw background.
screen.Fill(g.backgroundColor)
}
Define a method named Draw
, which draws the game onto the screen.
The canvas is provided as screen
, which we call Fill on. Fill
replaces all pixels in an image with the specified color.
main.go #
Create (or replace) a file named main.go
with the following code:
package main
import (
"log"
"github.com/hajimehoshi/ebiten/v2"
)
func main() {
ebiten.SetWindowTitle("Getting Started with Ebitengine - Trevors-Tutorials.com #3")
ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
err := ebiten.RunGame(newGame())
if err != nil {
log.Fatal(err)
}
}
We define our package name, just as we did in game.go
.
We import the log package, which provides useful logging functions.
We define a main
function, where our application’s execution will begin.
We call a few of Ebitengine’s functions to set the window title and to enable resizing the window.
And finally, we initialize a new game
and pass it to Ebitengine’s RunGame function.
RunGame
will block (wait to return) until an error occurs or the user requests to exit the application.
RunGame
will return an error value or nil
when no error occurred.
Running Our Application #
As we learned in the last tutorial,
we can execute go run
in the directory of our Go module to run our application.
We can also execute go build
to compile our application into an executable file,
which we can then distribute to others.
Once you have confirmed that your application builds and runs, try changing the background color to a different value.
Stay tuned for the next tutorial, Creating Your First Game with Ebitengine.
Please consider donating if you found this tutorial helpful.