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
SpriteFonts
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).
The SpriteFont
can then be loaded with the ContentManager
:
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)
As with SpriteBatch.Draw()
, we’ll explore what the various parameters are used for, and you can select the one that matches your needs.
SpriteFont spriteFont
The 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 ArgumentNullException
.
string text
The text
parameter is the string you want to draw onto the screen.
StringBuilder text
Optionally, a StringBuilder
object can be supplied as the text
parameter.
Vector2 position
This position specifies the upper-left hand corner of where the text will be drawn on-screen (unless the origin
parameter is set).
Color color
The 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)
float rotation
The 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
.
Vector2 origin
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).
float scale
The 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.
Vector2 scale
The scale
can also be specified as a Vector2
, which allows the horizontal and vertical scaling factors to be different.
SpriteEffects effects
The effects
parameter is one of the SpriteEffects
enum’s values. These are:
SpriteEffects.None
- the text is drawn normallySpriteEffects.FlipHorizontally
- the text is drawn with the texture flipped in the horizontal directionSpriteEffects.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
single layerDepth
The 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.
However, the SpriteFont
class does expose a method SpriteFont.Measure(string text)
and override SpriteFont.Measure(StringBuilder text)
which given a string
or StringBuilder
return a Vector2
indicating the size at which that text would be rendered.