拡大したテキストの輪郭がギザギザ。日本語を表示したら一部が「□」。スコア表示を毎フレーム更新したらなぜか重い——Unityのテキスト表示の悩みは、だいたいこの3つに集約されます。そして3つとも、TextMeshPro(TMP) を正しく理解すれば解決できます。
この記事では、TextMeshProの品質の根幹であるSDFの仕組みから、日本語表示でつまずくフォントアセットまわり、そしてゲームで多用する動的テキストのパフォーマンス最適化までを解説します。
この記事でわかること
- SDF——拡大してもぼやけないテキスト描画の仕組み
- フォントアセットの作成と、日本語の文字抜け(□表示)対策
TextMeshProUGUIのスクリプト操作とリッチテキストタグ- ドローコール削減(フォントアセット・マテリアルプリセットの共有)
- メッシュ再構築を避ける更新方法(毎フレーム更新の罠)
TextMeshProの基本概念:SDFとは?
TextMeshProが標準のテキストコンポーネントと決定的に異なるのは、テキストのレンダリングにSDF (Signed Distance Field) という技術を採用している点です。
標準のテキストは、フォントをビットマップ画像としてテクスチャに焼き付けて使用します。この方式では、テキストを拡大するとピクセルが引き伸ばされ、エッジがぼやけたりギザギザになったりします。
一方、SDFは、文字の輪郭からの距離情報をテクスチャに格納します。これにより、GPUがこの距離情報を使ってリアルタイムで文字の輪郭を滑らかに描画するため、どれだけ拡大・縮小しても、高解像度で鮮明なテキスト表示が可能になります。これが、TextMeshProの「高品質」の根幹です。

TextMeshProの導入方法
TextMeshProはUnityの標準パッケージです。Window -> Package Manager を開き、Unity Registry から Text Mesh Pro をインストールします。シーンに配置するには、GameObject -> UI -> TextMeshPro - Text を選択します。
初心者が躓きやすいポイントと解決策
1. フォントアセットの作成を忘れる
TextMeshProは、通常のフォントファイル(.ttfや.otf)をそのまま使用できません。必ず専用のフォントアセット(Font Asset) を作成する必要があります。
躓きポイント: シーンにTMPテキストを配置しても、フォントがデフォルトの「Liberation Sans SDF」のままで、日本語フォントなどを設定しても反映されない。
解決策:
- 使用したいフォントファイル(例:
NotoSansJP-Regular.otf)をプロジェクトにインポートします。 - インポートしたフォントファイルを右クリックし、
Create->TextMeshPro->Font Assetを選択します。 - 生成されたフォントアセットを、TMPコンポーネントの
Font Assetスロットにドラッグ&ドロップします。

2. 文字の抜け(アトラスのサイズ不足)
日本語や中国語など、文字種が多い言語を使用する場合、作成したフォントアセットにすべての文字が含まれず、一部の文字が「□」や「?」と表示されてしまうことがあります。これは、フォントアセットのテクスチャ(アトラス)に、必要な文字がすべて焼き付けられていないためです。

解決策: フォントアセット作成時に、Character Setの設定を見直します。
- カスタム文字リストを使用する: 必要な文字だけをテキストファイルにリストアップし、
Custom Charactersに設定するのが最も効率的です。 - ダイナミックアトラスを使用する:
Atlas Population ModeをDynamicに設定すると、実行時に必要な文字を自動でアトラスに追加してくれます。ただし、パフォーマンスへの影響やメモリ使用量の増加に注意が必要です。
動的なテキスト操作とリッチテキスト
TextMeshProは、標準のテキストコンポーネントと同様にC#スクリプトから簡単に操作できますが、参照するコンポーネントの型が異なります。
1. 基本的なテキストの更新
TMPコンポーネントを操作するには、using TMPro; を宣言し、TextMeshProUGUI(uGUI用)または TextMeshPro(3D用)を参照します。
using UnityEngine;
using TMPro; // 必須
public class TmpTextUpdater : MonoBehaviour
{
// インスペクターからTextMeshProUGUIコンポーネントをアタッチ
[SerializeField]
private TextMeshProUGUI scoreText;
private int currentScore = 0;
void Start()
{
// 初期テキストを設定
UpdateScoreText();
}
// スコアを増加させ、テキストを更新するメソッド
public void AddScore(int amount)
{
currentScore += amount;
UpdateScoreText();
}
private void UpdateScoreText()
{
// テキストを直接更新
scoreText.text = "現在のスコア: " + currentScore.ToString();
}
}
テキストの更新は、可能な限りUpdate()やLateUpdate()内ではなく、イベントドリブン(スコアが変更されたときなど)で行うようにしましょう。毎フレームのテキスト更新は、メッシュの再構築を発生させ、パフォーマンスを低下させる主要因となります。
2. リッチテキストタグの活用
TextMeshProの強力な機能の一つが、HTMLに似たリッチテキストタグです。これにより、スクリプトから動的に文字の色、サイズ、スタイルなどを変更できます。
using UnityEngine;
using TMPro;
public class TmpRichTextExample : MonoBehaviour
{
[SerializeField]
private TextMeshProUGUI messageText;
// 重要なメッセージをリッチテキストで表示する
public void DisplayImportantMessage(string playerName, int damage)
{
// <color>、<b>、<size>などのタグを使用
string richMessage = $"<color=#FF0000><b>{playerName}</b></color>に、<size=150%>{damage}</size>ダメージを与えました!";
messageText.text = richMessage;
}
}
この例では、<color=#FF0000>で色を、<b>で太字を、<size=150%>でサイズを動的に変更しています。
tips: 点滅などの連続的な変化を
textプロパティの毎フレーム書き換えで作るのは避けましょう(後述のメッシュ再構築が毎フレーム走ります)。透明度の点滅なら、文字列はそのままにmessageText.alphaやcolorプロパティを変える方が軽量です。
パフォーマンス最適化の秘訣
TextMeshProは標準テキストより高性能ですが、不適切な使い方をするとパフォーマンスを損なう可能性があります。
1. ドローコールの削減
TextMeshProの最大の最適化ポイントは、同じフォントアセットとマテリアルを使用するテキストオブジェクトは、可能な限り一つのドローコールにまとめられるという点です。

- フォントアセットの統一: UI全体で、同じフォントアセットを使用するように徹底しましょう。
- マテリアルインスタンスの共有: テキストの色やアウトラインなどの設定を、マテリアルプリセットとして作成し、複数のテキストオブジェクトで共有することで、ドローコールを削減できます。
2. テキストの再構築を避ける
テキストの内容が変更されると、TextMeshProはテキストメッシュを再構築します。これは負荷の高い処理です。
- 文字列の変更を最小限に: スコア表示など、頻繁に更新されるテキストは、文字列の生成(特に
string.Formatや文字列結合)を最適化し、必要なときだけ更新するようにします。 - 非表示テキストの無効化: 画面外や非表示のテキストは、
gameObject.SetActive(false)で無効化するのではなく、textComponent.enabled = falseを使用してコンポーネントのみを無効化することで、Unityの描画パイプラインから除外され、パフォーマンスが向上します。
実践:浮かび上がるダメージ数字を組む
RPGの攻撃ヒット、ローグライクのクリティカル、放置ゲームのタップ演出——「敵の頭上に数字がふわっと浮かんで消える」は、ジャンルを問わず登場するTextMeshProの代表的な使いどころです。ここまでのSDF・リッチテキスト・軽量な更新方法を、1つの演出に組み上げてみましょう。

仕組みは「3D用のTextMeshProを生成 → 上昇させながらalphaを下げる → 消えたら破棄」です。ダメージ数字のプレハブ(TextMeshProコンポーネント付き)を用意して、次のスクリプトをアタッチします。
using UnityEngine;
using TMPro;
public class DamagePopup : MonoBehaviour
{
[SerializeField] private float floatSpeed = 1.5f; // 上昇速度
[SerializeField] private float lifetime = 0.8f; // 表示時間
private TextMeshPro tmp; // 3D用(uGUIではない方)
private float elapsed;
void Awake() => tmp = GetComponent<TextMeshPro>();
// 生成直後に呼び出してダメージ値をセット(クリティカルはリッチテキストで強調)
public void Setup(int damage, bool isCritical)
{
tmp.text = isCritical
? $"<size=150%><b>{damage}</b></size>"
: damage.ToString();
}
void Update()
{
// 上へ浮かせる
transform.position += Vector3.up * floatSpeed * Time.deltaTime;
// textは書き換えず、alphaだけを下げる(メッシュ再構築が走らない)
elapsed += Time.deltaTime;
tmp.alpha = 1f - (elapsed / lifetime);
if (elapsed >= lifetime) Destroy(gameObject);
}
}
呼び出し側は、敵がダメージを受けた場所にプレハブをInstantiateしてSetup(damage, isCritical)を呼ぶだけです。
ポイントは2つです。
textの書き換えは生成時の1回だけ: フェードはtmp.alphaで行います。textプロパティを毎フレーム書き換えるとメッシュ再構築が毎フレーム走りますが、alpha(頂点カラー)の変更なら再構築は起きません。本文で解説した「更新の最適化」の実践形です。- 数字が大量に出るゲームはプーリングへ: 連続ヒットで毎回
Instantiate/DestroyするとGCスパイクの原因になります。手数が増えてきたら オブジェクトプーリングの記事 の仕組みに載せ替えるのが定番の進化です。
おまけ:先に知っておくと良いこと
- フォールバックフォント: メインのフォントアセットにない文字(絵文字・他言語など)は、
Fallback Font Assetsに登録した別アセットから自動で探されます。日本語+絵文字の組み合わせでは実質必須の設定です。 - Font Asset Creatorの主要設定: 日本語アセットを作る際は
Sampling Point Size(文字の元解像度)とAtlas Resolution(アトラスサイズ、日本語なら2048〜4096)、Paddingのバランスが品質を決めます。まずはAuto Sizing+2048から。 - 3D空間のテキスト: uGUI用の
TextMeshProUGUIとは別に、3D空間に直接置けるTextMeshPro(GameObject > 3D Object > Text - TextMeshPro)があります。頭上のダ メージ数字や看板はこちらです。 - UIの土台から見直すなら: テキストの置き場所であるCanvasやアンカーの基本は UIの基本の記事 を参照してください。
まとめ
- SDF技術の理解とフォントアセットの作成: TextMeshProの高品質な描画はSDF技術によるものであり、使用するフォントは必ず専用のフォントアセットとして作成・設定する必要があります。
- 日本語の文字抜けはアトラス設定で防ぐ: カスタム文字リストか
Atlas Population Mode: Dynamicで対処します。 - リッチテキストタグの活用:
<color>,<size>,<b>などのタグをC#スクリプトから動的に使用することで、表現力豊かなUIを簡単に実現できます。 - ドローコール最適化の徹底: 複数のテキストオブジェクトで同じフォントアセットとマテリアルプリセットを共有し、描画負荷を最小限に抑えることが、大規模UIでのパフォーマンス維持に不可欠です。
- 動的更新の最適化: 頻繁に更新されるテキストは毎フレームの
text書き換えを避け、フェードはalphaで行うなど、メッシュの再構築を最小限にします。
まずは日本語フォントのアセットを1つ作り、ダメージ数字のような小さな演出から手を動かしてみてください。