XNA: minha classe Custom Font (usando bitmap fonts)

July 15, 2009

Nos últimos dois meses tive a honra e prazer de assistir algumas aulas como convidado do curso de XNA da Alpha Channel ministrado pelo prof. Willians Monteiro. Embora tenha participado dos Gamefests e lido uma ou outra coisa sobre a tecnologia, foi a primeira vez que tive contato com o XNA na prática e posso dizer que gostei bastante, tanto é que sempre que há um tempinho livre eu “brinco” com o XNA.

Em uma das aulas ele explicou que no XNA você pode criar e usar fontes através do SpriteFont, mas um porém é que nesse caso você usaria apenas as fontes do sistema e isso claro é uma restrição chata, já que geralmente a tipografia usada nos jogos vai de acordo com a direção de arte. Depois que ele comentou sobre um dos métodos SpriteBatch::draw(), resolvi criar a classe abaixo para escrever textos usando uma fonte bitmap.

No meu exemplo, criei a seguinte imagem:

Como você pode perceber pelos comentários no código, a implementação não ficou lá essas coisas, fiz algo rápido pra aprender. Há features a serem melhoradas e adicionadas (veja algumas sugestões no todo list)… Mas enfim, você captou a idéia né?

Obs.: há pequenas diferenças entre o código original e a listagem abaixo por causa da formatação html. Sugiro baixar o original aqui (incluindo a imagem da fonte).

/*
 * XNA Custom Font Class
 *
 * Written by
 * Andre Kishimoto
 * http://www.tupinihon.com
 *
 * June 20th, 2009
 *
 *
 * Use a bitmap font to draw strings instead of a Sprite Font.
 *
 * Usage example:
 * 

    public class [YourClass] : Microsoft.Xna.Framework.Game
    {
        // …

        // Declare a new CustomFont
        CustomFont customFont;

        protected override void LoadContent()
        {
            // …

            // Create customFont and define its properties
            customFont = new CustomFont(Content, “customfont”, 20, 20, 4, 12);
        }

        protected override void Draw(GameTime gameTime)
        {
            // …
            spriteBatch.Begin();
            // …

            // Draw string using CustomFont class
            customFont.drawString(spriteBatch, “Using CustomFont class”, new Vector2(10, 50), Color.White);

            // …
            spriteBatch.End();
        }

        // …
    }

 *
 * In this example, there is an Asset called customfont (CustomFontExample.png), where each character is 20×20. Its content is as follows:
 * ABCDEFGHIJKL
 * MNOPQRSTUVWX
 * YZ
 * 0123456789
 *
 * As you can see, it has 4 rows and 12 columns, where the last row is used only for numbers (see the code to understand this structure)
 *
 *
 * Todo list (or, things you can try on your own while studying XNA development)
 *
 * - Include proportional typeface fonts (different width for each character)
 * - Add special characters such as ç, ã, ô, é and so on (e.g. used in Spanish and Portuguese strings)
 * - Optimize code
 * - Remove assumption of numbers to be at last row in the bitmap font
 * - Redesign font bitmap scheme
 *
 */

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

public class CustomFont
{
    private Texture2D font;

    private int width;
    private int height;
    private int row;
    private int col;

    public CustomFont()
    {
    }

    public CustomFont(ContentManager content, String name, int w, int h, int row, int col)
    {
        load(content, name, w, h, row, col);
    }

    public void load(ContentManager content, String name, int w, int h, int row, int col)
    {
        font = content.Load<Texture2D>(name);
        this.width = w;
        this.height = h;
        this.row = row;
        this.col = col;
    }

    // I believe this code is not optimized
    public void drawString(SpriteBatch spriteBatch, String text, Vector2 pos, Color color)
    {
        Rectangle currPos = new Rectangle(0, 0, width, height);
        int currIndexY = 0;

        char[] tempText = new char[text.Length];
        tempText = text.ToUpper().ToCharArray();

        // Loop through the whole string, char by char, get correct char location inside the bitmap font and draw it
        for (int i = 0; i < tempText.Length; i++)
        {
            // We assume 0-9 numbers to be at last row in the bitmap font
            if (tempText[i] >= ‘0′ && tempText[i] <= ‘9′)
            {
                currPos.X = (tempText[i] - ‘0′) * width; // Calculate position of the character inside the bitmap font
                currIndexY = row - 1; // Note: fourth row is used for numbers only (in this example)
            }
            else if (tempText[i] >= ‘A’ && tempText[i] <= ‘Z’)
            {
                currPos.X = (tempText[i] - ‘A’) * width; // Calculate position of the character inside the bitmap font
                // Get a value between [0..2] (in this particular example, where there are 3 rows and 12 columns)
                // Note: fourth row is used for numbers only (in this example)
                // Used to get the correct mapping inside the bitmap font
                currIndexY = (currPos.X / (col * width));
            }
            else if (tempText[i] == ‘ ‘)
            {
                // If it is a blank space, just advance imaginary cursor and process next character
                pos.X += width;
                continue;
            }
            else
            {
                // If character is not found, simply advance imaginary cursor and process next character
                pos.X += width;
                continue;
            }

            // Get correct row inside the bitmap font
            currPos.Y = currIndexY * height;

            // Small adjustment to get correct column inside the bitmap font
            if (currPos.X >= col * width)
            {
                currPos.X -= currIndexY * (col * width);
            }

            // Draw the current character
            spriteBatch.Draw(font, pos, currPos, color);

            // Advance imaginary cursor position in X (we assume every character has the same width)
            pos.X += width;
        }

        tempText = null;
    }
}

Download do código original e imagem da fonte: clique aqui.


  1. 2 comentários para “XNA: minha classe Custom Font (usando bitmap fonts)”

  2. // Comment by Silvio (September 03, 2009 – 8:34 am):

    mas se ta ficando bom nisso eim! manda ver!

  3. // Comment by Jesus (October 16, 2009 – 7:43 am):

    Oi,
    Escrevo como um webmaster do site http://www.jogosdeonline.net/, eu realmente gosto de seu site e eu teria interesse em troncar Links com seu site.
    A link seria:
    URL: http://www.jogosdeonline.net/ Title: Jogos Online
    o
    Jogos Online
    Aguardamos sua resposta.
    Atenciosamente,
    Jesus

Escreva um comentário