Don’t you just hate dying in your game in one hit? I know I do. I like playing Mario just like the next guy, but having an actual health bar can go a long way depending on what kind of game you’re trying to create. Could you imagine playing Legend of Zelda games where you die in a single hit? That would probably be the most frustrating game ever unless you’re a “pro”. So, let’s learn how to remedy that situation by creating a health system in our game.

The Health System

The health system is a classic used in games like Zelda, Grand Theft Auto, Kingdom Hearts, and many others. It’s an alternative to the life system in some cases, which allows us as game developers to give the player more opportunities to make mistakes and different types of experiences. With that said, every system can be distilled into basic building blocks; for the health system that is numbers.

Health is just hit points; hit points are essentially a number used to represent how much damage a player or enemy can take until they’re considered dead. If you look at the graphic above, the health bar and hearts fluctuate based on the hit points the player has as they all use the same underlying health in different ways. Let’s look at the code piece by piece.

Code Example

// Kino Rose
// NierPixe.com
// Zelda Heart, Health Bar Tutorial
//title: Heart Containers
init = (state) => {
state.player = {
health: 12,
healthMaximum: 12,
spr: 5
}
state.frameCount = 0
}
update = (state, input, elapsed) => {
state.frameCount += 1
//Show dynamic incrementation of player Health
// Note with the Zelda Implementation, due to the way the sine wave works, you will never see full 3 hearts.
state.player.health = clamp((state.player.healthMaximum * Math.abs(Math.sin(state.frameCount / 250))), 0, state.player.healthMaximum)
}
draw = (state) => {
clear()
drawLogoInformation()
drawPlayerHealthMinMax(state.player)
drawPlayerHealthBar(state.player)
drawPlayerHealthZeldaStyle(state.player)
}
function drawPlayerHealthZeldaStyle(player) {
const playerfullHearts = Math.floor(player.health / 4)
const playerRemainderHeart = (player.health % 4 ) /4
let xOffset = 0
const lineY = 72
//Draws all the full hearts, simply because they are all similar to each other
range(0, playerfullHearts).forEach( heart => {
xOffset = heart * 8
sprite(0 + xOffset , lineY, 0)
})
//drawRemainderHeart
//Update once more to place the heart next to the other hearts
xOffset += 8
/*
* Given that the last heart is the only that should be different from the others
* We evaluate it as a separate case from the others.
* Therefore, we only need to check this one case only.
* In the case of Zelda, each heart is broken up into pieces so we evaluate the quarters.
* This allows us to create the final heart based on the remainder rather than whole numbers.
*/
if(playerRemainderHeart >= 0.50) {
sprite(0 + xOffset, lineY, 1)
} else if(playerRemainderHeart >= 0.25) {
sprite(0 + xOffset, lineY, 2)
} else if(playerRemainderHeart > 0) {
sprite(0 + xOffset, lineY, 3)
}
}
//Standard Style of Drawing Health as numbers
function drawPlayerHealthMinMax(player) {
print(0, 36, `Player Health: ${Math.round(player.health)}/${player.healthMaximum}`)
}
//Draw Player Health as HealthBar
/*
* A bar is just a rectangle with a width. A health bar is usually represented as some number out
* of the maximum. Therefore, we can use the percentage of the player health to adjust the width
* of the bar to represent health. We create a bar below to make it more apparent that there is a
* cap on the player health and what the maximum is.
*/
function drawPlayerHealthBar(player) {
const maxHealthBarWidth = 40
const maxHealthBarHeight = 12
const cappedHealthBar = clamp((maxHealthBarWidth * (player.health / player.healthMaximum)) 4, 0, maxHealthBarWidth)
rectFill(0, 48, maxHealthBarWidth, maxHealthBarHeight, 5)
rectFill(2, 50, cappedHealthBar, maxHealthBarHeight 4, 3)
}
function drawLogoInformation() {
const title = "NierPixel.com"
const creator = "Kino Rose"
const tutorialName = "Zelda Heart + Health Bar Tutorial"
const lineHeight = 8
print(0, 0, title, 0)
print(0, lineHeight, creator, 0)
print(0, lineHeight * 2, tutorialName, 0)
}

Let’s start with line 8; this init function creates the important properties that we’re evaluating in the tutorial (health and healthMaximum). These properties are the basic data structures used to represent all the forms of health representations.

Next, if you look at line 26, we start the process of drawing each representation within the draw function using the player as a parameter. In drawPlayerHealthMinMax on line 69, we simply print the player’s current health / maximum health. This is just a text representation that’s all. With that said, let’s get a little more complicated and look at line 81 where we start drawing player health in the form of a health bar.

Within the drawPlayerHealthBar function, we define how long the health bar should be(maxHealthBarWidth) and where it should be placed on the screen. The most important aspect here is line 84; here we use the same idea from drawPlayerHealthMinMax to get a percentage that can be used to adjust the width of the health bar to create the effect of player health being at different percentages. Not so hard is it? We’re just thinking about different ways to represent the same thing. Now, we have one more to go through my friend, the sweet Zelda Style hearts.

Zelda Style hearts are a more robust representation than the other two and require a bit more thought. If you haven’t played Zelda before, first, you definitely should and two, the hearts are broken into quarters. That means effectively a heart is worth 4 hit points. You might start to think, “How am I going to represent this one? This sounds complicated.” Well, I have a solution for you before you get extremely frustrated. You only need to represent one of the hearts differently at any point in time. Look at line 3536; we create that differentiation by having full hearts and one remainder heart. Because we decided that heart is worth 4 hit points, we divide by 4. Because a heart is a complex shape, we have to create individual sprites for each one state (1/4, 2/4, 3/4, and full heart). After designing the sprites, we loop through each full heart and then add one more heart with a different sprite based on if it’s a 1/4, 2/4, or 3/4 in terms of the remainder. With that, you can now create Zelda Style hearts in your game.

When you get down to the barebones, all health representations in every game function the same way. In fact, a lot of the complex things we see in games are just numbers and formulas put together in such a way to create those beautiful pieces of work we call video games.

If you would like to try it out online, you can try it at this link and look at the code, in the code tab: Online Example

Awesome, now you have the tools to create your own styles of health or Zelda-like health bar. Stay tuned for more content coming soon! If you enjoy the content, subscribe and get updates on new blog posts that come out on Godot, Script-8, or Business concepts for Indie Game Developers.

Peace and have a good one!

%d bloggers like this: