(X) Hide this
    • Login
    • Join
      • Say No Bots Generate New Image
        By clicking 'Register' you accept the terms of use .

XNA for Silverlight developers: Part 2 - Text rendering

(17 votes)
Peter Kuhn
>
Peter Kuhn
Joined Jan 05, 2011
Articles:   44
Comments:   29
More Articles
8 comments   /   posted on Jan 26, 2011
Categories:   Gaming , Windows Phone , General
Are you interested in creating games for Windows Phone 7 but don't have XNA experience?
Watch the recording of the recent intro webinar delivered by Peter Kuhn 'XNA for Windows Phone 7'.

This article is part 2 of the series "XNA for Silverlight developers".

This article is compatible with Windows Phone "Mango". 
Latest update: Aug 1, 2011.

Rendering text output in a game is not as fundamental as it is in other apps or business applications. However, it still is a very important part because you will need this everywhere: to render statistics like the current player score on the screen, to show messages or help text, and of course also for additional features and parts of your game that are not related to the game play itself, like menus or high score screens. Drawing text is another area with huge differences between XNA and Silverlight, which we will see and learn about in this part of the series. As always, you can download the full source code at the end of the article.

Silverlight

Text in Silverlight is so essential that you barely write it out directly. Instead you use the variety of controls that is available to do this on a higher level: text blocks and labels to show simple text, rich text controls for formatted text, and text boxes to offer the user the ability to edit text. And of course there's a whole set of additional controls that build upon these basic elements. The text rendering itself offers incredibly rich possibilities; for example, you can change fonts and colors on the fly. You can even use gradients and image brushes to create more interesting looks. And of course, if you want to get down to the lower levels of text rendering, there's also the Glyphs class that let's you manipulate advanced properties on character level. Except for the latter one, Silverlight's text controls also handle layout issues for you. Centering text or changing other aspects of the rendering is a simple matter of setting properties to different values. And so something simple as this:

 <TextBlock Text="Silverlight and Text"
         FontFamily="Arial Black" 
         FontStyle="Italic" 
         FontSize="29.333">
     <TextBlock.Foreground>
         <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
             <GradientStop Color="Black" Offset="0"/>
             <GradientStop Color="Red" Offset="1"/>
         </LinearGradientBrush>
     </TextBlock.Foreground>
 </TextBlock>

Turns out to become a rendered text like this:

image

    XNA

    Silverlight's text rendering features are not only very sophisticated and comfortable, they're also expensive. In a game, where computing power is a critical resource, you don't want to waste that on calculating gradients and sub-pixel text rendering details. And of course XNA doesn't have a concept of controls with layout containers or a visual tree where text elements could integrate. So – I bet you already expected that – text rendering is more painful and limited. The good news however is that the required steps have been simplified a lot compared to "pure" Direct3D environments, so even if it is not as comfortable, drawing text in XNA still is straight-forward and not hard to accomplish – as long as you plan ahead.

    Fonts basics

    In the last part we've learned about the SpriteBatch class which I said is used for both text and sprite rendering. This already hints at the fact that in XNA, rendering text is actually done using bitmaps. This immediately becomes apparent when you try to do something where bitmaps are bad at. Take the following picture of a short text that was manipulated using a 500% scale transform in Silverlight, for example:

    image

    And here is how the same thing looks in XNA:

    image

    Where I come from, people like to use the term "suboptimal" for things like this :). Seriously, this is something you simply don't do in XNA. If you need to use fonts at different sizes, you don't use the built-in scaling capabilities, but ideally create separate so-called sprite fonts for each size (or at least start with a bigger size and only scale it down). For example, text rendered with the same sprite font but created at five times the previous size looks pretty nice:

    image

    Creating sprite fonts

    Now let's see how these sprite fonts are actually created, what they are and how they are used in practice. Like any other asset, your fonts go through the content pipeline too, so the content project of your game is the right place to start at.

    image

    When you choose to add a new item to a content project you get a set of specific types for XNA, and "Sprite Font" is one of them. After it is created, Visual Studio opens it automatically for you. Surprisingly, it is just an XML file (I've removed the default comments for clarity):

     <?xml version="1.0" encoding="utf-8"?>
     <XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">
       <Asset Type="Graphics:FontDescription">
        <FontName>Segoe UI Mono</FontName>
         <Size>14</Size>
         <Spacing>0</Spacing>
         <UseKerning>true</UseKerning>
         <Style>Regular</Style>
         <!-- <DefaultCharacter>*</DefaultCharacter> -->
         <CharacterRegions>
           <CharacterRegion>
             <Start>&#32;</Start>
             <End>&#126;</End>
           </CharacterRegion>
         </CharacterRegions>
       </Asset>
     </XnaContent>

    This file only describes the details of the sprite font that the content pipeline and in particular the sprite font importer and processor will create for you. The result of this import process is a texture that contains images of the individual characters, and when you render your text later, the sprite batch class will internally choose the right parts of this texture to re-create the text on the screen. All the detail information about the individual options you can use are listed in the XML schema reference for sprite fonts on MSDN. I'll list the important parts here:

    • FontName: This is the friendly name of the font (as opposed to the file name) you can find in the fonts control panel item in your operating system.
    • Size: The size in points the font should be imported at. This is the setting you should tune until you can use the font in your application without using scaling. If you need the same font at different sizes, you should create multiple sprite fonts with this value set to the required values.
    • Style: Allows to use the common font styles like "Regular", "Bold", "Italic" or the combination "Bold Italic".
    • DefaultCharacter: The character to use if you try to render a character that is not contained in the generated sprite font. The meaning of this setting becomes clear after we've looked at the character regions settings.
    • CharacterRegions: see below.

    To understand the concept of character regions, you first need to understand two things: first of all, a font may contain a lot of characters. Especially Unicode fonts can easily contain several thousand characters. And second, each character takes up both storage space and memory when it is converted to and loaded as a texture, a lot more than in its original form. Imagine the waste and probably even problems you would run into if you would let XNA convert the whole font. To get an idea of what we are talking about here: Segoe UI, the font used in Windows Vista/7 and newer versions of Office, contains more than 2800 characters. Arial Unicode MS, a particular heavyweight distributed with earlier Office versions, apparently has almost 40,000 characters in it. For normal English text however, with both numbers and upper/lower case letters plus common special characters, you will barely need 100 of them.

    To fix this problem, a sprite font only includes those characters which you specify in one or more character region definitions. The "Start" and "End" values of a region depict the first and last Unicode character codes to include in the font. Either specify the characters themselves directly, or their decimal values using the "&#" prefix like the sample above demonstrates. The standard file created for a new sprite font includes the region &#32 to &#126. All numbers lower than 128 are identical in Unicode and ASCII, so this means all characters between " " (space) and "~" are included by default (MSDN has a ASCII table you can use to look that up):

    image

    If you need to include Unicode characters, make sure the font you want to use has those, and take a look at the code charts provided on Unicode.org to decide what regions you need. Please note that XNA's text rendering is somewhat limited in cases where characters are composed of multiple characters/glyphs, so you might run into problems with some languages.

    Important note on fonts: One important thing to note is that you cannot use any font that is installed on your computer in a game this way. Most fonts require special licenses to be distributed in such a form, and for some such a license might be very expensive or even not be available at all. XNA comes with a set of built-in fonts you can use without getting into trouble. Please check the "redist.txt" file that comes with Game Studio which lists a number of included fonts. For all other fonts you want to use, make sure you have an appropriate license that allows this. The path to the "redist.txt" file should be:

    %programfiles%\Microsoft Visual Studio 10.0\Common7\IDE\VPDExpress\1033\redist.txt

    Loading and using sprite fonts

    You load a sprite font like any other asset in your game by using a content manager. As we've learned in the last part, the game class already has a Content property (which actually is a content manager) we can use to do that. To try out the newly created font, change the LoadContent method of your game class in the following way. Also add a class field that holds the sprite font.

     SpriteFont sampleFont;
      
     ...
      
     protected override void LoadContent()
     {
         // Create a new SpriteBatch, which can be used to draw textures.
         spriteBatch = new SpriteBatch(GraphicsDevice);
      
         // TODO: use this.Content to load your game content here
         sampleFont = Content.Load<SpriteFont>("SampleFont");
     }

    Once loaded, it can easily be used in the draw method by utilizing the sprite batch. Remember that you have to call the Begin method first before you can start drawing with a sprite batch, and to call the End method when you're finished.

     protected override void Draw(GameTime gameTime)
     {
         GraphicsDevice.Clear(Color.Black);
      
         // TODO: Add your drawing code here
         spriteBatch.Begin();
      
         spriteBatch.DrawString(sampleFont, // the sprite font to use for the rendering
             "Rendering text\r\nin XNA",    // the text to render, can contain line breaks
             Vector2.Zero,                  // the position of the text (in relation to the origin)
             Color.Green,                   // the text color
             0.0f,                          // the rotation angle (radians) 
             Vector2.Zero,                  // the origin of the sprite
             1.0f,                          // the scale factor, try to keep at or close to 1
             SpriteEffects.None,            // allows flipping
             0.0f);                         // a depth value for depth sorting
       
         spriteBatch.End();
      
         base.Draw(gameTime);
     }

    There are also alternate overloads with less arguments if you don't want or need to specify some of the values. The result is your first rendered text in XNA:

    image

    Measuring text size

    One problem you will quickly run into is that you need to know the size some text will occupy on the screen. For example, you may want to render right-aligned or centered text, or your texts are dynamic with changing content (think player score, game time etc.) and you want to compensate for that. Luckily, the sprite font class has a method named "MeasureString" that exactly does that. In fact, it's the only method it implements which is not inherited from object. MeasureString takes a string or string builder as argument and returns the size of the rendered text. Since all parameters like font size and font style are static and have been set at compile time when the sprite font was created, they are not of interest or required here. With that, centering a text is a matter of computing the position dependent on the render size (another possibility would be to work with a combination of position and sprite origin of course):

     protected override void Draw(GameTime gameTime)
     {
         GraphicsDevice.Clear(Color.Black);
      
         // TODO: Add your drawing code here
      
         // calculate the text position
         string myText = "Some centered text!";
         Vector2 myTextSize = sampleFont.MeasureString(myText);
         Vector2 myTextPosition = new Vector2(graphics.GraphicsDevice.Viewport.Width / 2.0f - myTextSize.X / 2.0f, 
                                              graphics.GraphicsDevice.Viewport.Height / 2.0f - myTextSize.Y / 2.0f);
      
         // draw the text
         spriteBatch.Begin();
         spriteBatch.DrawString(sampleFont, // the sprite font to use for the rendering
             myText,                        // the text to render, can contain line breaks
             myTextPosition,                // the position of the text (in relation to the origin)
             Color.Green,                   // the text color
             0.0f,                          // the rotation angle (radians) 
             Vector2.Zero,                  // the origin of the sprite
             1.0f,                          // the scale factor, try to keep at or close to 1
             SpriteEffects.None,            // allows flipping
             0.0f);                         // a depth value for depth sorting 
         spriteBatch.End();
      
         base.Draw(gameTime);
     }

    image

    This is the basis for all text rendering you will do in XNA. As I said in the beginning, you use this not only for in-game text, but also for your menus, high score lists, about screens and instructions, simply put: whenever you need text.

    Advanced font rendering/bitmap fonts

    In the beginning, we have seen Silverlight text that uses gradients, and I've talked about other possibilities in Silverlight like using image brushes to create very unique text. In XNA, this is not supported when it comes to dynamically creating those effects. However, since characters are images in XNA, you can use all the possibilities you have with image manipulation and design to make your fonts more unique. However, this requires that you don't use the default import process of sprite fonts. Instead, you use a feature called bitmap fonts.

    Bitmap fonts are nothing but images of characters which you create yourself (as opposed to let them create automatically). Then, instead of using the sprite font importer and processor in your XNA content project, you import the image you've created by setting the importer to "Texture" (because it's really a texture you're importing) and the processor to "Sprite Font Texture". The result after the import process is the same however. You load a bitmap font as sprite font object and use it exactly like you would use a sprite font created the way we just did.

    image

    To make a bitmap font work, your image needs to look like this (this image was taken and enhanced to actually work from the MSDN explanation of this feature):

    BitmapFont

    There are several conventions and restriction your image has to follow:

    • The upper left character has to match the "First Character" property in the properties window above (in this case it's the space character which also is the default).
    • From there, the runtime expects the following characters left to right and top to bottom, which means if you start with space, the next character has to be an exclamation mark, the third one quotes etc. You cannot skip characters. If you need to provide different ranges, you need to write a custom font processor for the content pipeline that matches indexes to characters as described on the above linked MSDN page.
    • For monochrome fonts (where you add the tint in the draw method of the sprite batch like we did), you are supposed to work with grayscale images. Black means transparent and white full solid. If you want to use colors like the example above does for the digits, you need to provide transparency in the alpha channel of the image instead.
    • You need to fill the space between the characters with a magenta color (ff00ff) or you will receive compilation errors, because the processor needs that color to distinguish the individual characters. Don't worry about wasting space, the characters will be packed as tightly as possible during the import process, so you can set the spacing to a value that allows comfortable working.

    When you're using bitmap fonts, you can design each individual character as you want. You can for example apply effects and distortions your graphic program offers, or even draw the characters by hand. Obviously though, creating such an image manually is tedious work. Most people therefore use some sort of generator that at least creates the basic image from an existing font and work from there. Microsoft has published such a tool too, but unfortunately it doesn't work correctly with XNA 4.0 yet.

    A word on "Mango"

    With the first major update of the Windows Phone 7 platform (code name "Mango") it is possible to mix Silverlight and XNA content not only in the same application, but also on the same page. If you make use of this new possibility, you are able to have the very advanced and comfortable Silverlight text rendering features in your XNA game too. With little effort you can use normal Silverlight text blocks with all their styling possibilities and other advantages mentioned in this article and display them together with your XNA rendered content. Part 12 of this series shows how to do that, and even how to make use of Silverlight's animation features in combination with this.

    Summary

    In this article we've learned the differences between text rendering in Silverlight and XNA. We've seen that importing a font into XNA and use it for text output is not that hard if you follow some basic rules, but it's also clear that if nicer fonts and design effects are required, things can quickly become time-consuming.

    You can download the source code of the sample here. It also includes the bitmap font part, but please note that I've used the above sample image taken from the MSDN article. It doesn't have an alpha channel, so the colored digits will only display as gray scale or tinted in one color.

    Download source code

    As always, feel free to leave any comments and feedback, and of course also suggestions what can be improved or what you want to see in future episodes.

    About the Author

    Peter Kuhn aka "Mister Goodcat" has been working in the IT industry for more than ten years. After being a project lead and technical director for several years, developing database and device controlling applications in the field of medical software and bioinformatics as well as for knowledge management solutions, he founded his own business in 2010. Starting in 2001 he jumped onto the .NET wagon very early, and has also used Silverlight for business application development since version 2. Today he uses Silverlight, .NET and ASP.NET as his preferred development platforms and offers training and consulting around these technologies and general software design and development process questions.

    He created his first game at the age of 11 on the Commodore 64 of his father and has finished several hobby and also commercial game projects since then. His last commercial project was the development of Painkiller:Resurrection, a game published in late 2009, which he supervised and contributed to as technical director in his free time. You can find his tech blog here: http://www.pitorque.de/MisterGoodcat/


    Subscribe

    Comments

    • iiordanov

      RE: XNA for Silverlight developers: Part 2 - Text rendering


      posted by iiordanov on Jan 26, 2011 13:53

      Awesome article Peter!!!

      Thanks for sharing it with us.

    • -_-

      RE: XNA for Silverlight developers: Part 2 - Text rendering


      posted by Peter Kuhn on Jan 27, 2011 09:27

      Thank you :)

    • -_-

      RE: XNA for Silverlight developers: Part 2 - Text rendering


      posted by Gustav on Feb 15, 2011 13:26

      Really nice and useful article. Thank you!

      I have only a question, do you know if the BitmapFont is widely used by XNA developers or do they sticky with sprite fonts?

    • -_-

      RE: XNA for Silverlight developers: Part 2 - Text rendering


      posted by Peter Kuhn on Feb 15, 2011 14:33

      Thank you, Gustav. Unfortunately I have no statistics about the use of bitmap fonts in comparison to normal sprite fonts in games.

      Btw. I recently found a nice freeware tool to create bitmap fonts. I have only done a few tests, but it produces good results (except for some spacing issues when you use certain effects): http://www.nubik.com/SpriteFont/

    • -_-

      RE: XNA for Silverlight developers: Part 2 - Text rendering


      posted by avrashow on May 28, 2011 02:44
      Link to PART 1 - Fundamentals seems broken. From several of your pages.
    • MisterGoodcat

      RE: XNA for Silverlight developers: Part 2 - Text rendering


      posted by MisterGoodcat on May 28, 2011 03:14

      Thank you for bringing this to my attention. I have passed on the information to the Silverlight Show team and I'm sure the problem will be fixed soon. Please pardon the inconvenience.

    • BarboiMihail

      Re: XNA for Silverlight developers: Part 2 - Text rendering


      posted by BarboiMihail on Dec 19, 2011 14:01

      Say ... how are you ?

      I got a little stupid easy problem (it's seams this ones are the hardest) :

      I've got a "Windows Phone Silverlight AND XNA application" built with with vs2010 pro

      I'm trying to draw text ... and the only place I got sucked is when I'm trying to load content : it tells me "Unable to find file : contet\spritefont1" and BOOM ...

      I've tried different paths with no result ..

      The base class is :  public partial class GamePage : PhoneApplicationPage

      and because of that there's no LoadContent ... as for a public class Game1 : Microsoft.Xna.Framework.Game

       so what can I do to load content ?

       

      like 

       SharedGraphicsDeviceManager.Current.GraphicsDevice.SetSharingMode(true); 

      spriteBatch = new SpriteBatch(SharedGraphicsDeviceManager.Current.GraphicsDevice); 

      ContentManager cmm = (Application.Current as App).Content;

      SpriteFont font1;

      ....

      cmm.RootDirectory = "Content";
      font1 =  cmm.Load<SpriteFont>("SpriteFont1");

       

       

      My only problem is to loadcontent is this context : public partial class GamePage : PhoneApplicationPage

    • MisterGoodcat

      Re: XNA for Silverlight developers: Part 2 - Text rendering


      posted by MisterGoodcat on Dec 20, 2011 17:48

      Hi Mihail. I've just tried to do exactly what you're trying to do, using my sample from part 12 as a start (which demonstrates mixing Silverlight and XNA on Mango). I have no problem with this:

      content = (Application.Current as App).Content;
      font = content.Load<SpriteFont>("SpriteFont1");

      Make sure that your sprite font is correctly set up in the "Content" project, i.e. both the importer and processor are set to "Sprite Font Description", and that the build action is set to "Compile". Also make sure that you're using the correct asset name that is configured in the Content project.


    Add Comment

    Login to comment:
      *      *       

    From this series