An important part of game development is adding a HUD (Heads Up Display). This helps keep your players informed throughout the game. What are some things that you may want in your HUD? Current Level Score Collectibles status Buffs/Nerfs Other things that are specific to your game If you’ve not been following along with the posts, I’ve been teaching a game development class at a local library. We’ve been building a clone of Sink Sub.
An important part of game development is adding a HUD (Heads Up Display). This helps keep your players informed throughout the game.
What are some things that you may want in your HUD?
If you’ve not been following along with the posts, I’ve been teaching a game development class at a local library. We’ve been building a clone of Sink Sub. In this game we need to keep track of score and display it to the user. If you want to see the game play as it is now, April 2024, you can check it out here.
Before we dig into the actual code, lets take a look at the algorithm for getting our text ready for display.
When needing to load a font face, we’ll be using an API described in the ebitengine text/v2 docs. There is a method defined here, NewGoTextFaceSource. This method takes an io.Reader and returns a *text.GoTextFaceSource and an error.
Currently, I’m just using a font provided by the ebitengine example source, fonts.MPlus1pRegular_ttf. This variable is a slice of bytes (loaded from the embed command). This is the pulled into a bytes.NewReader, which takes a slice of bytes, loads them in memory, and prepares it as a reader to be sent to functions that require an io.Reader.
Once we call the, NewGoTextFaceSource, we’ll get a reference to a *text.GoTextFaceSource, which can be used for creating many *text.GoTextFace.
There are a couple of ways that you can create strings, especially with formatting. You could use fmt.Sprintf
In our case here, we want to have a more nicely formatted string. Thinking of showing a score, we want to show separators for easier groking of data.
To do that we’re going to be using a message.Printer. This lets us set a printer that is language aware, and format our numbers and other strings accordingly.
If you’re accustomed to English like I am, you might not know, but other locations and other languages will format their numbers differently.
Choosing a printer will help us do this.
package main
import (
"bytes"
"image/color"
"log"
"math/rand/v2"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/examples/resources/fonts"
"github.com/hajimehoshi/ebiten/v2/text/v2"
"golang.org/x/text/language"
"golang.org/x/text/message"
)
// The font source we'll use
var mplusFaceSource *text.GoTextFaceSource
func main() {
// Loading the font face source with the data from the font
ff, err := text.NewGoTextFaceSource(bytes.NewReader(fonts.MPlus1pRegular_ttf))
if err != nil {
log.Fatal("error loading font", err)
}
mplusFaceSource = ff
ebiten.SetWindowSize(1280, 720)
ebiten.SetWindowTitle("Font Demo")
// Creating our game with a new printer, using English
if err := ebiten.RunGame(&Game{printer: message.NewPrinter(language.English)}); err != nil {
log.Fatal(err)
}
}
type Game struct {
score int64
printer *message.Printer
}
func (g *Game) Update() error {
// Randomly updating a score
num := rand.Int32N(26)
if num%5 == 0 {
g.score += 10
}
return nil
}
func (g *Game) Draw(screen *ebiten.Image) {
// Writing hello game to the screen
op := &text.DrawOptions{}
op.GeoM.Translate(25, 25)
op.ColorScale.ScaleWithColor(color.White)
text.Draw(screen, "Hello, Game!", &text.GoTextFace{Source: mplusFaceSource, Size: 24}, op)
// Writing a score to the screen
op = &text.DrawOptions{}
op.GeoM.Translate(25, 50)
op.ColorScale.ScaleWithColor(color.White)
score := g.printer.Sprintf("Score: %d", g.score)
text.Draw(screen, score, &text.GoTextFace{Source: mplusFaceSource, Size: 18}, op)
}
func (g *Game) Layout(ow, oh int) (int, int) {
return ow, oh
}
Like all things in the blog so far, this is a basic look at what can be done with adding text to your games. This should get you started on figuring out how you can use it for your specific game.