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.
Publicado em gamedev, xna | 2 comentários »