Unity font extraction
I just stumbled across a paste of some useful unity code I wrote roughly a year ago: font extraction.
Unity’s fonts can be dynamically generated, or generated from a character set at build time. When you pick a character set, Unity generates a font texture, which you can access and even replace. There are a number of blog posts on the topic, and there’s an entry in the wiki. But, all of these other posts are just about extracting the texture. They don’t address the issue of font data.
The unity font is basically 3 things: a texture, a mapping from chars to rectangles in the texture, and spacing information. For most fonts, the spacing is 0, or at least constant, but it can have custom spacing for each pair of adjacent characters. But since it’s usually 0, i’m going to ignore spacing in today’s code (but you can get at it with the same technique). And the way unity stores that information is with serialized properties on the asset. That means that you can get at them in the editor, but not at runtime, so I made a script to pull it out into something i could use at runtime. It also means that it’s gross and uses strings.
Anyway, here’s some code to pull an atlas out of a unity texture:
public class SpriteFont : ScriptableObject {
public Glyph[] glyphs;
public Texture2D sprite_texture;
[Serializable]
public class Glyph {
public Rect uv;
public Rect vert;
public float width;
public int index;
}
}
SpriteFont BuildFont(Font font, string asset_path) {
var sprite_font = ScriptableObject.CreateInstance<SpriteFont>();
AssetDatabase.CreateAsset(sprite_font, asset_path);
//pull out font atlas info
var sFont = new SerializedObject(font);
var size = sFont.FindProperty("m_CharacterRects.Array.size").intValue;
sprite_font.glyphs = new SpriteFont.Glyph[size];
for (int i = 0; i < size; ++i) {
var prefix = "m_CharacterRects.Array.data[" + i + "]";
var g = new SpriteFont.Glyph();
sprite_font.glyphs[i] = g;
g.uv = GetRect(sFont, prefix + ".uv");
g.vert = GetRect(sFont, prefix + ".vert");
g.index = sFont.FindProperty(prefix + ".index").intValue;
g.width = sFont.FindProperty(prefix + ".width").floatValue;
}
//get the font texture
var path = AssetDatabase.GetAssetPath(font);
var tex = AssetDatabase.LoadAssetAtPath(path, typeof(Texture2D)) as Texture2D;
sprite_font.sprite_texture = tex;
EditorUtility.SetDirty(sprite_font);
return sprite_font;
}
For each character, you have the uv coordinates in the texture, the screen-space rectangle, how wide it is (how much to offset the next char by), and the character itself (index is the character, as an int). If you were making a robust tool, it should also extract the line height, and the kerning (spacing), and if you’re feeling extra robust, the per-character kerning.
Twisted Oak Studios offers consulting and development on high-tech interactive projects. Check out our portfolio, or Give us a shout if you have anything you think some really rad engineers should help you with.
Older Posts
- Unfathomable Bugs #6: Pretend Precision
- My Bug, My Bad #3: Accidentally Attacking WarCraft 3
- Collapsing Types vs Monads (followup)
- Collapsing Futures: Easy to Use, Hard to Represent
- Eventual Exceptions vs Programming in a Minimal Functional Style
- The Mystery of Flunf
- Explain it like I’m Five: The Socialist Millionaire Problem and Secure Multi-Party Computation
- Computer Science Blows My Mind
- A visit to Execution Labs in Montréal
- Transmuting Dice, Conserving Entropy
- Rule of Thumb: Ask for the Clock
- Rule of Thumb: Use Purposefully Weakened Methods
- Rule of thumb: Preconditions Should be Checked Explicitly
- Intersecting Linked Lists Faster
- Mouse Path Smoothing for Jack Lumber
- My Bug, My Bad #2: Sunk by Float
- Repeat Yourself Differently
- Grover’s Quantum Search Algorithm
- Followup to Non-Nullable Types vs C#
- Optimizing Just in Time with Expression Trees
- When One-Way Latency Doesn’t Matter
- Determining exactly if/when/where a moving line intersected a moving point
- Emulating Actors in C# with Async/Await
- Making an immutable queue with guaranteed constant time operations
- Improving Checked Exceptions
- Perishable Collections: The Benefits of Removal-by-Lifetime
- Decoupling shared control
- Decoupling inlined UI code
- Linq to Collections: Beyond IEnumerable<T>
- Publish your .Net library as a NuGet package
- When null is not enough: an option type for C#
- Unfathomable Bugs #5: Readonly or not
- Minkowski sums: examples
- My Bug, My Bad #1: Fractal Spheres
- Working around the brittle UI Virtualization in Windows 8
- Encapsulating Angles
- Unfathomable Bugs #4: Keys that aren’t
- How would I even use a monad (in C#)?
- Useful/Interesting Methods #1: Observable.WhenEach
- Unfathomable Bugs #3: Stringing you along
- Anonymous Implementation Classes – A Design Pattern for C#
- Tasks for ActionScript 3 – Improving on Event-Driven Programming
- Minkowski sums and differences
- Non-Nullable Types vs C#: Fixing the Billion Dollar Mistake
- Unfathomable Bugs #2: Slashing Out
- Script templates and base classes
- Abusing “Phantom Types” to Encode List Lengths Into Their Type
- Constructive Criticism of the Reactive Extensions API
- Quaternions part 3
- Quaternions part 2
- Quaternions part 1
- Unfathomable Bugs #1: You can have things! You can have things IN things! You can have …
- Coroutines – More than you want to know
- Asset Bundle Helper
- The Visual Studio goes away
- .Net’s time traveling StopWatch
- Polish
- Introducing Catalyst
