Skip to content

Tutorial:Screen, colors and squares

James Ray edited this page May 26, 2018 · 10 revisions

You've already learned things about how to make a basic game with Ebiten in the last tutorial, but that cannot be counted as an actual game because it can only show the text, thus we would need to learn more things about the screen before we make a real game, and that's what we'll talk about in this chapter.

The game screen is the most important place in the game. It displays everything you can see, and the screen is always refreshed but the speed is too fast so it might have been refreshed million times before you notice.

Let's continue the example from the last tutorial:

package main

import (
    "github.com/hajimehoshi/ebiten"
    "github.com/hajimehoshi/ebiten/ebitenutil"
)

func update(screen *ebiten.Image) error {
    // Display the text though the debug function
    ebitenutil.DebugPrint(screen, "Our first game in Ebiten!")
    return nil
}

func main() {
    // Initialize Ebiten, and loop the update() function
    if err := ebiten.Run(update, 320, 240, 2, "Hello world!"); err != nil {
        panic(err)
    }
}

Remember the code above will display something like this:

It seems a little bit boring. Let's fill the screen with some colors.

Color struct

Before you color your game screen, you need to create a color struct. It's quite simple. you might already heard about the Hex color (for example: #FF0000) if you have experience with the photo editor or the webpage design software before.

You will need to import the image/color package to create the color struct by replacing the import section of your code to this:

import (
    "image/color"

    "github.com/hajimehoshi/ebiten"
    "github.com/hajimehoshi/ebiten/ebitenutil"
)

It's time to create a color. Let's adopt red color here. The hex of the red is #FF0000, so it will be 0xff, 0x00, 0x00 in our code, and it looks like this (the last 0xff is alpha value).

color.NRGBA{0xff, 0x00, 0x00, 0xff}

Now you know how to create a new color, and it's time to paint your game screen.

Fill the screen with the color

There is a Fill(clr color.Color) function for the screen in Ebiten. It is used to fill the screen with a color, and you will need to pass a color.Color value to the function. That's exactly what our color struct returns.

Remember the update() function. The function which keeps being called by Ebiten, and we'll paint the screen in the function.

func update(screen *ebiten.Image) error {
    
    // Fill the screen with #FF0000 color
    screen.Fill(color.NRGBA{0xff, 0x00, 0x00, 0xff})
    
    // Display the text though the debug function
    ebitenutil.DebugPrint(screen, "Our first game in Ebiten!")
    
    return nil
}

You need to care about the priority of the codes above. You will need to fill the color before you display the text, otherwise the text will be covered by the color (so you couldn't see the text).

And save your file, run go run ./main.go in your terminal, you'll see the following result:

Creating squares

In the next few steps, we'll create some squares, so the game will be more game-like. The squares won't be able to move at least now, however, in the next few chapters it will be able to.

Squares are just images

There're actually no squares in Ebiten. Everything is an image (including the screen, it's just bigger), so what we'll do next, is just creating a new image, and make it looks like a square.

Fill the new image with the color

As we said before, the squares are just images. You can create a new image by using ebiten.NewImage(width, height int, filter Filter) in Ebiten, here are the values that you'll need to provide to the function:

  • width is the width of the new image with the pixels.
  • height is the height of the new image with the pixels.
  • filter is the filter for rendering the image, we won't use it at this chapter, so just do what we do and it'll be okay.

In the following example, we'll create an 16x16 image, it will look like a common square, and the image will be saved into the square variable, so we can use it later. Note that square is defined as a global variable since creating an image is an expensive operation so we should avoid to call this many times. In this example, only one square is necessary. In update function, fill the square with white color.

var square *ebiten.Image

func update(screen *ebiten.Image) error {
    // ...
    if square == nil {
        // Create an 16x16 image
        square, _ = ebiten.NewImage(16, 16, ebiten.FilterNearest)
    } 

    // Fill the square with the white color
    square.Fill(color.White)

    // ...
}

You might be wondering about what is color.White, you can actually found it in the image/color package, the package provided two default colors, which are BLACK (color.Black) and WHITE (color.White).

Reposition and render the image to the game screen

You will need to render the new image to the main screen once you've created it, otherwise it won't be displayed on the screen, but before you do it, you will also need to create a "render option struct" which's named as DrawImageOptions{}, which tells Ebiten how should the image should be drawn on the screen. We'll talk about it later, and right now, we can leave it as blank.

Here's the fully example.

var square *ebiten.Image

func update(screen *ebiten.Image) error {
    
    // Fill the screen with #FF0000 color
    screen.Fill(color.NRGBA{0xff, 0x00, 0x00, 0xff})
    
    // Display the text though the debug function
    ebitenutil.DebugPrint(screen, "Our first game in Ebiten!")

    if square == nil {
        // Create an 16x16 image
        square, _ = ebiten.NewImage(16, 16, ebiten.FilterNearest)
    } 

    // Fill the square with the white color
    square.Fill(color.White)

    // Create an empty option struct
    opts := &ebiten.DrawImageOptions{}
    
    // Draw the square image to the screen with an empty option
    screen.DrawImage(square, opts)
    
    return nil
}

DrawImage(image *Image, options *DrawImageOptions) is the function that draws the image to the screen. It's simple to use. You will need to pass the new image which will be rendered on the screen to the first parameter, and the second one is the render option struct which we created above, and that's it.

Now run your game with go run ./main.go command in your terminal after you saved it.

Our text is covered by the new image. We'll need to set the position of the image to avoid the such issue.

Image transforming

To put an image at a position as you like, there's only one way to specify geometry matrix when drawing an image. The geometry matrix is affine matrix and you can translate, enlarge or rotate the image with it.

What we need is to add the translate effect to the DrawImageOptions struct by using Translate(tx, ty float64) function. float64 is the floating point data type, and here're the two parameters that you'll need to pass to the function:

  • tx Is the distance from the left. It's also called as the x offset.
  • ty Is the distance from the top. It's also called as the y offset.

So right now, let's add another code to tell the render function that we want to translate the new image while rendering it.

    // The previous empty option struct
    opts := &ebiten.DrawImageOptions{}
    
    // Add the Translate effect to the option struct.
    opts.GeoM.Translate(64, 64)

Save your file, execute your game, and you'll see the new image is 64 pixels far from the top and the left of the screen.

By the way, you can also translate your image multiple times and fill different colors to get the following result.

You can’t perform that action at this time.