PVG
23. Player Health Ui — Program Video Games

23. Player Health Ui

[[programvideogames]]In this lesson we'll be implementing the player health UI.

There's not much to the code, but a bit to think about as we are using half-hearts.

All code today is in main.odin. Let's get into it.

Setting Up

First, we'll use the core:math package below, so import that.

import "core:math"

Next up we're going to create a new 2D camera that stays at the same location - this way we can scale up our pixel-art UI to the same zoom level as the game.

As an aside, I think pixel size mixing looks really bad. That's when some pixel art assets in the same game have "bigger pixels" than others

Game_State :: struct {
    camera:                 rl.Camera2D,
    ui_camera:              rl.Camera2D,
    // ...
}

Initialise the camera zoom level the same way.

gs.camera = rl.Camera2D {
    zoom = ZOOM,
}
gs.ui_camera = rl.Camera2D {
    zoom = ZOOM,
}

Here's a zoomed in version of the heart asset we'll be using.

Don't save this one, as I've updated the tileset.png to include the hearts.

![[Pasted image 20241019172837.png]]

Drawing the Heart Sprites

// Where rl.DrawFPS used to be

rl.BeginMode2D(gs.ui_camera)

// Player health
{
    full_hearts := player.health / 2
    has_half_heart := f32(player.health) / 2 > f32(full_hearts)
    // Integer division truncates towards zero.
    // 5 / 2 = 2
    // -5 / 2 = -2
    empty_hearts := math.ceil(f32(player.max_health - player.health) / 2)

    x := f32(16)

    for _ in 0 ..< full_hearts {
        rl.DrawTextureRec(gs.tileset_texture, {336, 176, 16, 16}, {x, 16}, rl.WHITE)
        x += 16
    }

    if has_half_heart {
        rl.DrawTextureRec(gs.tileset_texture, {336, 144, 16, 16}, {x, 16}, rl.WHITE)
        x += 16
    }

    for _ in 0 ..< empty_hearts {
        rl.DrawTextureRec(gs.tileset_texture, {336, 112, 16, 16}, {x, 16}, rl.WHITE)
        x += 16
    }
}

rl.EndMode2D()

First, we calculate how many full hearts we'll be displaying. One heart is 2 health.

Since integer division truncates towards zero, if we have 5 health right now, 5 / 2 will result in 2, not 2.5 or 3.

That's perfect for the full_hearts.

Next, we use the fact that integer division truncates to see if we should have a half heart.

By converting the value to an f32, we can use decimal division. 5.0 / 2.0 = 2.5 and 2.5 > 2.0.

Finally, we calculate how many empty_hearts.

Then we simply iterate over the full, draw the half if required, and iterate over the empty, while increasing the x position.