bmfont_rendering.png

The next step in my CraftworkGUI project is to implement text rendering. I’ve chosen to manually generate a bitmap font texture using the Bitmap Font Generator tool by AngelCode.com and write my own simple text renderer for MonoGame. The process is fairly easy and most of the code required already exists if you use the C# XML serializer for font loading by DeadlyDan linked from the AngelCode.com website.

Step 1: Generate the texture and font file using BMFont

If you haven’t already done so, download and install the BMFont tool. When you start the tool you’ll be presented with a screen that looks like this:

bmfont.jpg

Setup your Font options, they can be found in the Font Settings dialog from the options menu. You can choose whatever you settings you prefer here. Next, you need to select the characters you want to generate. Typically this will include all of the top half of the character set as shown in the screenshot.

Change the output format to XML in the Export Options dialog. This is important because we are going to be using the XML serializer to load our font. While your in the Export Options dialog you might also want you change your texture format, I prefer png. To keep things simple, I also like to make sure my font is going to fit on one texture. You might need to play around with the texture width and height a little to get something you’re comfortable with.

Output your font file and texture using the Save bitmap font as option.. You should end up with a .fnt file containing some XML and an image with the same name followed by _0. The font generator tightly packs the glyphs into the texture atlas to minimize the texture size.

Step 2: Add the BmFont XML Serializer code to your project

You can find the BmFont Serializer code on pastebin. It makes me feel a little uncomfortable that the code doesn’t live in a proper code repository, but I’ll keep a copy of it on my server in case it ever disappears.

The code consists of a handful of classes used for serialization including:

  • FontChar
  • FontCommon
  • FontFile
  • FontInfo
  • FontKerning
  • FontLoader
  • FontPage

For the most part, you won’t need to change the code at all. If you’re like most programmers you’ll probably fiddle with it a little and change the namespaces to your liking. I used the refactoring tools in MonoDevelop to split each class into it’s own file.

At the most basic level, loading a font file is easy. If you’ve put your font file and texture in the Content folder you can use the following code on Windows.

var fontFilePath = Path.Combine(Content.RootDirectory, "CourierNew32.fnt");
var fontFile = FontLoader.Load(fontFilePath);
var fontTexture = Content.Load<Texture2D>("CourierNew32_0.png");

But this won’t work when your using MonoGame to deploy to other platforms like Android or iOS. The solution is to modify or create a new FontLoader.Load method that takes a Stream instead of a file path like this:

public static FontFile Load(Stream stream)
{
        XmlSerializer deserializer = new XmlSerializer(typeof(FontFile));
        FontFile file = (FontFile) deserializer.Deserialize(stream);
        return file;
}

Now the code to load the font can use a TitleContainer.OpenStream method to load the file in a more generic way. MonoGame supports loading files on any platform using the same path through the content manager or the TitleContainer.

var fontFilePath = Path.Combine(Content.RootDirectory, "CourierNew32.fnt");
using(var stream = TitleContainer.OpenStream(fontFilePath))
{
    var fontFile = FontLoader.Load(stream);
    var fontTexture = Content.Load<Texture2D>("CourierNew32_0.png");
    // textRenderer initialization will go here
    stream.Close();
}

Step 4: Implementing a basic text renderer

At this stage you probably just want to get some text on the screen as fast as possible. Fortunately, a basic text renderer isn’t hard to implement. Here’s one I prepared earlier:

public class FontRenderer
{
        public FontRenderer (FontFile fontFile, Texture2D fontTexture)
        {
                _fontFile = fontFile;
                _texture = fontTexture;
                _characterMap = new Dictionary<char, FontChar>();

                foreach(var fontCharacter in _fontFile.Chars)
                {
                        char c = (char)fontCharacter.ID;
                        _characterMap.Add(c, fontCharacter);
                }
        }

        private Dictionary<char, FontChar> _characterMap;
        private FontFile _fontFile;
        private Texture2D _texture;
        public void DrawText(SpriteBatch spriteBatch, int x, int y, string text)
        {
                int dx = x;
                int dy = y;
                foreach(char c in text)
                {
                        FontChar fc;
                        if(_characterMap.TryGetValue(c, out fc))
                        {
                                var sourceRectangle = new Rectangle(fc.X, fc.Y, fc.Width, fc.Height);
                                var position = new Vector2(dx + fc.XOffset, dy + fc.YOffset);

                                spriteBatch.Draw(_texture, position, sourceRectangle, Color.White);
                                dx += fc.XAdvance;
                        }
                }
        }
}

The FontRenderer class takes a FontFile from the BMFont loader and a MonoGame/XNA Texture2D as input. It creates a dictionary mapping for each character in the font for fast lookup during rendering. The DrawText method takes a MonoGame/XNA SpriteBatch, a postion and string to render. It loops through each character in the string and looks up the info for each character. It creates a source rectangle on the texture to find the appropriate glyph and offsets the character position because of the tight texture packing. The font also specifies how many pixels to advance the cursor for each character.

You can create a new instance of the FontRenderer in your LoadContent method.

_fontRenderer = new FontRenderer(fontFile, fontTexture);

And call the DrawText method from your main Draw method.

_fontRenderer.DrawText(_spriteBatch, 50, 50, "Hello World!");

This implementation is not very sophisticated, it doesn’t take into account any kerning, multiple lines or word wrapping. However, all of these features can be implemented from the data loaded from the BMFont XML file.

24 thoughts on “Tutorial: BMFont rendering with MonoGame

  1. Very helpful, thanks!
    I’m putting together a ‘quickstart’ project and set of tutorials for Monogame 3. Would you mind if this is included (with credit to you of course)?

    Reply
    • I’m glad you found it useful. You’re welcome to use it in the quickstart project and tutorials. I just ask that you put the URL somewhere and let me know when you’re done so I can take a look.

      Reply
  2. Nice article, never tough about doint it that way : I’m currently still using a ‘dummy’ VS true XNA content project to compile my fonts to .xnb and SpriteFont tool to create them then I just add the .xnb file as an asset to my monogame project. cheers

    Reply
  3. First: Thank You for this Awesome Tutorial ;)
    Second:
    If I start my programm every thing works fine, but if I close my programm I get an AccessVialationException any idea what this could be ?

    Reply
    • An AccessViolationException is pretty generic but since it’s happening when you close the program I’m going to have a stab in the dark and say you might not be disposing of an unmanaged resource correctly. Just a guess though, good luck.

      Reply
      • I did not add anything but your code =/ I even added the Content.Unload(); to the UnloadContent Method.

        And the Problem just appears if I use the FontRenderer.DrawText Method, and there is nothing to dispose or ?

        Reply
        • Not sure man. I’ve never experienced that problem with it before. Perhaps there’s something different about your setup. I’ve been developing for Android using MonoDevelop.

          If you can reproduce the issue in an example app or something perhaps I can take a closer look. It’s probably worth taking it to the MonoGame forums, they are more likely to know what’s going on here.

          Reply
          • Okay thanks for the tip,

            found Error, in the ProjektTemlpate I used the line “game.Dispose()” was missing. It is just needed if you draw some thing manualy like this text so the error just came if i used the textRenderer

            thanks ;)

    • In the Export options you’ll see a Presets drop down. You’ll probably want it set to ‘White text with alpha’ and your texture format as ‘png – Portable Network Graphics’

      If that doesn’t work let me know and I’ll look into it some more.

      Reply
  4. Thx for the quick answer. It doesn’t help to change the Presets to “White text with alpha”. If I set it and press ok, and go back into export setting its set as Custom again.
    It still has the black outline.

    Reply
  5. The front you provided works fine. So It must be a problem with my BMFont, No matter how my setting are I only get png’s with black background.

    Reply
  6. Most excellent, thank you! Been having quite a bit of trouble getting DrawText to work until I stumbled upon this gem.

    Reply
  7. Thanks for the post, I have one question, how are you handling big fonts ? (for example 64px). It will create more than one texture for the same font. Is there a way to output all in a single texture?

    Reply
    • Yes, that’s right. Currently the font loader only supports one texture. You can change the texture output size in the export options of BMFont to make sure it all fits. It can take a little tweaking to get right.

      You can also exclude characters you don’t need to save space by clicking them in the BMFont main interface. The grayed out characters will not be exported.

      Reply

Leave a reply

required

18,503 Spam Comments Blocked so far by Spam Free Wordpress

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>