[unity]TextMeshPro のフォントを一括置換するエディタ拡張機能

みなさん、TextMeshPro に設定するフォント、切り替え用のスクリプトとか仕込んでますか??

私は全く用意していないので、いざフォントを変えるとなると全てのシーンやプレファブにあるフォントを手動で置き換えていました……。

変更箇所が何十か所もあったり、ヒエラルキーの奥深くに潜んでいたりしてどうしても置き換え忘れが発生します。
忘れたままフォントファイルを消すと LiberationSans に戻って日本語表示が文字化けしたり……怖くて置き換え前のフォントファイルを消せないことも。

そんな問題を解決するため、Editor 機能で一括して置き換えるプログラムを作ることにしました。

フォントファイルはどのように管理されているのか

フォントファイル(に限らず、全てのファイル)は全て GUID と呼ばれる唯一無二の ID が割り振られています。meta ファイルを覗くと確認できます。

これが unity 管理の生命線

シーンやプレファブではこの ID を元にして情報をリンクしています。

この GUID を新しいフォントの GUID に置換してしまえばいいよね? というのが今回の作戦です。

スクリプト

Assets/Editor/TMPFontGuidReplacer.cs

using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using TMPro;
using System.IO;
using System.Text.RegularExpressions;

public class TMPFontGuidReplacer : EditorWindow
{
    TMP_FontAsset srcFont;
    TMP_FontAsset dstFont;

    Material      srcMaterial;
    Material      dstMaterial;

    [MenuItem ("Tools/TextMeshPro Guid Replacer")]
    static void Exec()
    {
        var window = ScriptableObject.CreateInstance<TMPFontGuidReplacer>();
        
        window.Show();
    }

    void OnGUI()
    {
        GUILayout.Space(10);

        GUILayout.Label($"Assets/ 下の Scene、Prefab に存在する GUID を全て置き換えます");

        GUILayout.Space(30);

        GUILayout.Label($"Replace Font:", EditorStyles.boldLabel);
        EditorGUILayout.BeginHorizontal();
        srcFont = (TMP_FontAsset)EditorGUILayout.ObjectField(srcFont, typeof(TMP_FontAsset), true);
        GUILayout.Label($"→", GUILayout.Width(50));
        dstFont = (TMP_FontAsset)EditorGUILayout.ObjectField(dstFont, typeof(TMP_FontAsset), true);
        EditorGUILayout.EndHorizontal();
        GUILayout.Space(10);

        GUILayout.Label($"Replace Font Material:", EditorStyles.boldLabel);
        EditorGUILayout.BeginHorizontal();
        srcMaterial = (Material)EditorGUILayout.ObjectField(srcMaterial, typeof(Material), true);
        GUILayout.Label($"→", GUILayout.Width(50));
        dstMaterial = (Material)EditorGUILayout.ObjectField(dstMaterial, typeof(Material), true);
        EditorGUILayout.EndHorizontal();

        GUILayout.Space(30);

        EditorGUILayout.BeginHorizontal();
        if (GUILayout.Button("確認"))
        {
            replaceGuid(true);
        }
        if (GUILayout.Button("実行"))
        {
            replaceGuid(false);
        }
        EditorGUILayout.EndHorizontal();

    }

    void replaceGuid(bool isCheck)
    {
        var srcFontId = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(srcFont));
        var dstFontId = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(dstFont));

        var srcMaterialId = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(srcMaterial));
        var dstMaterialId = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(dstMaterial));

        List<string>    fileList = new List<string>();

        string[] filterPatterns =
        {
            "*.unity",
            "*.prefab",
        };

        foreach (var pattern in filterPatterns)
        {
            string[] files = Directory.GetFiles(Application.dataPath, pattern, SearchOption.AllDirectories);
            fileList.AddRange(files);
        }

        foreach (var file in fileList)
        {
            string text = File.ReadAllText(file);

            bool replace = false;

            // font
            if (string.IsNullOrEmpty(srcFontId) == false &&
                text.IndexOf(srcFontId) >= 0)
            {
                if (string.IsNullOrEmpty(srcMaterialId) == true)
                {
                    text = Regex.Replace(text, srcFontId, dstFontId);
                    replace = true;
                }
                else
                {
                    if (text.IndexOf(srcMaterialId) >= 0)
                    {
                        text = Regex.Replace(text, srcFontId, dstFontId);
                        text = Regex.Replace(text, srcMaterialId, dstMaterialId);
                        replace = true;
                    }
                }

            }

            if (replace == true)
            {
                if (isCheck == false)
                {
                    Debug.Log($"Replace: {Path.GetFileName(file)}");
                    File.WriteAllText(file, text);
                }
                else
                {
                    Debug.Log($"Replace Check: {Path.GetFileName(file)}");
                }
            }
        }

        if (isCheck == false)
        {
            AssetDatabase.Refresh();
        }
    }
}

使い方

左に置き換え前のフォント、右に置き換えたいフォントをドラッグします。
確認」を押すと、あらかじめ置換されるアセットを確認することができます(ログに表示されます)。問題なければ「実行」して、新しいフォントに置き換えます。

フォントマテリアルをドラッグすれば、それも置き換えることができます。

Assets/ 下にある、拡張子が *.unity、*.prefab であるファイルのみ置き換えています。

危険な所業である

このコードは自己責任の下、実行してください。コードを用いたいかなる問題についても当方は保証できません。実行前には必ず現状のコードをバックアップしておくことをお勧めします。

TMP_Font を Object にするとあらゆる GUID 置換ツールに変身しますが、当然危険度は増します!
この手のツールはどうしても必要な場合のみ利用する、にとどめるのが無難です。

返信を残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA