Text in videogames is challenging. In the early days of computing, video cards had a limited number of modes - the most common was for displaying text that was streamed to the video card as ASCII character data. This is also how the command prompt and terminals work - they operate on streams of character data.
But games used different modes that used the limited memory of the card to supply pixel data in a variety of formats. Notably, text support was non-existent in these modes.
Fast-forward to the modern day. Now text is typically handled by the operating system and its windowing libraries. Which are notably unusable within a DirectX rendering context. So we still use the same techniques used in those earliest video games to render text. Bitmap fonts.
A bitmap font is one in which each character is represented by a raster graphic - a bitmap. Much like sprites, these are copied into the bitmap that is the scene. Thus, we have to bit blit each character one at a time. This is in contrast to the fonts used by modern operating systems, which are vector based. A vector font contains the instructions for drawing the font characters, so it can be drawn at any scale.
MonoGame provides some support for drawing text through the
SpriteBatch. But to use this functionality, we first need to create a
SpriteFont object is similar to the sprite
BatSprite class we worked on in the last section. It wraps around a texture containing rendered font characters, and provides details on how to render each character. However, we don’t construct
SpriteFont objects ourselves, rather we load them through the
ContentManager and the content pipeline.
The content pipeline creates a sprite font from an existing font installed on your computer. Essentially, it renders each needed character from the font into a texture atlas, and combines that with information about where each character is in that atlas. To create your sprite font, choose the create new item from the MGCB Editor, and then select “SpriteFont Description (.spritefont)”:
This will create a SpriteFont Description, which will be compiled into a
SpriteFont. It also adds this description into your Content folder. Open it, and you will see it is nothing more than an XML file, which specifies some details of the font:
<?xml version="1.0" encoding="utf-8"?> <!-- This file contains an xml description of a font, and will be read by the XNA Framework Content Pipeline. Follow the comments to customize the appearance of the font in your game, and to change the characters which are available to draw with. --> <XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics"> <Asset Type="Graphics:FontDescription"> <!-- Modify this string to change the font that will be imported. --> <FontName>Arial</FontName> <!-- Size is a float value, measured in points. Modify this value to change the size of the font. --> <Size>12</Size> <!-- Spacing is a float value, measured in pixels. Modify this value to change the amount of spacing in between characters. --> <Spacing>0</Spacing> <!-- UseKerning controls the layout of the font. If this value is true, kerning information will be used when placing characters. --> <UseKerning>true</UseKerning> <!-- Style controls the style of the font. Valid entries are "Regular", "Bold", "Italic", and "Bold, Italic", and are case sensitive. --> <Style>Regular</Style> <!-- If you uncomment this line, the default character will be substituted if you draw or measure text that contains characters which were not included in the font. --> <!-- <DefaultCharacter>*</DefaultCharacter> --> <!-- CharacterRegions control what letters are available in the font. Every character from Start to End will be built and made available for drawing. The default range is from 32, (ASCII space), to 126, ('~'), covering the basic Latin character set. The characters are ordered according to the Unicode standard. See the documentation for more information. --> <CharacterRegions> <CharacterRegion> <Start> </Start> <End>~</End> </CharacterRegion> </CharacterRegions> </Asset> </XnaContent>
You can edit the values of the various elements to control the font, as well as attributes like size and style that will be used to create the raster representation. Any font installed on your development machine can be used (though for uncommon fonts, it is a good idea to include the font’s file, usually a .ttf, in your repository so you can install it on other development machines).
SpriteFont can then be loaded with the
SpriteFont spriteFont = Content.Load<SpriteFont>("name-of-spritefont");
Where the supplied string is the same name as the .spritefont file, without the extension.
Once loaded, text can be drawn to the screen with
SpriteBatch.DrawString(SpriteFont spriteFont, Vector2 position, Color color). There are several overrides to choose from:
DrawString(SpriteFont spriteFont, string text, Vector2 position, Color color)
DrawString(SpriteFont spriteFont, string text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth)
DrawString(SpriteFont spriteFont, string text, Vector2 position, Color color, float rotation, Vector2 origin, float scale, SpriteEffects effects, float layerDepth)
DrawString(SpriteFont spriteFont, StringBuilder text, Vector2 position, Color color)
DrawString(SpriteFont spriteFont, StringBuilder text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth)
DrawString(SpriteFont spriteFont, StringBuilder text, Vector2 position, Color color, float rotation, Vector2 origin, float scale, SpriteEffects effects, float layerDepth)
SpriteBatch.Draw(), we’ll explore what the various parameters are used for, and you can select the one that matches your needs.
spriteFont parameter is a
SpriteFont object describing the font you want to write with. If the
SpriteFont has not been loaded (is null), then invoking
DrawText() will throw an
text parameter is the string you want to draw onto the screen.
StringBuilder object can be supplied as the
This position specifies the upper-left hand corner of where the text will be drawn on-screen (unless the
origin parameter is set).
color parameter is a
Color the text will be rendered in (this is actually blended as is with sprites, but the base color is white, so whatever color you choose is the color that will be displayed)
rotation is a rotation value measured in radians that should be applied to the text. This rotation is about the
origin of the sprite, which is why all the overrides that specify the
rotation also specify the
origin is the spot within the text where rotations and scaling are centered. This also affects text placement - the
position vector indicates where the
origin of the text will fall on-screen. It is a vector measured relative to the upper-left-hand-corner of the text, in texture coordinates (i.e. pixels of the source texture).
scale is a scalar value to scale the text by. For example, a value of
$ 2.0f $ will make the text twice as big, while
$ 0.5f $ would make it half as big. This scaling is in relation to the
origin, so if the origin is at the center of the text grows in all directions equally. If instead it is at
$ (0,0) $, the text will grow to the right and down only.
scale can also be specified as a
Vector2, which allows the horizontal and vertical scaling factors to be different.
effects parameter is one of the
SpriteEffects enum’s values. These are:
SpriteEffects.None- the text is drawn normally
SpriteEffects.FlipHorizontally- the text is drawn with the texture flipped in the horizontal direction
SpriteEffects.FlipVertically- the text is drawn with the texture flipped in the vertical direction
Note you can specify both horizontal and vertical flipping with a bitwise or:
SpriteEffects.FlipHorizontally | SpriteEffects.FlipVertically
layerDepth is an integer that indicates which sprites should be drawn “above” or “below” others (i.e. which ones should obscure the others). Think of it as assembling a collage. Sprites with a higher
layerDepth value are closer to the top, and if they share screen space with sprites with a
lowerDepth, those sprites are obscured.
Measuring with SpriteFont
Note that with
SpriteFont, there is no way to specify the width the text should be drawn - it is entirely dependent on the font, the string to render, and any scaling factors applied. Nor is there any automatic word wrapping.
SpriteFont class does expose a method
SpriteFont.Measure(string text) and override
SpriteFont.Measure(StringBuilder text) which given a
StringBuilder return a
Vector2 indicating the size at which that text would be rendered.