1 of 40

Hajime Hoshi (星一) (@hajimehoshi)

English version

2 of 40

Who am I?

  • Hajime Hoshi (星一) @hajimehoshi
    • Software Engineer
  • Work: Non-game programming in C++
  • Hobby: Game programming in Go
  • Learned game programming myself
  • Wanted and tried developing a game, but I started to enjoy developing tools to develop games, and I’ve not finished developing games yet.

3 of 40

What I’m talking today

  • About Ebiten (海老天) - a 2D game library
    • What Ebiten is
    • Actual example
    • Implementation (Mainly how tough�the implemention was)

  • Feel free to ask questions anytime

Go Gopher by Renée Franch is licensed under CC BY 3.0

4 of 40

What Ebiten is

5 of 40

Ebiten - What Ebiten is

  • https://hajimehoshi.github.io/ebiten/
  • An extremely simple 2D game lib
    • About 50 functions for rendering and input
  • Multi-platform
  • Apache License 2.0

Desktops

Windows, macOS, Linux, FreeBSD

Browsers

Chrome, Safari, Firefox

Mobiles

Android, iOS

6 of 40

Ebiten - Features

Features

Graphics

Draw from a rect to a rect (basically that’s all!)�Geometry matrix, Color matrix, compositions, offscreen rendering, text rendering

Input

Keyboard, Touch, Mouse, Gamepad

Sound

Wav, MP3, Ogg/Vorbis, PCM (any io.Reader)

Sync with game progress

7 of 40

Ebiten - Mainloop

  • Specify a function called every frame to Run
  • *ebiten.Image representing the screen is passed to the function
  • In Ebiten, all images, offscreens, the screen are *ebiten.Image

func update(screen *ebiten.Image) error {

// Update

// Draw to the screen

return nil

}

func main() {

if err := ebiten.Run(update, 320, 240, 2,

“title”); err != nil {

log.Fatal(err)

}

}

8 of 40

Ebiten - Graphics

  • Affine matrix
  • Translation, scaling, and rotating are represented as one matrix
  • API without confusion

*ebiten.Image (render target)

*ebiten.Image (render source)

The photograph by Chris Nokleberg is licensed under CC BY 3.0

9 of 40

Ebiten - Demo

  • A kind of block-falling game
  • Works both on desktop and�browser with the same Go code

10 of 40

Actual example

11 of 40

Actual example - Clock of Atonement

  • 償いの時計 - Clock of Atonement
  • Android / iOS
  • Worked with @daigo
  • Introduced by Fami-tsu (ファミ通)
  • 15000 DL (Aug, 2017)
  • Got a little money

12 of 40

Actual example - Clock of Atonement

  • Developed a game editor as a web app
    • Like RPG Maker
  • Converted RPG Engine in Go to JavaScript with GopherJS
  • Tested the game on browsers

Ebiten (Go)

RPG Engine (Go)

Game Editor

(Web App)

Test Player�(Web App)

RPG Engine

(JS)

Game Data

(JSON)

13 of 40

Actual example - Clock of Atonement

  • Compiled the RPG engine with gomobile
  • Embedded the game data JSON into the mobile app

Ebiten (Go)

RPG Engine (Go)

Game Editor

(Web App)

Game Player

(Mobile App)

RPG Engine�(Native bin.)

Game Data

(JSON)

14 of 40

Implementation (Summary)

15 of 40

Implementation - Graphics

  • OpenGL
  • Ebiten is a 2D game lib but utilizes GPU

API

Go Bindings

Desktops

OpenGL

(GLFW)

github.com/go-gl/gl

github.com/go-gl/glfw

Browsers

WebGL

github.com/gopherjs/gopherjs

github.com/gopherjs/webgl

Mobiles

OpenGL ES

golang.org/x/mobile/gl

16 of 40

Implementation - Sound

  • https://github.com/hajimehoshi/oto (音)
    • A low-level library to play sound on multiple platforms
  • Used by other game libs (e.g. https://github.com/faiface/beep)

API

Desktops

winmm (Windows), OpenAL (macOS, FreeBSD)

ALSA (Linux)

Browsers

WebAudio

Mobiles

AudioTrack (Android), OpenAL (iOS)

17 of 40

Implementation - Audio decoder

  • Ported MP3 decoder after the patent problems were solved
  • MP3 works on any browsers and Ebiten uses the native MP3 decorders
    • Ogg doesn’t work on Safari natively

Go library

MP3

github.com/hajimehoshi/go-mp3 or browsers’

Ogg

github.com/jfreymuth/oggvorbis

Wav

(Implemented in Ebiten)

18 of 40

Implementation (Desktops)

19 of 40

Implementation (Desktops) - GLFW

  • OpenGL / GLFW
    • As GLFW works on multiplatforms, Ebiten automatically works on multiplatforms
      • Probably works on *BSD other than FreeBDS
    • github.com/go-gl/glfw: a go-gettable binding
  • Cgo is inevitable

http://www.glfw.org/

20 of 40

Implementation (Desktops) - Cgo

  • Cgo requires gcc/clang
    • Inevitable Cgo as long as GLFW is used
  • Windows - MinGW
    • Hard to install from the official site
    • Recommend TDM-GCC
    • http://tdm-gcc.tdragon.net/
  • macOS - clang
    • Type clang, and follow the instruction
  • Linux / FreeBSD - gcc/clang

21 of 40

Implementation (Browsers)

22 of 40

Implementation (Browsers)

  • GopherJS
    • Software to compile Go to JavaScript
    • Can call any JavaScript funcs from Go
    • Even goroutines work
    • Performance is not so good
  • Ebiten uses web technology like WebGL and�WebAudio

GopherJS Logo?

https://twitter.com/gopherjs

23 of 40

Implementation (Browsers) - GopherJS

package main��import (� "github.com/gopherjs/gopherjs/js"�)��func main() {� doc := js.Global.Get("document")� doc.Call("addEventListener", � "click",� func() {� println("clicked")� }� )�}

document.addEventListener(� 'click', function() {� console.log('clicked');� }�)

The above code represents a semantically-equivalent code, and actual compilation results will not be so clean.

24 of 40

Implementation (Browsers) - Performance

  • Take profile
    • Easiest to do compared to other platforms
  • Avoid copying structs
  • Avoid locks, channels and goroutines
    • Might cause setTimeout
  • Avoid defer
    • defer is 25x slower. Actually this is slow with the original Go (gopherjs/gopherjs#648)
  • Avoid reflection (like encoding/json)
    • We are trying to change JSON to MessagePack in our game engine for Clock of Atonement

25 of 40

Implementation (Browsers) - GopherJS status

  • GopherJS is still unstable but developed very actively
    • Bug when using complex128 as a map keys (gopherjs/gopherjs#187)
    • Bug in ‘shift’ operation with a uint variable (gopherjs/gopherjs#418)
    • str[idx] doesn’t cause ‘out of index’ error (gopherjs/gopherjs#543)
    • (a-b)/c becomes a - b/c (gopherjs/gopherjs#649)
      • Found June this year
      • I fixed this and became a contributor!

26 of 40

Implementation (Mobiles)

27 of 40

Implementation (Mobiles) - gomobile

  • gomobile (golang.org/x/mobile)
    • Tools to compile Go progrsm for Android/iOS
  • gomobile build
    • Create an application (Android: *.apk, iOS: *.ipa)
  • gomobile bind
    • Create a library (Android: *.aar, iOS: *.framework)

28 of 40

Implementation (Mobiles) - gomobile

  • Only gomobile bind is available with Ebiten
    • Need to write Java/Objective-C to some extent
  • gomobile build is inflexible
    • Creates an app with only one OpenGL surface
    • Lacks features for actual apps in the stores
      • Signature, advertisements, collaboration with other apps, etc.

Command

Output

Pros

Cons

gomobile build

Apps

Easy

Inflexible

gomobile bind

Dynamic libs

Flexible

Not easy, Requires other langs

29 of 40

Implementation (Mobiles) - Difficulty

  • Detecting the culprit is hard
    • Ebiten is wrong?
    • gomobile is wrong?
    • Go itself is wrong?
    • GPU is wrong?
  • Context losts
  • Performance tuning

30 of 40

Implementation (Mobiles) - Bugs (1/4)

  • Usage of OpenGL
    • Unforced errors with initializing consts (c07f16d9)
      • OpenGL doesn’t tell what is wrong :-(
      • The screen is black until I noticed the problem
      • Solve with guts
    • glTexSubImage2D doesn’t work (0c611d87)
      • glFlush solved that
    • iOS’s target framebuffer is non-0 (5eb77d2f)
      • The screen is black until I noticed the problem

31 of 40

Implementation (Mobiles) - Bugs (2/4)

  • Various GPU behaviors
    • Can’t compare two mat4s with == (24bb5b5c)
      • Found iOS first time. Why?
    • Divide by 0 (f537378f)
    • Crash on Kindle Fire (hajimehoshi/ebiten#390)

32 of 40

Implementation (Mobiles) - Bugs (3/4)

  • Bugs in gomobile
    • Wrong arguments in a GL function (golang/go#14403)
    • int is treated as 32bit on ObjC side via gomobile bind (golang/go#16182)
  • Buts in Go
    • 100 < 1 is true on ARM 32bit (golang/go#19403) (hajimehoshi/ebiten#329)
      • Casting int -> float32 16 times in a row caused that

33 of 40

Implementation (Mobiles) - Bugs (4/4)

  • Bugs in Ebiten due to gomobile bind (0ee6b440)
    • Ebiten’s game is embedded as a dynamic lib with gomobile bind
    • init function was called unexpectedly → crash!
      • Global variables that should be set from Java/JNI were uninitialized unexpectedly
      • No clue other than that “init was called” from the stack trace
    • Lesson: init functions in a dynamic lib in Go can be called before any function in the lib is called in the first time
  • You can’t expect that init is called as you expect

34 of 40

Implementation (Mobiles) - Context Lost

  • OpenGL ES causes context lost
    • Actually WebGL causes the same thing, but it was so rare that I couldn’t notice that
  • *ebiten.Image pixel data was only on GPU
  • All data on GPU is cleared
    • Crash when switching an app
    • Crash when turning the power off

35 of 40

Implementation (Mobiles) - Context Lost

  • Adopted B
  • Record draw commands as much as possible, and restore with them
  • Now Ebiten users don’t have to care about context lost

Idea

Pros

Cons

A) Notice the user to recreate images

Easy for Ebiten

Impose burden on users�Break the compatibility

B) Ebiten tries to restore the game

Easy for users

Make Ebiten impl. complicated �Bad performance

36 of 40

Implementation (Mobiles) - Performance Tuning

  • Android
    • The tools are not useful for native binaries
      • Go with println
    • Unstable measurement results
      • Depends on e.g. if the device is connected with power supply or not
    • Take relative time instead of absolute time
      • Take three time points ABC, then calculate (the average of) (B-A) / (C-A)
    • See the detail in an article in Qiita (Japanese)
  • iOS: Easy since the tools were useful
  • Reduce draw calls anyway (same as general OpenGL progamming)

37 of 40

Conclusion

38 of 40

How is game developing in Go?

  • Go doesn’t change the situation extremely
    • Depends on your preference
  • Does GC matter? → Not sure
    • GC is not problematics for us so far
    • Because Go’s GC works very well or�we’ve not developed so huge games that GC matters
  • Small number of games in Go
    • A little while ago: There are more game libs in Go than games in Go…
    • Now the number of games has been increasing e.g. thanks to Gopher Game Jam
    • However, I’d like to see more games in Go

39 of 40

Conclusion

  • Ebiten - A simple 2D game library
    • Actual example: Clock of Atonement
    • Desktops
      • Use GLFW, that requires Cgo
    • Browsers
      • Use GopherJS
    • Mobiles
      • Use gomobile bind. The toughest implementation
  • I wish there would be more games in Go

40 of 40

Thank You!