§ root / a / polish-02

Polish 02 - Bitmap Numbers

RPG Player Types.

This is the second article in a series that takes the final RPG code from the “How to Make an RPG” project and adds a little more polish to one element.

The Cave Quest demo in the book uses a lot of true type text. True type text works well for modern games but in a relatively low-resolution JRPG, bitmap text is better. Bitmap text can be aligned perfectly to the pixel grid so the text is sharp. If the true-type text happens to render in a way that’s not pixel-aligned then it’s very easy for it to appear blurry.

Blurry Number Font.

Here you can see a blown-up in game number; horribly blurry!

Why? Because the true type font is a vector image that’s been scaled down to the the pixel grid and it doesn’t nicely align to the pixels. The width of the font, instead of being 8 pixels might something like 6.7984582, it’s never going to fit nicely without getting a bit blurred.

Each piece of polish should take less than a day; that’s why I’m only changing the numbers to bitmap fonts.

Bitmap Font Engine

Dinodeck, the underlying C++ engine, handles fonts and drawing but it doesn’t support bitmap fonts natively. Therefore we’ll write the bitmap font code in Lua! In fact there’s already a project over here that can draw monospace, bitmapped fonts - which is fine for our number needs.

Current Number Fonts

Numbers are used all over the place.

  • In-game Menu
    • Time
    • Gold
    • Party Summary Panels
  • Inventory Menu
  • Status Screen
    • Stats
    • Party Member Summary Panel
    • Level Summary Panel
  • Shop
    • Shop Prices
    • Party Gold
    • Equip and inventory counts
  • Combat
    • HP/MP Displays
    • Jumping Number Effects (heal, damage etc)
  • Combat Summary Screen
    • XP
    • Level
  • Loot Screen
    • Item Counts
    • Gold Found
    • Party Gold

All these places need updating in the code. Here’s how the screens look at the moment:

In-Game Menu.

Inventory.

Status Screen.

Note: The player actions text is at the wrong scale. I’ll fix that too while I’m in the code.

Shop Screen.

Combat.

Here’s a screen I’m not going to change:

Want to stay at the inn?

In this screenshot the number text is inline with dialog text so it’s tricky to change.

Sourcing a Replacement Font

Here’s the texture for the replacement font. You’re free to use this as I made it! The one shown below has been blown up 2x (download the original here).

My bitmap font

Code Changes

For this article I’ll only go through a single example where I update the existing code to use a bitmap font.

Add the Bitmap Font Rendering Code

Each font is composed of two pieces; the texture and a lua definition describing the position of the glyphs in the texture. For the numbers I’ve added two files in the following locations:

/art/font/number_font.png
/code/font/NumberFontDef.lua

Then there’s the font rendering code that I’ve added in the main code directory.

/code/BitmapText.lua

The interface for using a bitmap font is the same as Dinodeck’s code for using true type fonts.

In main.lua file I create a global gNumberFont to draw the number text in the game. When I come to add more fonts, I’ll move the number font out of global scope to somewhere more sensible.

LoadLibrary('Asset')
Asset.Run('Dependencies.lua')
gRenderer = Renderer.Create()
gNumberFont = BitmapText:Create(NumberFontDef)

That’s it for setup.

Using a Bitmap Font for the Gold Count

Now to demonstrate the process: I’ll update the number for the Gold count on the in-game menu. The gold value is rendered out in a state called FrontMenuState. Here’s the relevant function:

function FrontMenuState:Render(renderer)

    -- Lots of code omitted
    local goldX = self.mLayout:MidX("gold") - 22
    local goldY = self.mLayout:MidY("gold") + 22

    renderer:ScaleText(self.mParent.mLabelSize, self.mParent.mLabelSize)
    renderer:AlignText("right", "top")
    renderer:DrawText2d(goldX, goldY, "GP:")
    renderer:DrawText2d(goldX, goldY - 25, "TIME:")
    renderer:AlignText("left", "top")
    renderer:DrawText2d(goldX + 10, goldY, gWorld:GoldAsString())

In this snippet note that the the gold value comes from gWorld:GoldAsString() and it’s being rendered using renderer:DrawText2d, also note that the alignment is set to top and left. Here’s the modified function:

function FrontMenuState:Render(renderer)

    -- Lots of code omitted

    local goldX = self.mLayout:MidX("gold") - 22
    local goldY = self.mLayout:MidY("gold") + 22

    renderer:ScaleText(self.mParent.mLabelSize, self.mParent.mLabelSize)
    renderer:AlignText("right", "top")
    renderer:DrawText2d(goldX, goldY, "GP:")
    renderer:DrawText2d(goldX, goldY - 25, "TIME:")
    renderer:AlignText("left", "top")
    -- New Lines:
    gNumberFont:AlignText("left", "top")
    gNumberFont:DrawText2d(renderer, math.floor(goldX + 10), math.floor(goldY), gWorld:GoldAsString())

Here you can see the difference:

Crisp text

The box on the top has the gold rendered very clearly and crisply while the one below is blurred. That’s the power of using bitmap fonts when working at low resolutions.

The Final Look

Here’s how some of the game screens look with the number changes. This was mostly swapping out the render calls to use the BitmapFont code and a little realignment in places.

In-Game Menu with crisper fonts.

Status Screen using crisp bitmap number fonts.

Obviously a further improvement would be to replace all remaining text with bitmap fonts and that’s definitely on the polish list.

Source

There’s a github with an updated project here.

Related Articles