Sprite-Based Text!

I just finished writing code for sprite-based text in unity. Since the game is low-fi, I wanted the font to be somewhat legit. Here’s the font I created for the old XNA version of the game:

font.png
letters and stuff

And here’s it rendering in Sportsball

Sportsball font in-game
We got font!

I created a simple class that will make all the necessary game objects to make text out of.  To use it, you simply put your sprite font into your Resources folder and set it to Texture Type “Sprite” and Sprite Mode “multiple”. Then, load it in via code where appropriate and set up your character indexes (more on this in a moment) . When you want to put text on screen, just create a SpriteText object. Here’s just the constructor:

SpriteText(string spriteFontName, Dictionary<string, Sprite> sprites, Dictionary<char, int> characterIndexes, int defaultCharacterIndex, float charSize, string text = null)

The first parameter is the sprite font name. When you make a multi-frame sprite, Unity names it “spritename_#” where spritename is the name of the font and # is the character number. Note that if you put your sprite font into a grid, Unity will ignore your Space character (and any blank tiles). To get around this, doodle in the space character with a solid color, slice the image in Unity, hit “Apply”, then delete your doodle in the image and save it.

The 2nd parameter is a dictionary of sprites keyed by name. I find it very useful to pre-load all your necessary sprites and keep them in a dictionary for handy access. Here’s how I do it:

Dictionary<string, Sprite> sprites;
Sprite[] spriteList = Resources.LoadAll("Sprites");
	
for (int i = 0; i < spriteList.Length; i++)
{
	if (!sprites.ContainsKey(spriteList[i].name))
	{
		sprites.Add(spriteList[i].name, spriteList[i]);
	}
}

The 3rd parameter is the character indexes. This is how the code looks up what the sprite # is based on the character you want to use. Here’s a snippet from this font:

Dictionary<char, int> FontCharacterIndexes = new Dictionary<char, int>()
	{
		{' ', 0},
		{'!', 1},
		{'"', 2},
		{'#', 3},

So for example when the code encounters the ‘#’ character, it knows to look for “font_3” in the sprites. Define all the characters you want and their indexes in this dictionary.

The 4th parameter is the sprite index you want to use if no appropriate character is found. This is handy to avoid problems if a weird character gets in some how (say, through user input). You could use zero in this example to replace unknown characters with a space.

The 5th parameter is the width of the font character –This only supports mono-spaced fonts at the moment. This parameter controls how spaced out the characters will be.

The last (optional) parameter is the text you want to display. You can always set or change the text later using SetText(text);

Here’s the whole class:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class SpriteText
{
	private string spriteFontName;
	private Dictionary<string, Sprite> sprites;
	private List characters = new List();
	private GameObject gameObject;
	private Dictionary<char, int> characterIndexes;
	private int stringLength = 0;
	private int defaultCharacterIndex;
	private float charSize;

	public Transform Transform
	{
		get
		{
			return gameObject.transform;
		}
	}

	public SpriteText(string spriteFontName, Dictionary<string, Sprite> sprites, Dictionary<char, int> characterIndexes, int defaultCharacterIndex, float charSize, string text = null)
	{
		gameObject = new GameObject("sprite text (" + spriteFontName + "): " + text);

		this.spriteFontName = spriteFontName;

		this.sprites = sprites;

		this.characterIndexes = characterIndexes;

		this.defaultCharacterIndex = defaultCharacterIndex;

		this.charSize = charSize;

		if (text != null && text != string.Empty)
		{
			SetText(text);
		}
		else
		{
			stringLength = 0;
		}
	}

	public void SetText(string text)
	{
		//save old scale, position and rotation
		Vector3 oldScale = gameObject.transform.localScale;
		gameObject.transform.localScale = Vector3.one;

		Vector3 oldPosition = gameObject.transform.position;
		gameObject.transform.position = Vector3.zero;

		Quaternion oldRotation = gameObject.transform.rotation;
		gameObject.transform.rotation = Quaternion.identity;

		//delete old game objects
		foreach (GameObject obby in characters)
		{
			GameObject.Destroy(obby);
		}

		//reset string length
		stringLength = 0;

		string spriteName = null;
		GameObject charObj = null ;
		SpriteRenderer spriteRenderer;
		bool charFound;

		float currentX = 0;

		characters = new List();

		if (text != null && text != string.Empty)
		{
			//loop through characters and create game objects
			foreach (char charry in text)
			{
				charFound = false;
				if (characterIndexes.ContainsKey(charry))
				{
					spriteName = string.Format("{0}_{1}", spriteFontName, characterIndexes[charry]);
					if (sprites.ContainsKey(spriteName))
					{

						charObj = new GameObject("char: " + charry);

						charFound = true;
					}
				}

				//use default character if characters not found
				if (!charFound)
				{
					spriteName = string.Format("{0}_{1}", spriteFontName, defaultCharacterIndex);
					if (sprites.ContainsKey(spriteName))
					{
						charObj = new GameObject("default char: " + charry);

						charFound = true;
					}
				}

				//if we have found a character, create all the stuff for the sprite 
				if (charFound && charObj != null && spriteName != null)
				{
					charObj.transform.parent = gameObject.transform;
					charObj.transform.position = new Vector3(currentX, 0, 0);
					spriteRenderer = charObj.AddComponent();
					spriteRenderer.sprite = sprites[spriteName];
					currentX += charSize;
					characters.Add(charObj);
					stringLength++;
				}
			}
		}

		//reset scale, position and rotation
		gameObject.transform.localScale = oldScale;
		gameObject.transform.position = oldPosition;
		gameObject.transform.rotation = oldRotation;
	}
}
Tags:

Add a Comment