Ebitengine
Testing
This page was adapted from a blog that focused on cross-platform game development with WSL.
You can use Ubuntu to develop, build, and test a game for Windows, Linux, and the browser.
In this example, we'll use Ubuntu and the Ebitengine game engine for Go.
Set up the project
Install Go with snap:
sudo snap install go --classic
Make a Go project:
mkdir game && cd game
go mod init game
Write the code
Use a text editor to edit the game code:
vim main.go
# nano main.go
Paste this simple example, which bounces a ball around the window and changes the ball color based on the build target:
package main
import (
"fmt"
"image/color"
"log"
"runtime"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
"github.com/hajimehoshi/ebiten/v2/vector"
)
const (
screenW = 480
screenH = 360
ballRadius = 12
textPadX = 8
textPadY = 8
)
var label = fmt.Sprintf("This is a %s build", runtime.GOOS)
func ballColor() color.Color {
switch runtime.GOOS {
case "linux":
return color.RGBA{R: 255, G: 0, B: 0, A: 255}
case "windows":
return color.RGBA{R: 0, G: 0, B: 255, A: 255}
case "js":
return color.RGBA{R: 0, G: 255, B: 0, A: 255}
default:
return color.White
}
}
type Game struct {
x, y float64
vx, vy float64
}
func (g *Game) Update() error {
g.x += g.vx
g.y += g.vy
if g.x-ballRadius <= 0 || g.x+ballRadius >= screenW {
g.vx = -g.vx
} else if g.y-ballRadius <= 0 || g.y+ballRadius >= screenH {
g.vy = -g.vy
}
return nil
}
func (g *Game) Draw(screen *ebiten.Image) {
screen.Fill(color.Black)
ebitenutil.DebugPrintAt(screen, label, textPadX, textPadY)
vector.DrawFilledCircle(screen, float32(g.x), float32(g.y), ballRadius, ballColor(), true)
}
func (g *Game) Layout(_, _ int) (int, int) {
return screenW, screenH
}
func main() {
ebiten.SetWindowSize(screenW, screenH)
ebiten.SetWindowTitle("Crossplatform builds on Ubuntu")
log.Fatal(ebiten.RunGame(&Game{x: 40, y: 40, vx: 4, vy: 2.5}))
}
Resolve the Go dependencies:
go mod tidy
Build cross-platform
Windows build
Build for Windows:
GOOS=windows GOARCH=amd64 go build .
Linux build
To build for Linux, first install the dependencies required to compile the game for X11:
sudo apt update
sudo apt install \
libx11-dev \
libxrandr-dev \
libxcursor-dev \
libxinerama-dev \
libxi-dev \
libxxf86vm-dev \
libgl1-mesa-dev \
xorg-dev
Then build for Linux:
GOOS=linux GOARCH=amd64 go build .
Web build
To build for WebAssembly, run:
GOOS=js GOARCH=wasm go build -o game.wasm .
Next, copy wasm_exec.js to the current directory so that the WebAssembly binary can be executed:
# note: exact location depends on your Go version
cp $(go env GOROOT)/misc/wasm/wasm_exec.js .
Finally, create two HTML files: one that includes the game (game.html) and another that embeds the game in an iframe (index.html).
Game:
<!DOCTYPE html>
<script src="wasm_exec.js"></script>
<script>
const go = new Go();
WebAssembly.instantiateStreaming(fetch("game.wasm"), go.importObject).then(r => go.run(r.instance));
</script>
Embedder:
<!DOCTYPE html>
<style>
body { margin: 0; background: #555; display: flex; justify-content: center; align-items: center; height: 100vh; overflow: hidden; }
</style>
<iframe src="game.html" width="480" height="360" frameborder="0"></iframe>
Run the builds
You now have three builds of your game for different platforms.
Run on Linux
Run the Linux build:
./game
Run on web
To run the game in a browser, create a simple Python server to serve files from localhost:
python3 -m http.server -b localhost
Then open http://127.0.0.1:8000/ in your browser.
Run on Windows
The steps to run on Windows depend on whether you are using Ubuntu Desktop or Ubuntu on WSL:
With WSL, you can simply run the Windows executable directly from an Ubuntu session:
./game.exe
Result
The ball is a different color depending on the platform:
- Red: Linux
- Blue: Windows
- Green: Web