17
\$\begingroup\$

My UI designer has made a lovely photoshop PSD of the UI and everything pretty. The biggest problem I'm having is converting some of the more elegant fonts used into something renderable in-game. Is there a way to convert these font styles in Photoshop to a bitmap font of some sort?

I need to be able to render text like this inside my code:

enter image description here

\$\endgroup\$
3
  • \$\begingroup\$ Have you checked sites like this? blogs.msdn.com/b/garykac/archive/2006/08/30/732007.aspx \$\endgroup\$
    – ssb
    Oct 31, 2012 at 6:02
  • \$\begingroup\$ Thank you very much for the bounty, but I noticed you did not mark it as the accepted answer. Are you dissatisfied with the response? Were you expecting a different type of answer? I assemble game GUIs as part of my day job, so I think I may be able to answer your questions. Feel free to ask for clarifications or more thorough explanations if you need so. \$\endgroup\$ Nov 12, 2012 at 5:26
  • \$\begingroup\$ Just an oversight on my part. :) I've corrected this now. Enjoy and thank you! \$\endgroup\$ Nov 12, 2012 at 19:01

7 Answers 7

18
+50
\$\begingroup\$

Okay, you're going to have to forgive me for not giving you specific XNA code, because I'm not knowledgeable in that platform, but what I'm going to tell you should work on any game engine that lets you draw sprites.

Fonts is not your only problem, so I'm going to give you a piece of advice, and then I'm going to answer your question. With these two things, you should be able to make a lovey-dovey relationship with your GUI designer, and you both will be able to very happily make games.

The first thing is that you're going to sit down with your designer, and you're going to ask her to give you two sets of files. The first is a set of transparent files that make up your GUI (optimally in PSD or DXT format). For every button, fixed label, background, border and textbox, you will get one file (you can also do texture atlasing, but I'd recommend you do that after you assemble your GUI, and then adjust your source coordinates when blitting). Non-static text should be left out at this point (I'll revisit this later).

The second thing you will get is the actual GUI design, this time in Photoshop format. For this file, you're going to ask your designer to make the entire GUI design, using only the files she previously gave you.

She's then going to put each GUI element into a separate layer, using no effects whatsoever. You're going to tell her to do this pixel perfect, because the locations where she's going to put everything, is where everything will actually be in the finalized game.

Once you get that, for each layer, you're going to press Ctrl-T, and on the Info pane (F8), you will take note of the X and Y coordinates for each element. Make sure your units are set to pixels (Preferences->Units & Rulers->Units). These are the positions you're going to use when drawing your sprites.

Now, for fonts, as you may clearly know now, you won't be able to get your fonts to look exactly the same way as you see them in Photoshop using text rendering APIs. You're going to have to pre-render your glyphs, and then programatically assemble your texts. There are many ways to do this, and I will mention the one I use.

First thing is to render all your glyphs into one or more files. If you only care about English, one texture for all the glyphs will suffice, but if you want to have a more extended character set, you can use several files. Just make sure all the glyphs you want are available on the font your designer chose.

So, to render the glyphs, you can use the facilities of System.Drawing to get the font metrics and draw your glyphs:

Color clearColor = Color.Transparent;
Color drawColor = Color.White;
Brush brush = new SolidBrush(drawColor);
TextRenderingHint renderingType = TextRenderingHint.AntiAliasGridFit; // Antialias is fine, but be careful with ClearType, which can blergh your renders when you apply effects
StringFormat stringFormat = StringFormat.GenericTypographic;
string fileNameFormat = "helvetica14_{0}.png";
string mapFileFormat = "helvetica14.txt";
string fontName = "Helvetica";
string fontPath = @"c:\windows\fonts\helvetica.ttf";
float fontSize = 14.3f;
int spacing = 2;

Font font = new Font(fontName, fontSize);
int x = 0;
int y = 0;
int width = 1024; // Force a maximum texture size
int height = 1024;
StringBuilder data = new StringBuilder();
int lineHeight = 0;
int currentPage = 1;
var families = Fonts.GetFontFamilies(fontPath);
List<char> codepoints = new List<char>();
HashSet<char> usedCodepoints = new HashSet<char>();
foreach (FontFamily family in families)
{
    var typefaces = family.GetTypefaces();
    foreach (Typeface typeface in typefaces)
    {
        GlyphTypeface glyph;
        typeface.TryGetGlyphTypeface(out glyph);
        foreach (KeyValuePair<int, ushort> kvp in glyph.CharacterToGlyphMap) // Render all available glyps
        {
            char c = (char)kvp.Key;
            if (!usedCodepoints.Contains(c))
            {
                codepoints.Add(c);
                usedCodepoints.Add(c);
            }
        }
    }
}
Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
Graphics g = Graphics.FromImage(bitmap);
g.Clear(clearColor);
g.TextRenderingHint = renderingType;

foreach (char c in codepoints)
{
    string thisChar = c.ToString();
    Size s = g.MeasureString(thisChar, font); // Use this instead of MeasureText()
    if (s.Width > 0)
    {
        s.Width += (spacing * 2);
        s.Height += (spacing * 2);
        if (s.Height > lineHeight)
            lineHeight = s.Height;
        if (x + s.Width >= width)
        {
            x = 0;
            y += lineHeight;
            lineHeight = 0;
            if (y + s.Height >= height)
            {
                y = 0;
                g.Dispose();
                bitmap.Save(string.Format(fileNameFormat, currentPage));
                bitmap.Dispose();
                bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
                g = Graphics.FromImage(bitmap);
                g.Clear(clearColor);
                g.TextRenderingHint = renderingType;
                currentPage++;
            }
        }
        g.DrawString(thisChar, font, brush, new PointF((float)x + spacing, (float)y + spacing), stringFormat);
        data.AppendFormat("{0} {1} {2} {3} {4} {5}\n", (int)c, currentPage, x, y, s.Width, s.Height);
        x += s.Width;
    }
}
g.Dispose();
bitmap.Save(string.Format(fileNameFormat, currentPage));
bitmap.Dispose();
File.WriteAllText(mapFileFormat, data.ToString());

With this, you've drawn white glyphs over a transparent background on a bunch of PNG files, and made an index file which tells you for each codepoint, in which file the glyph is located, its location and dimensions. Notice that I also put two additional pixels to separate each glyph (to accomodate for further effects)

Now, for each of those files, you put it in photoshop, and do all the filters you want. You can set colors, borders, shadows, outlines, and anything else you want. Just make sure that the effects don't make the glyphs overlap. If so, adjust the spacing, re-render, rinse and repeat. Save as PNG or DXT, and along with the index file, put everything in your project.

Drawing text should be very simple. For each char you want to print, find its location using the index, draw it, advance the position and repeat. You can also adjust for spacing, kerning (tricky), vertical spacing, and even coloring. In lua:

function load_font(name)
    local font = {}
    font.name = name
    font.height = 0
    font.max_page = 0
    font.glyphs = {}
    font.pages = {}
    font_definition = read_all_text("font/" .. name .. ".txt")

    for codepoint, page, x, y, width, height in string.gmatch(font_definition, "(%d+) (%d+) (%d+) (%d+) (%d+) (%d+)") do
        local page = tonumber(page)
        local height_num = tonumber(height)
        if height_num > font.height then
            font.height = height_num
        end
        font.glyphs[tonumber(codepoint)] = { page=tonumber(page), x=tonumber(x), y=tonumber(y), width=tonumber(width), height=height_num } 
        if font.max_page < page then
            font.max_page = page
        end
    end

    for page = 1, font.max_page do
        font.pages[page] = load_image("font/" .. name .. "_" .. page .. ".png")
    end

    return font
end

function draw_text(font, chars, range, initial_x, initial_y, width, color, spacing)
    local x = initial_x - spacing
    local y = initial_y - spacing
    if range == nil then
        range = { from=1, to=#chars }
    end
    for i = 1, range.to do
        local char = chars[i]
        local glyph = font.glyphs[char]
        if char == 10 then -- line break
            x = initial_x - spacing
            y = y + ((font.height - (spacing * 2)) * 1.4)
        elseif glyph == nil then
            if unavailable_glyphs[char] == nil then
                unavailable_glyphs[char] = true
            end
        else
            if x + glyph.width - spacing > initial_x + width then
                x = initial_x - spacing
                y = y + ((font.height - (spacing * 2)) * 1.4)
            end
            if i >= range.from then
                draw_sprite(font.pages[glyph.page], x, y, glyph.x, glyph.y, glyph.width, glyph.height, color)
            end
            x = x + glyph.width - (spacing * 2)
        end
    end
end

And there you go. Repeat for every other font (and optimally size as well)

Edit: I changed the code to use Graphics.MeasureString instead of TextRenderer.MeasureText() because they both use different measurement systems, and may lead to inconsistencies between the measured glyph and the drawn one, especially with overhanging glyphs found in some fonts. More information here.

\$\endgroup\$
2
  • 2
    \$\begingroup\$ Why did I get a downvote on this? If you're going to downvote, please at least give some comments on what I did wrong so I can fix it next time. \$\endgroup\$ Nov 6, 2012 at 1:59
  • \$\begingroup\$ Not me. This is an excellent in-depth answer. +1 :-) \$\endgroup\$
    – Kromster
    Apr 8, 2015 at 5:05
6
\$\begingroup\$

Well, like someone else said, in XNA, spritefont does the heavy lifting for you. on the creators club website there is a bitmap font exporter that exports in XNA style font images. (http://xbox.create.msdn.com/en-US/education/catalog/utility/bitmap_font_maker) Then, you can open it in photoshop or whatever and make it look pretty. From there, you add the texture to your content project, and in the content type, select sprite font texture. In your code, you load it like a normal sprite font

(Example: http://www.youtube.com/watch?v=SynGWrWYUQI)

\$\endgroup\$
6
  • \$\begingroup\$ The problem is I'm not exactly sure how one can take the styles from a Photoshop font and apply them to a texture like this. \$\endgroup\$ Nov 5, 2012 at 22:49
  • \$\begingroup\$ Here, I shall make a video \$\endgroup\$
    – CobaltHex
    Nov 5, 2012 at 23:42
  • 3
    \$\begingroup\$ youtube.com/watch?v=SynGWrWYUQI \$\endgroup\$
    – CobaltHex
    Nov 6, 2012 at 0:09
  • \$\begingroup\$ BMFont does the same thing, but in my opinion is a better program. angelcode.com/products/bmfont \$\endgroup\$
    – Cypher
    Nov 6, 2012 at 23:16
  • \$\begingroup\$ eh, by exporting a basic spritefont, you can customize it however you want in photoshop \$\endgroup\$
    – CobaltHex
    Nov 7, 2012 at 4:53
4
\$\begingroup\$

The solution is quite simple and is used by a great number of games. All you have to do is treat your fonts as if they were sprites.

Have your designer draw out the entire range of numbers and letters that you want to use in your game. Then render them to varying sized static images (.png, .bmp, whatever format you use). You'll have something like this:

enter image description here

Now all you have to do is render each letter from your "font sheet" as if it were a sprite to the screen. It certainly helps to build a helper class to translate between strings and sprites.

My implementation is more complex, but handy. The font sheet is built like the above image with multiple fonts all in one .png file. I have a .ini file that maps each fonts' letter to a position on the sheet, along with its width and height. This lets my designer (and myself) go nuts making cool fonts without having to touch any code. When drawing a string to the screen, I have a method that looks up the font and the char from the .ini file to get the position and boundaries of the letter from the font sheet, then I just draw the Texture2D with SpriteBatch.Draw() using the source Rectangle of the letter in question.

\$\endgroup\$
2
\$\begingroup\$

UI is a vast and complex subject. Font rendering is a tough part. I advise you to use an already-existing library that allows you to display Flash or HTML content in-game, instead of redoing everything yourself.

Awesomium looks promising and should work with XNA, so you could give it a try. It's free for use for non-commercial games, or if you're not making tons of money:

Free for Indie Companies
    If your company made less than $100K last year, you qualify!
Free for Non-Commercial Use
Free for Evaluation & Development

\$\endgroup\$
7
  • \$\begingroup\$ That's a pretty nifty idea - I guess it abstracts away some of the UI code I have to write, too. I'll give it a try. Thanks! \$\endgroup\$ Oct 30, 2012 at 13:56
  • \$\begingroup\$ As it's a port of the Webkit engine you should be able to do most of the text styles you need with CSS, including stroke outlines, drop shadows and gradients. \$\endgroup\$
    – ChrisC
    Oct 31, 2012 at 16:14
  • \$\begingroup\$ that is way over complexifying for simply drawing fonts \$\endgroup\$
    – CobaltHex
    Nov 5, 2012 at 20:43
  • 1
    \$\begingroup\$ @CobaltHex I've seen this attitude so many times and so many spoiled UI that derivated from an initial "yey let's make a UI from scratch it must be easy, we'll reinvent the wheel while we're at it" that I grew sick of it. As an exercise, check the OP mock-up picture, and imagine you have to render a whole UI in the same style across several platforms, screen resolutions and ratios. Now the need for a proper UI becomes clear, that's why I took the liberty to post this answer, even if it's not a direct solution to the given problem. \$\endgroup\$ Nov 5, 2012 at 21:57
  • \$\begingroup\$ I am not saying that you make a UI from scratch, but if you just want to draw a font, inserting an entire rendering engine is a bit overkill \$\endgroup\$
    – CobaltHex
    Nov 5, 2012 at 23:44
1
\$\begingroup\$

If you are looking for fancier effects than the plain .spritefont importer, you can try searching for a "bitmap font generator".

Personally I prefer this one: http://www.ironstarmedia.co.uk/2010/01/free-game-dev-utility-fancy-bitmap-font-generator/

Some other ideas:

  • Use a bitmap font generator and then edit the bitmap it produces further in photoshop, etc.
  • Put as much static text as possible into pre-drawn sprites. Then fill in just things like gamertags and score totals using .spritefonts or fonts generated from a bitmap font generator.
\$\endgroup\$
1
  • \$\begingroup\$ I've definitely seen that (and it's very cool, by the way) but I was hoping to directly turn my Photoshop text styles into usable, bitmap fonts. \$\endgroup\$ Oct 30, 2012 at 4:52
0
\$\begingroup\$

XNA does all of the hard work for you. With Spritefont you can easily convert a font file on your machine into the kind of sprite sheet you are asking about by defining an XML file.

Once you add the XML file to your Content project load it with the ContentManager:

ContentManager.Load<SpriteFont>(@"MyFont");

Here is an example of a .spritefont file from my content project:

<?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.
//TODO: A different font should be chosen before shipping for licensing reasons
-->
    <FontName>Pericles</FontName>

    <!--
Size is a float value, measured in points. Modify this value to change
the size of the font.
-->
    <Size>8.5</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>Bold</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>&#32;</Start>
            <End>&#126;</End>
        </CharacterRegion>
        <CharacterRegion>
            <Start>&#9;</Start>
            <End>&#9;</End>
        </CharacterRegion>
    </CharacterRegions>
</Asset>
</XnaContent>
\$\endgroup\$
2
  • 1
    \$\begingroup\$ The problem is these are just plain, TrueType fonts. I need bitmap fonts or something with the like with all my dropshadow, glows and the like still applied. \$\endgroup\$ Oct 30, 2012 at 2:31
  • \$\begingroup\$ See my edit, font(s) like these: i.imgur.com/VaBpQ.png \$\endgroup\$ Oct 30, 2012 at 2:53
0
\$\begingroup\$

I am not sure as I never used it but people suggested me to use Glyph Designer. Here is a video tutorial:

http://vimeo.com/32777161

Here there is a discussion on it mentioning photoshop.

There are people using also Hiero font editor.

In this website there is a list of other font editor you might want to have a look at.

Let me know which one works best for you :)

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .