Hopefully you’ve had a chance to get started with setting up your environment and gone over the basic anatomy of a go program. Those two posts are foundational to getting started with the ebitengine game engine. First off, what is a game engine, especially in the case of ebitengine? What is a game engine When you get started bring your dream game into reality, I’m sure that you’re going to want it to run on as many platforms as possible.
Hopefully you’ve had a chance to get started with setting up your environment and gone over the basic anatomy of a go program. Those two posts are foundational to getting started with the ebitengine game engine.
First off, what is a game engine, especially in the case of ebitengine?
When you get started bring your dream game into reality, I’m sure that you’re going to want it to run on as many platforms as possible. But a correlation to that is that you’re not going to want to write all of the platform specific code to handle:
A game engine abstracts the cross platform realities of handling these features and provides a unified interface that you, as the developer, can program to that interface and the game engine will handle running your game.
Now, as the developer, you just need to learn the interface provided by your game engine, and you can target as many platforms as possible. That is a huge win.
I don’t want to completely reinvent the wheel, so lets first look at the ebitengine install guide (be sure to select your operating system of use at the top, if it isn’t correct). This will give you the steps that you need to install any needed build dependencies, create your game directory, initialize your go.mod file, and the basics needed to have a default game started.
So, jump over there quick, get your game started, and then be sure to come back here.
That install guide will give you some go code to run a basic game, lets look at it here.
// Package declaration
package main
// Import statements
import (
"log"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
)
type Game struct{}
func (g *Game) Update() error {
return nil
}
func (g *Game) Draw(screen *ebiten.Image) {
ebitenutil.DebugPrint(screen, "Hello, World!")
}
func (g *Game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) {
return 320, 240
}
// Func main
func main() {
ebiten.SetWindowSize(640, 480)
ebiten.SetWindowTitle("Hello, World!")
if err := ebiten.RunGame(&Game{}); err != nil {
log.Fatal(err)
}
}
If you remember our discussion about go anatomy, there are a few components that are common with each program that we work with.
Each of these are in the code above. They tell the go compiler that we’re in the main package and we have a start to our program with the func main.
There is a game struct that is declared. Remember, that a struct is a collection of data. But, we also have 3 (in the case of this example) methods declared on the game struct.
These three methods are defined as an interface in the ebitenengine codebase. This is the interface that is required to be satisfied on our game struct, so that it can be passed to the ebiten.RunGame call as seen in our code example above. That run game call has a game created via, &Game{}.
Once you call, RunGame, this will start what is known as the main game loop. It is known as a blocking call. So, unless there is an error, or you stop the game, this loop will run indefinitely.
So, how exactly does this indefinite game loop interact with your game?
When you call ebiten.RunGame, you pass in an instance of the game defined above. The game loop then interacts with your game via the interface methods defined. The game loop itself may have a lot of different steps, that we don’t really need to know about. These steps can be broken down to:
The game struct that you create, will get the update call. From there, every item within your game, will need to run its update logic. That then implies the update method is the hook into your game logic.
As you build out your game, you’ll need a reference to your game objects from the game object (i.e. player struct, enemy structs, projectile structs etc.). You’ll then run your logic for each of the components of your game here. It is recommended that your game objects have their own update method, which can be called from the main game update method.
The draw step then gets called on each of the game objects. Each object will know where it is located and how to draw itself on the screen (this screen is provided by the game engine each frame).
There is so much to cover with programming in general and a game engine specifically. There is no way that we can cover everything right here. But I hope this discussion gives you enough information to get your thoughts running and a place in the documentation to start learning from your own curiosities.