Adding Text
Over
the years, game text has been implemented in multiple ways—texture
blits to the screen, vector drawing, and even just the use of message
boxes. In modern development, most game libraries support the concept of
text rendering, removing the burden of having to implement this
yourself. DirectX does support a method of text rendering, but it still
requires a bit of work on your part for it to be useful. The following
sections describe how font systems are commonly put together.
Textured Fonts
When using text in your
game, it is best that all the text appears in a consistent manner. Using
a single font style will help in this regard. A font
is a series of letters and symbols written in a certain style. In both
2D and 3D games, text is commonly drawn using textured fonts.
Textured fonts, or
bitmapped fonts, are based on letters pre-drawn into a texture. The
letters are arranged in a grid with a single letter occupying each
section of the grid. When it comes time to write out words, the words
are built dynamically by drawing the textured letters on the screen.
This allows a single series of letters to be used to create any word or
phrase you require. Because of the simplistic nature in which the font
is used, text rendering can be quite fast. Also, because the fonts are
pre-drawn on a texture, the look and feel of the text can be changed by
altering the applied texture.
Textured fonts do have some downsides though:
They don’t scale
well. Because they are pieces of a texture, they can only grow or shrink
so much before they become ugly and unreadable.
The
texture must support every possible letter or symbol you may use. If
your game contains only a language such as English, this may not be a
problem, but translating your game to a language such as Japanese may
make your texture too large.
Because
there are different amounts of spacing preceding and following each
letter, textured fonts sometimes appear odd when writing certain words.
Textured fonts allow for the build-up of words as they’re needed and are managed through a font system.
Figure 1 shows how the letters of a font can be laid out in a texture.
A Font System Explained
Occasionally there
are instances when it makes sense to hardcode any text in the game into
preexisting graphics; a font system is used when the text that you need
to draw needs to be drawn dynamically. Imagine that you’re playing an
RPG and you need to talk to a resident in the village. In a hardcoded
text system, what the character says is pre-generated into a texture and
pulled up at the appropriate time. Depending on the amount of text,
that could potentially mean hundreds of textures needing to be loaded
just for standard conversations. A dynamic font system allows for the
building up of these strings by loading in only a single texture
containing all the letters in the font. This method saves load times and
a huge amount of otherwise wasted memory.
Font systems commonly
use sprites to do the actual text drawing. Each letter uses a single
sprite and is drawn in a sequence to spell out words or phrases.
Creating a Font System Using Sprites
The input string comes into the system and is broken up into
multiple sprites. These sprites are then positioned correctly and passed
to Direct3D to be drawn.
The following sections show how to implement a sprite-based font system.
The FontSprite Structure
The first step is the creation of a FontSprite structure. The FontSprite structure is needed to describe the properties of every letter sprite.
A simple font system requires a simple FontSprite structure. The FontSprite structure is very similar to the GameSprite structure used in the last chapter. The most important difference is the inclusion of a new variable called letterIndex. Each letter in the font texture has an index associated with it, with the first letter ‘A’ being 0, ‘B’ is 1, and so on.
The FontSprite structure is shown here:
// FontSprite structure
typedef struct
{
// sprite details
float width;
float height;
// sprite position
float posX;
float posY;
int letterIndex;
BOOL visible;
}FontSprite;
Translating a String to Sprites
A new function called UpdateText needs to be created to translate the input string into FontSprite structures. For each letter in the string, a FontSprite structure needs to be filled in. Since the string coming through is dynamic, the UpdateText function needs to be able to pick up on the text it contains and generate valid FontSprites. Because of the number of sprites being created, the font system will use sprites from a sprite pool.
The following code sample shows the UpdateText function.
// The string that will be output in sprites
char *message = "HELLO WORLD";
/*******************************************************************
* UpdateText
* Updates the sprites being used for text rendering
* Inputs - void
* Outputs - void
*******************************************************************/
void UpdateText()
{
int curLetterCount = 0;
// loop through the letters in the message string
for (unsigned int i = 0; i < strlen(message); i++)
{
// set the position of the letter
sprites[curLetterCount].posX = (curLetterCount * letterWidth);
// save off the letter index
sprites[curLetterCount].letterIndex = (message[i] - 'A');
// go to the next letter
curLetterCount++;
}
numberOfLetters = curLetterCount;
}
The UpdateText
function loops through the input string looking at each letter. The
function then determines the letter’s index by subtracting the ASCII
value from ‘A’. This index is then stored in the letterIndex variable for passage to the function that updates the sprites in the sprite pool. The posX
variable is also set based on the current letter in the string. Because
this is a simplified example, only capital letters are supported in the
input string.
The function sets a variable called numberOfLetters, which is used during the Render function to tell Direct3D how many sprites are to be drawn in the batch.
Updating the Sprite Pool
After the FontSprite
structures are filled out, the system will convert these over to sprite
objects within the sprite pool. If you remember from the last chapter, a
sprite pool is a collection of general D3DX10_SPRITE structures that can be dynamically repurposed each frame.
Since the sprites in the
sprite pool can be used for any letter in the input string, the texture
coordinate needs to be constantly changed to reflect the current letter.
The texture coordinate for each letter is calculated with the help of
the letterIndex variable set during the UpdateText function. The letterIndex is used to reference the letter’s position in the font texture.
spritePool[curPoolIndex].TexCoord.x = (float)((sprites[i].letterIndex *
letterWidth) / fontTextureWidth);
The only change to the Render function is the use of the numberOfLetters variable set within the UpdateText function. This keeps extra sprites in the sprite pool from being drawn when they’re not in use. Figure 2 shows the output of the simple font system.
This system is set up to draw
a single string and is not meant as a reusable general purpose
component. The font system also doesn’t support alpha blending, causing
the letters to appear within rectangles.