Building Heads Up Display for Your Game How to Add a Health Bar to Ebitengine Games

In the previous post we discussed some basics of making a Heads Up Display (HUD for short). In this post we’re going to cover the basics of presenting a health bar in part of your HUD. Again, by way of reminder, we’re using the go programming language and the ebitengine game engine. We’re going to be using the vector package from the ebitengine library to draw out primatives. As well as some basic math that you’ve probably forgotten if you’ve learned it, or don’t know it if you haven’t.


Jul. 23, 2024 741 words ebitengine ·

In the previous post we discussed some basics of making a Heads Up Display (HUD for short). In this post we’re going to cover the basics of presenting a health bar in part of your HUD.

Again, by way of reminder, we’re using the go programming language and the ebitengine game engine. We’re going to be using the vector package from the ebitengine library to draw out primatives. As well as some basic math that you’ve probably forgotten if you’ve learned it, or don’t know it if you haven’t.

How we’ll use this

Before we can really get into drawing a health bar in our HUD, we’re first going to have to set the stage for where this makes sense. In our submarine game, there is a single player ship, this ship has a couple of stats on it. Of interest for this discussion, it has a max health and a current health. We’ll be using these two things for this post. Also, if we want to see any changes to our health bar, we’ll also need some way to simulate a hit which removes health.

The foundation

Before we look at some code, lets discuss some of the mechanics that our health bar will use for drawing. If you look at the main image above, you’ll see a health bar in the top left of the screen. It is drawn with two rectangles, a green one showing current health, and a red one showing the max health/damage.

If you think about this mathematically, the red bar behind is the whole, the green bar in front is the the part, this sounds an awful lot like a ratio. Now that we know this, lets think about it with regards to drawing on an ebitengine image.

We’ll start by picking an arbitrary width for our max health, this really is up to you, it is just whatever fits well into your game HUD. I picked 300 pixels in the example that I’ll add below. Then we need to pick a location for the top left corner of our max health bar; say 50x50. Then, we can draw our current health at the same location. We can then determine the length of our current health as a ratio of our max health. This can be computed as a ratio (part/whole):


healthRatio := currentHealth/maxHealth
currentHealthWidth := 300 * healthRatio

That really is the hard part of drawing our health bar. The rest of it, is just the basics of building an ebitengine based game.

The Code

Let’s take a look at some example code that lays out our health bar as discussed above.

  1. We define a game struct
  2. We define a ship struct
  3. The Game Update method simulates being hit and losing health
  4. The health bar is drawn in the Game Draw method
package main

import (
	"math/rand"

	"github.com/hajimehoshi/ebiten/v2"
	"github.com/hajimehoshi/ebiten/v2/vector"
	"golang.org/x/image/colornames"
)

const (
	maxHealthWidth = 300
)

func main() {
	ebiten.SetWindowSize(720, 440)
	ebiten.SetWindowTitle("Health - HUD")

	game := NewGame()

	ebiten.RunGame(game)
}

type Ship struct {
	MaxHealth     float64
	CurrentHealth float64
}

func NewShip() *Ship {
	return &Ship{
		MaxHealth:     12,
		CurrentHealth: 12,
	}
}

type Game struct {
	ship *Ship
}

func NewGame() *Game {
	return &Game{
		ship: NewShip(),
	}
}

func (g *Game) Update() error {

	// Simulating a hit. We get a random number, the % is a modulus operator, if the hit is divisible by 5 we'll decrement some health
	hit := rand.Intn(100)
	if hit%5 == 0 {
		g.ship.CurrentHealth -= 0.23
	}

	// Once the health goes below zero, we'll just randomly pick a percentage of the max health to fill back up to
	if g.ship.CurrentHealth <= 0 {
		g.ship.CurrentHealth = g.ship.MaxHealth * rand.Float64()
	}

	return nil
}

func (g *Game) Draw(screen *ebiten.Image) {
	// Adding a new color to the background screen image
	screen.Fill(colornames.Aliceblue)

	// Assigning a max size for our health bar, and drawing it using vector.DrawFilledRect
	vector.DrawFilledRect(screen, 50, 50, float32(maxHealthWidth), 20, colornames.Red, true)

	// Using our current health as a ratio of max health to get the width for our current health bar
	healthRatio := g.ship.CurrentHealth / g.ship.MaxHealth
	currentHealthWidth := maxHealthWidth * healthRatio
	vector.DrawFilledRect(screen, 50, 50, float32(currentHealthWidth), 20, colornames.Green, true)
}

func (g *Game) Layout(ow, oh int) (w, h int) {
	return 720, 440
}

Wrapping Up

As you can see in this simple example, it only takes a little bit of math and some basic knowledge of the vector package in the ebitengine project you can easily get started