Create a Flappy Bird Clone With Golang P1

Posted in golang tutorials -

This weekend I decided to start another tutorial series in this blog, as I did learn a lot during making the last one. This time I decided to up the ante by picking up on a language that I am not very familiar with: Go Programming Language, or Golang. If it is the first time you heard of it, Go is a language designed at Google, is syntactically similar to C and is very well-designed to support concurrency. One of Go’s authors, Ken Thompson, was also one author of B language (together with Dennis Ritchie) (B was the direct predecessor of C Programming Language).

So far, I only have worked in one small side-project at work that using Golang. It was a long time ago, though, so I forgot most of the stuff I did learn (That was one of the reasons I want to have another go with Go this time, to refresh my memory). Another reason to use Go is the fact that despite its simple syntax (In my pov, it is as easy as Python and much less complicated than C or C++), Go is a lower level language than Python, so the speed will be much faster. (Here is an article explaining Go’s advantages over Python that you can explore if you want better explanation).

Upon googling with the keywords “Making games with golang” (I normally type “Golang” instead of “Go” in searching, since the word “Go” is so common in English), I found this library Ebiten which seems to be very well-documented. They also have several examples of several games that were built using this library, one of which is Flappy, which is quite similar to the Flappy Bird game we built last time (I like our version much better, though). You can even play those games in your browser, which was quite a shock to me (after exploring the documentation, I found that they use something called GopherJS to convert Go code to Javascript, another plus point for Go).

I did think long and hard on what game should we make with Go and Ebiten, but in the end I decided that remaking FlappyBird will be a good choice. Since we’ve made it before, we won’t have to deal with the game logic and can go straight into learning the language. Also the Flappy example source code looks quite intimidated for me, so I think we can write it in a more understandable way and with better graphics.

Part 1: Exploring around

The first thing we need to do is to install Go. Please click on this link and download the version according to your operating system. This part should be easy (it got a bit easier than when I installed Go the first time), so I won’t cover it. Also, install any other dependencies needed by Ebiten using instruction here. Create a directory called “FlappyBird” in your gopath (which is the directory that stores all your Go projects, normally it’s ~/go in UNIX-like systems. For Windows, unfortunately I don’t have any system to run the test, so you may need to run go run, e.g go run -tags=example github.com/hajimehoshi/ebiten/examples/rotate. The path where ebiten is downloaded to is your gopath.

Create a main.go file inside the newly created directory and open it. For now we would like to have a simple example game, which we can divide into smaller pieces and understand one piece at a time (just like the Bouncing Ball example we used to learn pygame). Luckily Ebiten has many examples. Let’s click on the Animation Basic.

Now copy the whole source code and paste it into the empty main.go that we created earlier. Let’s make sure we can run it.

go run main.go

go run is a way to quickly compile and run go code, which is similar to python command. It is normally used in development phase, so that we don’t have to recompile the code all the time. In production phase, we will use go build to compile the code to executable form.

If you get some error complaining that ebiten is not in gopath, run go get github.com/hajimehoshi/ebiten and try again. That problem was caused because for some reason, Ebiten was not downloaded automatically.

Otherwise, you should see a running guy in the middle of a black screen:

If you have got there, it means everything works. Great! Now let’s explore the code a bit.

Go, just like C, executes the code in a special function called main. Let’s start from there.

According to the comment at the beginning of the main() function, image.Decode is used to decode image from a byte slice. If you go to the github.com/hajimehoshi/ebiten/examples/resources/images directory inside your gopath, you will see a file called runner.go, in which a very long byte slice called Runner_png was defined. Shortly explain, this slice is the equivalent of the runner.png file (which is located in the same directory), in binary code. Since we will eventually want to use our own images (instead of having to convert every image into byte code), let’s try to use the runner.png image instead of decoding the byte slice.

In the aforementioned comment section, there are some suggestion for using files as images:

1) Use os.Open and pass the file to the image decoder.
   This is a very regular way, but doesn't work on browsers.
2) Use ebitenutil.OpenFile and pass the file to the image decoder.
   This works even on browsers.
3) Use ebitenutil.NewImageFromFile to create an ebiten.Image directly from a file.
   This also works on browsers.

You can feel free to explore the other options, but I will choose the third one, due to two reasons:

  1. It works on browsers.
  2. It doesn’t require Decoding image. According to ebitenutil Doc, NewImageFromFile returns *ebiten.Image, just like NewImageFromImage, so we can eliminate another step.

The code now looks like this:

    runnerImage, _, err := ebitenutil.NewImageFromFile("runner.png", ebiten.FilterDefault)
	if err != nil {
		log.Fatal(err)
	}

Notice that you have to copy the runner.png from github.com/hajimehoshi/ebiten/examples/resources/images to github.com/<your-github-username>/FlappyBird. Also, you should add github.com/hajimehoshi/ebiten/ebitenutil and removes excessive bytes from import part.

Now when we run the code, we get this error:

./main.go:75:5: runnerImage declared but not used

This problem was caused because runnerImage was initiated by the syntax :=. This is a difference between Go and Python, that there is different syntax for [assigning by reference and by value]. In this case, the runnerImage that created by ebitenutil.NewImageFromFile is different from the runnerImage initiated in the var section, which seems to be used in the Draw() function. Let’s test that theory by assigning runnerImage by reference instead.

    img, _, err := ebitenutil.NewImageFromFile("runner.png", ebiten.FilterDefault)
	if err != nil {
		log.Fatal(err)
	}
    runnerImage = img

This time, the code runs perfectly. Note that the documentation suggests to embed your resources instead of using NewImageFromImage(); however let’s worry about that later.

After loading the images, the main() function simply calls ebiten.RunGame(&Game{}). You can read about it more from here, but to summarise, changes in the game loop are made via the Game struct and the three functions Update(), Draw() and Layout(). Let’s care about them in the next post.

Written by Huy Mai