AIゲーム制作ラボ

Unity WebGLの日本語フォント設定と軽量化:3作の実測

UnityのWebゲームを日本語化した実例から、Noto Sans JPの必要文字だけを同梱する手順を紹介。Pythonサンプル、UI Toolkit・UGUI・IMGUIの設定、文字欠けの確認と3作品の容量実測を掲載します。

Libra

公開 2026年9月21日

この記事の要点3項目

必要文字だけに絞ったフォントは39,056〜74,144バイト これは圧縮前のTTF容量で、ゲーム全体の配信容量とは別です。

残り2つの要点を読む
  • 日本語の文字列とフォント設定は両方必要。 UI Toolkit・UGUI・IMGUIの3方式で公開版を確認しました。
  • 文字対応の検査だけでは表示を保証できません。 PCとスマホ幅で、結果画面や操作説明の重なりも確認します。
目次8
検証した環境最終確認 2026年9月21日
  • Unity 6000.6.0f1 / WebGL / Windows上のChrome
  • Python 3.12.0 / fonttools 4.63.0
  • 灯路v1.2・リフレアv1.4・Codex版ブロック崩しv1.1-ja

書いてあるのは、この環境で実際に試した結果です。ツールの仕様や料金は変わるので、重要な判断の前には公式の情報も確認してください。

日本語を表示するために、フォント全体をそのまま積む必要はありませんでした。 Codexが作った3作品では、画面で使う文字を集め、Noto Sans JPを39,056〜74,144バイトのTTFに絞って同梱しました。

きっかけは、私が灯路を遊んで「何で表記英語なの、日本語にして」と指摘したことです。紹介ページは日本語でも、ゲームに入ると手数も操作も英語。軽量化のために残した表記を、公開後に直すことになりました。

ここでは固定文言のWebゲームを対象に、実際に使った生成スクリプトとUnity側の設定を載せます。自由入力の日本語やTextMeshProの設定は対象外です。

日本語版の灯路。手数、一手戻す、やり直す、ヒントなどの操作が日本語になっている
灯路v1.2。説明文だけでなく、面名・手数・操作・設定まで日本語に揃えた公開画面。

3作品で同梱したフォントは約39〜74KB

元にした可変フォントは9,589,900バイトでした。ウェイトを400に固定し、各ゲームのC#にある非ASCII文字と、空白を含むASCIIの32〜126を残しています。下の文字数はUnicodeコードポイントの数で、フォント内部のグリフ数とは区別しています。

2026年9月21日に生成したフォント。容量は圧縮前のTTF
ゲーム収録文字TTF容量
灯路27174,144 bytes
リフレア20152,700 bytes
ブロック崩し
(Codex版)
15639,056 bytes

これを「ゲームが9MB軽くなった」とは扱っていません。以前のゲームには元フォントを入れていなかったからです。灯路の配信4ファイルはv1.1の6,975,738バイトからv1.2の7,020,418バイトへ、44,680バイト増えました。日本語化を含む版同士の差であり、フォントだけを切り替えた比較ではありません。

KBと配信容量を分ける

この記事のKBは1,000バイトで概算しています。TTFは圧縮前、公開ゲームの数字はBrotli圧縮されたdata・wasm・frameworkとloaderの合計です。フォントのファイルサイズを、そのまま通信量の増分に足すことはできません。

サンプルを展開し、日本語フォントを生成する

日本語フォント生成サンプルZIPには、Pythonスクリプトと短いC#の文言例が入っています。Unityプロジェクト一式や元フォントは含めていません。

  1. 1

    Pythonと配布ファイルを用意

    Python 3.12.0で検証しました。ZIPを展開し、README.mdがあるフォルダで以下のコマンドを実行します。
  2. 2

    原本とOFLを同じ場所へ

    Google FontsのNoto Sans JPからNotoSansJP[wght].ttfとOFL.txtを取得。font-sourceフォルダを作り、TTFだけNotoSansJP.ttfへリネームします。
  3. 3

    生成後に報告JSONを見る

    Sample/Assets/ResourcesへTTF、OFL、info.jsonが出力されます。missingGlyphsが空配列か確認します。

フォント原本はGoogle Fontsの配布フォルダから取得します。サンプルで使う配置は次のとおりです。

README.md
subset-game-font.py
font-source/
  NotoSansJP.ttf
  OFL.txt
Sample/Assets/JapaneseUiText.cs
python -m pip install fonttools==4.63.0
python subset-game-font.py font-source/NotoSansJP.ttf Sample

ZIPを別フォルダへ展開して同じスクリプトを実行した結果です。短い文言例のため、3作品より収録文字数は少なくなっています。

配布サンプルの実行結果(JSONから抜粋)成功
"glyphCount": 116,
"bytes": 24988,
"missingGlyphs": [],
"fonttools": "4.63.0"

JSONのglyphCountというキーは、このスクリプトでは収録対象のコードポイント数を保存しています。

自分のゲームへ適用するときは、最後のSampleを、AssetsがあるUnityプロジェクトのパスに置き換えます。同名の生成済みファイルは上書きされます。空白のあるパスは引用符で囲みます。

生成されるのはGameJapanese.ttfGameJapanese-OFL.txtGameJapanese-info.jsonの3ファイル。JSONには元ファイルのSHA256、収録文字数、TTF容量、欠落文字を残しています。今回使った原本のSHA256は次の値でした。

c2f3b4d463500a2ddcd3849cded1fceeb9fd6d1c32e6cbecd568453ba50fc68f

配布元が更新されれば同じ容量になるとは限りません。自分の結果では、自分が取得した原本のハッシュと生成条件を保存します。OFL本文を同梱し、改変したフォントのファミリー名はGameJapaneseへ変更しました。元の著作権情報は残しています。

抽出するのはC#内の文字。外部データまでは拾わない

今回の3作は文言をC#に書いていたため、抽出元をAssets配下のC#に揃えました。文字列リテラルだけを解析せず、コメント中の日本語も含めています。最小容量を追い切る方法ではありませんが、取りこぼしを減らしやすい範囲でした。

text = ''.join(p.read_text(encoding='utf-8-sig') for p in sources)
required = set(range(32, 127)) | {ord(c) for c in text if ord(c) > 127}
missing = required - set(font.getBestCmap())
if missing:
    raise ValueError('Missing source glyphs: ' + repr(
        ''.join(chr(c) for c in sorted(missing))))

これは配布スクリプトの抜粋です。全文ではウェイト固定、サブセット生成、改名、保存後の文字対応の再照合まで行います。getBestCmap()で確認するのは文字に対応するグリフがあるかで、Unityで正しく描けるかの検査ではありません。

⚠️ この抽出方法だけでは拾えない文字

JSON・CSV・UXML、サーバーから受信する文言、プレイヤーが入力する名前は対象外です。C#にUnicodeエスケープで書いた文字も、その表記から実際の文字へ復元していません。該当するゲームは抽出元を追加する必要があります。文言を変えたら再生成し、収録されていない文字をそのまま公開しないようにします。

Unity側はUI方式ごとにフォントの設定先が違う

生成したTTFをAssets/Resources/GameJapanese.ttfに置き、Unityにインポートさせます。.metaもゲームのリポジトリへ保存しました。ファイルを置くだけで終わらず、文字を描く側へ渡します。

以下は3作の設定箇所を整理した組み込み用の断片です。textは既存のUGUI Text、rootはUIDocumentのrootVisualElementを指します。単独で起動するMonoBehaviourではありません。

// 共通。using UnityEngine;
var font = Resources.Load<Font>("GameJapanese");
if (font == null)
    throw new System.InvalidOperationException("日本語フォントが見つかりません");

// UGUI(Codex版ブロック崩し)
// text: UnityEngine.UI.Text
text.font = font;

// UI Toolkit(リフレア)
// using UnityEngine.UIElements;
root.style.unityFontDefinition = FontDefinition.FromFont(font);

// IMGUI(灯路)。GUI.skinを使うのでOnGUI内で作成する。
var labelStyle = new GUIStyle(GUI.skin.label) { font = font };
GUI.Label(new Rect(20, 20, 300, 40), "灯台へ光を届ける", labelStyle);

IMGUIの実装ではスタイルを毎回作らず、初回に作って使い回しています。UI Toolkitではルートへ設定した後、子要素の表示も公開画面で確認しました。個別のスタイルが別フォントを指定していれば、その設定も確認が必要です。

TextMeshProのTextとUGUIのTextは別です。 今回の3方式のコードを、TextMeshProの設定手順として流用しないでください。

文字欠けだけでなく、全画面の収まりを見る

3作の生成時に、要求した文字が元フォントと生成後のフォントに存在することを確認しました。さらに公開Web版をPC幅と375px幅で操作し、文字と配置を見ています。スマホ幅はWindows上のChromeでの検証で、スマホ実機ではありません。

公開画面で確認したこと

灯路:全12面の面名・説明、ヒント、結果、設定、保存の通知
リフレア:開始説明、吸収・停止・音、ボス撃破後の結果と再挑戦
Codex版ブロック崩し:開始、得点・残機、クリア、終了、再挑戦
文字が存在しても、ボタン外へはみ出す、背景やゲーム要素に重なる場合がある
01

説明を日本語にしたら、パドルと重なった

症状
ブロック崩しの発射案内が、発射前のパドル付近に重なっていました。
原因
文字の収録検査では、画面内で他の要素と重なることまでは分かりません。
対処
説明の表示位置を上げて再ビルドし、公開版のPC・スマホ幅で確認しました。
02

エラー0でも、ゲームが始まっていなかった

症状
ブラウザ検査の開始クリックが無視され、結果として撮った画像もタイトルのままでした。
原因
ローダーの完了後にもUnityのスプラッシュが残り、その間にクリックしていました。
対処
開始前の待機を見直し、実際に開始・クリア・結果へ移った画像を確認しました。コンソールのエラー0だけでは合格にしません。

ゲーム本体のUnity検査は灯路84件、リフレア34件、Codex版ブロック崩し14件が成功しました。ただし、これらはフォント専用の検査件数ではありません。ブロック崩しのクリア・失敗画面は既存メソッドで状態を作ったUI検査で、人が全ステージを遊んだ結果とも分けています。

日本語が出ないときは、生成・参照・表示を順に調べる

症状今回の手順で確認する場所
生成前に止まるPythonからfontToolsをimportできるか、原本の隣にOFL.txtがあるか、Assets内にC#があるか。
文字不足で停止要求文字が原本にあるか。絵文字などを文字として使う場合も確認する。
読み込みがnullResources内のファイル名と読み込み名を照合。読み込み名には.ttfを付けない。
一部の文字だけ消える文言追加後の再生成忘れ、外部ファイル由来の文字、別のフォント指定を調べる。
読めるが重なる文字サイズ、ボタン幅、画面比率と配置。フォント容量の問題とは分けて直す。

私はゲーム内の英語を、紹介ページの日本語だけで補えるとは考えないことにしました。今後は初版から日本語UIを作り、必要文字の生成と公開Web版の表示確認を制作手順へ入れています。

よくある質問

よくある質問

日本語フォントを入れると必ず数MB増えますか?

固定文言に絞った今回の3作では、生成TTFは約39〜74KBでした。圧縮済みゲーム全体の増分とは別の数字です。収録文字数やフォント、ビルド条件で変わります。

TextMeshProでもこのコードを使えますか?

掲載している代入先はUI Toolkit・UGUIのText・IMGUIです。TextMeshPro用のフォントアセット生成と設定は今回検証していません。

プレイヤー名を自由に入力するゲームにも使えますか?

この抽出方法だけでは足りません。C#に存在しない名前や外部データの文字は収録されません。固定文言に使う方法として掲載しています。

出典

出典

  1. 01fontTools subsetfontTools指定文字からサブセットを作る機能と、欠落文字の扱いを確認。
  2. 02Noto Sans JPのOFL.txtGoogle Fonts今回取得したフォントの同梱ライセンス。改変後はGameJapaneseへ改名し、本文を保存。
  3. 03FontDefinition.FromFontUnityFontからUI ToolkitのFontDefinitionを作るAPIを確認。実動作はUnity 6000.6.0f1で検証。

この記事は役に立ちましたか?

関連記事