While converting some old 2D Toolkit-based Unity code to plain vanilla Unity recently, I ran into a conundrum: Unity’s got great support for standard font formats, but still isn’t quite up to par with tk2d’s support for building fonts from spritesheets.
It’s a minor issue–after all, it’s easier and more legible to drop in an existing font–but I wanted to keep the hand-drawn look.
So, I fell down the rabbit hole of cataloging the different options Unity provides for UI text (including TextMesh Pro, recently acquired by Unity and built in to 2018.1). While my knowledge of typography is pretty shallow (and it seems to be a rather complex topic), this should give you an idea of what’s out there and why you might want to use it.
Standard Font Asset
The standard Unity support for .ttf and .otf font files is the simplest and most common way to get text in your game.
Behind the scenes, this seems to be a dynamically-created sprite font. Unity builds a texture from the font given a particular font size.
Source: Font assets are automatically built from .ttf or .otf files.
Usage: UI Text components only
Scaling options: Text can be freely scaled in the UI Text component. Scaling the font asset itself will increase the size of the texture generated from the font, making the end result sharper.
Pros/cons: Simple to use, but you’re limited to whatever the imported font supports.
More information: Unity documentation on Fonts
Custom Font
Unity does have the ability to create custom sprite fonts, but it’s somewhat limited in the area of scaling.
Source: Custom fonts are built from a Material (which references a Texture), as well as a set of character mappings.
The character mappings seem a bit obtuse to me (though I assume it’s easier if you understand UV coordinates), and there doesn’t seem to be a GUI tool for generating these from the spritesheet itself. Each character has the following properties:
- Index: The index of the ASCII character
- UV texture coordinates: Ranges from 0 to 1, as a percentage of the texture’s width and height
- Vert: Pixel coordinates
- Advance: Number of pixels to advance before drawing the next character; higher numbers space letters farther apart.
Scaling options: Scaling seems to be the custom font’s weak point. From what I can tell, custom fonts ignore the “Font Size” property of Text components, and are limited to whatever the imported texture’s size is.
You can set the scale of the game object containing the Text component. However, this changes the borders of the element, so it’s rather inconvenient if you’re trying to line up differnet elements.
Usage: UI Text components only
Pros/cons: It’s native Unity support for sprite fonts, but you can only adjust size using scaling. No tool for generating character mappings; you have to write them out by hand.
More information: Unity documentation on Fonts
TextMesh Pro Font Asset
Unlike Unity, TextMesh Pro has a single format for both font files and sprite fonts, and the behavior of both types of fonts are similar.
The downside to TextMesh Pro fonts is that they can only be used with TextMesh Pro UI components. If you think there’s a reason to use TMP, it’s better to make this decision early on and use it consistently throughout your project. Trying to retrofit an existing project written with standard UI Text components would be a pain.
Source: TMP Font Assets are built from a Material and character mappings, much like Unity custom fonts.
Character mappings are only in pixel coordinates, not UV mappings, so it’s easier and more precise than Unity custom fonts. There’s also a tool called Font Asset Creator that will build a TMP font asset from a font file. Still, it’s a tedious process for sprite fonts.
Scaling options: You can scale TMP font assets in the TMP UI component by font size without having to change the scaling of the game object. In my experience, this is a reason to choose TextMesh Pro over native Unity Text if there’s any reason you’ll want to use a sprite font.
Usage: TextMesh Pro – Text UI components only
Pros/cons: More flexible than Unity’s font assets or custom fonts, but requires using TextMesh Pro’s own UI Text component. There’s no tool for generating mappings from spritesheets; you have to build them out by hand.
More information: TextMesh Pro documentation for Font Assets
TextMesh Pro Sprite Asset
TMP sprite assets are a little out of place on this list–they’re not really a font asset in the same sense as the other three types. Rather, they’re an extra feature that TextMesh Pro – Text components give you.
Sprite assets solve the problem of mixing standard text with game-specific symbols or icons (for example, think of the item symbols that show up in Final Fantasy inventories).
Usage: TextMesh Pro – Text UI components. You can specify one TMP font asset and one TMP sprite asset for each component.
To reference a sprite icon in text, use the tag <sprite index=#> (where # is the index of the sprite, starting at 0).
Source: TMP Sprite Assets are built from a Material and character mappings. They’re conceptually similar to TMP font assets. The Sprite Importer is a slightly better tool than the Font Asset Creator, because it can use FNT files to generate character mappings for a spritesheet. (See notes about FNT files in the next section.)
Pros/cons: None, as they’re really a side benefit of using TextMesh Pro. If there’s any possibility you’ll want to use this functionality in a project, it’s a good reason to choose TextMesh Pro early on.
More information: TextMesh Pro documentation for Sprites
Generating Custom Fonts and TMP Font Assets from FNT files
This could be a whole other blog post in itself, but it’s worth noting in passing–it makes custom fonts and TMP font assets far less tedious to create.
A major downside of creating sprite fonts (using either Unity custom fonts and TMP font assets) is that there’s no GUI tool to define characters from the spritesheet. Essentially, you’re typing in a bunch of numbers, testing, and readjusting, which is a tedious process.
The good news is, there’s a more-or-less standard text format for this information, which is used by various GUI tools for building sprite fonts. (I’ve even written a simplified utility myself which partially supports the FNT spec.)
The bad news is, Unity custom fonts and TMP font assets don’t seem to support this by default.
However, Unity supports the concept of asset post-processors, which can read in raw files in a project and convert them into assets that can be used in code. Asset post-processors fire when an assed is imported or re-imported.
I’ve written a very basic FNT-to-TextMesh Pro Font Asset converter here. YMMV, but it may be worthwhile to use it as an example. If you can write a converter that’s good enough for your needs, you can offload sprite font creation to a more efficient tool, saving yourself the hassle of mapping by hand.
In Summary
Here’s a quick comparison:
Font Asset | UI Component | Source | Configuration | Benefits/Drawbacks |
---|---|---|---|---|
Font (Unity standard) | UI Text | .ttf or .otf | Mostly automatic | Simple to use, but can’t use sprites or textures |
Custom Font | UI Text | Material, Texture, and character mapping | Must define each character manually using UV coordinates. (Possible to script asset creation from FNT files or other sources using AssetPostprocessor.) | No way to adjust font size except by scaling GameObject. No built-in GUI tool for mapping characters to a texture. |
TextMesh Pro Font Asset | TextMesh Pro – Text | Material, Texture, and character mapping | Must define each character manually using pixel coordinates. (Possible to script asset creation from FNT files or other sources using AssetPostprocessor.) | No built-in GUI tool for mapping characters to a texture. Can’t use with standard Unity UI Text components. |
TextMesh Pro Sprite Asset | TextMesh Pro – Text | Material, Texture, and character mapping | Must define each sprite manually using pixel coordinates or via Sprite Importer. (Possible to script asset creation from other sources using AssetPostprocessor.) | Used in conjunction with TextMesh Pro Font Assets to add custom icons not tied to a character. |