game design, code, writing, art and music

English По-русски

Bitmap font generation in games

In all of my games I've been using bitmap fonts for all of the text. It's the only kind of text rendering method I've supported since the creation of the YUME game engine, because it's fast to render using the GPU and has little memory footprint.

Bitmap fonts are basically just prerendered characters on a texture, which are cut up and then arranged on the screen to display words. It's like cutting up a newspaper to create a letter.

The Hound of the Baskervilles (1981 film, directed by Igor Maslennikov)
The Hound of the Baskervilles (1981 film, directed by Igor Maslennikov)

The texture that stores all the glyphs in a bitmap font is called an atlas.

The atlas is usually accompanied by a text file, which describes the position and size of each glyph, as well as additional data for letter intervals, kerning and other details.

Bitmap font atlas used for debugging in Speebot, Phantom Path and Pilie Pals
Bitmap font atlas used for debugging in Speebot, Phantom Path and Pilie Pals

This technique has been used in computers for ages. It's relatively simple to implement and benefits from hardware acceleration, too. The GPU only needs to store one texture in memory, and you can generate all of your text fields by building the geometry and sending it to the GPU only when the text is changed.

This way you can render lots of dynamic text fields at once, and not have to worry about performance.

And since the atlas is just an image, you can add any effects to the text that you can think of: shadows, outlines, gradients — the GPU doesn't care, it won't affect the performance at all.

There are some big drawbacks to this approach, however.

  • Bitmap font doesn't scale well. You need to prepare a separate atlas for each font size.
  • You can only use glyphs that are in the atlas. If you suddenly need to add another character, you need to update each atlas that you have.
  • Some languages have tens of thousands of characters. You will probably need to prepare a separate atlas for each supported language, and save space in the texture by only including glyphs that are actually used in your game.

If, for example, your game is using 2 different font styles, 5 font sizes and supports 3 languages, that likely means that you're using 30 bitmap font atlases in total. If you suddenly need to add a new glyph (for example, the copyright sign ©), then you need to update all 30 atlases. The number grows with each font size and language that you need to support. That's a lot of work.

Alternatively, you can use vector fonts. These generally only require one source font file, which contains vector data for each glyph. You are then supposed to use some kind of a library that parses this data and draws the required text onto a texture.

Unfortunately, there are drawbacks to this approach as well:

  • Rendering vector fonts is expensive and slow. Each pixel of each character needs to be calculated based on the vector data and then drawn. This usually happens on the CPU, too.
  • A new texture must be created every time you update a text field, which must then be sent to the GPU to replace the previous texture. If you're updating a text field every frame, this will start causing performance and memory issues very quickly.

I want to keep using bitmap fonts for their excellent performance, but I also want the flexibility and simplicity of using a vector font file, because I don't want to create atlases manually. So, in an attempt to decrease my workload, I've combined the two approaches in my game engine.

The idea is to use vector fonts, but only for automatically generating bitmap font atlases.

The game loads a font file, parses it, uses a vector library to draw each required glyph onto a texture, stores the position and kerning data in memory — and the bitmap font is ready to use. This process is repeated for each font style, size and glyph set that's required by the game.

All of the internal text rendering code remains unchanged, so the performance remains great. But now, instead of having to prepare font atlases manually, I just update some data files when I need to support a new font style or glyph set.

For instance, there's a data file that describes font styles for the English language:

{
	"primary": {
		"glyphs": "QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm!1234567890+-,.?!:()'\"#& ",
		"font": "PTSans-Regular.ttf"
	},
	"secondary": {
		"glyphs": "QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm!1234567890+-,.?!:()'\"#& ",
		"font": "PTSans-Italic.ttf"
	}
}

A similar style description file is added for each supported language.

In the code I only need to provide the style name and the font size, and the engine generates a font atlas for that language. If this particular atlas has already been generated in the past, it's picked from the cache.

textField = BitmapTextField.fromStyle("primary", 24);
textField.setText("As you value your life or your reason keep away from the moor");

This new system will be used in my next game. The font style description files, font files and translation files will be easily accessible in the game's folder, which means that it will be possible to create localization mods for officially unsupported languages.

Next Article

Pilie Pals - Russian localization and language mods

Subscribe

Receive a notification on your device whenever there's a new blog post available, in order to:

Follow the development process of my next game.

Get notified of articles about the art and tech of designing games and engines.

Receive updates about changes to my games.

Subscription is free and takes just one click!