【Unity】FontEngineで軽量テキスト描画
これはUnity Advent Calendar 2023 (2) の8日目の記事です。
UnityEngine.TextCore.LowLevel.FontEngine
は、TextMesh ProやUI Toolkitがバックエンドとして使用している文字描画エンジンです。
その機能の多くはinternal
でユーザーに公開されておらず、使える機能もかなり低レベルですが、そのかわり高速に動作します。基本的な文字描画機能のみを必要としていて、TextMesh Proだとオーバースペックである場合は、直接FontEngine
を使用してみるのもありなんじゃないかと思います。
……とは言いつつ、そんな場面はかなり限定的なので、基本的にはネタ記事です。
基本的な使い方
① フォントをロードする
FontEngine.LoadFontFace()
を使ってFont
を読み込みます。
一度にロード状態にできるフォントは1つまでで、基本的に最後にロードしたフォントが使用されます。
using UnityEngine;
using UnityEngine.TextCore.LowLevel;
[SerializeField] private Font font;
FontEngine.Initialize();
FontEngine.LoadFontFace(font, 90); // フォントサイズ=90
② Glyphを取得する
FontEngine.TryGetGlyphWithUnicodeValue()
を使って、特定の文字の情報を示すGlyph
構造体を取得します。
if (!FontEngine.TryGetGlyphWithUnicodeValue(character,
GlyphLoadFlags.LOAD_NO_BITMAP, out var glyph))
{
// Glyphの取得に失敗した場合
}
③ テクスチャに描画する
FontEngine.RenderGlyphToTexture()
を使用して、対象のテクスチャに文字を描画することができます。
RenderGlyphToTexture()
はinternal
なので、無理やり呼び出すためのコードを先に用意してしまいます。
public static class FontEngineProxy
{
private static Func<Glyph, int, GlyphRenderMode, Texture2D, FontEngineError> dRenderGlyphToTexture;
public static FontEngineError RenderGlyphToTexture(
Glyph glyph,
int padding,
GlyphRenderMode renderMode,
Texture2D texture)
{
if (dRenderGlyphToTexture == null)
{
var method =
typeof(FontEngine).GetMethod("RenderGlyphToTexture", BindingFlags.Static | BindingFlags.NonPublic);
dRenderGlyphToTexture =
(Func<Glyph, int, GlyphRenderMode, Texture2D, FontEngineError>)Delegate.CreateDelegate(
typeof(Func<List<Glyph>, int, GlyphRenderMode, Texture2D, FontEngineError>),
null, method);
}
return dRenderGlyphToTexture.Invoke(glyph, padding, renderMode, texture);
}
}
実際に描画を行うコードは次のような感じです。
using UnityEngine.TextCore;
// テクスチャ上の描画位置を指定(ピクセル単位)
glyph.glyphRect = new GlyphRect(x, y, width, height);
FontEngineProxy.RenderGlyphToTexture(glyph, 0, GlyphRenderMode.SMOOTH_HINTED, texture);
texture.Apply();
注意点として、テクスチャフォーマットはR8である必要があり、他のフォーマットだと正しく描画されません。グレースケールを前提とした実装ですね。
その他のFontEngineの機能(抜粋)
RenderGlyphsToTexture()
複数のグリフをListとして渡し、一度に描画してくれるAPIです。
特に検証はしていませんが、RenderGlyphToTexture()
を繰り返し呼ぶより効率よく描画を行えそうな雰囲気があります。
TryPackGlyphInAtlas()
/ TryPackGlyphsInAtlas()
アトラス上の空いているスペースに文字を詰めてくれるAPIです。TextMesh Proの内部で使用されています。
つかいみち
以上でわかると思いますが、FontEngine
がやってくれるのは、テクスチャに文字を描画するところまでです。また、FontEngine
はCPU動作なので、毎フレームの描画よりはアトラスの作成用途が基本になると思います。
使用例
unicode上の文字を連続的にグリッド上に配置する、という機能の実装にFontEngine
を使用しました。
以前は一文字ごとに別々のTMP Textを配置していましたが、スクロール時にFPSが大きく低下していました。原因はおそらく、TextMesh Proのダイナミックアトラスにおける文字のパッキング処理が1文字ずつ頻繁に発生したことにあります。
今回は文字の配置がグリッド状で、画面上に同時に表示される文字の数も固定数なので、TextMesh Proの柔軟なダイナミックアトラスシステムはオーバースペックでした。必要なアトラステクスチャのサイズは固定されていますし、文字のパッキングも一行ずつ行えばいいので、計算量の面でも有利なはずです。
ということで、使用できる場面は非常に限定的ですが、FontEngine
使えると色々便利だよ、という記事でした。
Discussion