Unity font optimization

Optimization background:
The total characters of the font are about 30,000 characters, including English letters, Chinese characters and characters of various languages, numbers, mathematical symbols, punctuation marks, etc. Generally, the characters used in a game are at most about 1,000 characters. For example, in a fighting game, the three characters of table tennis are not used in the game, so there are three extra characters in the font file, so it needs to be simplified.
Optimization direction:
1. The font file only contains the characters used in scenes, prefabs and language tables, and there are no redundant characters.
2. For multilingual games, you can make font files for each language, and only download the font files for the required languages.
3. For multi-font games, you can count the characters, text and corresponding fonts separately for each font.
4. For the chat function game, the font file of the chat text can contain 8000 commonly used characters.
The above optimization direction is the end point of simplification of font characters, there is nothing redundant, and there are some disadvantages that need to be considered. For example, the language table only adds one character, the original font file needs to be updated, and the content of hot updates increases.
For heavy games, fonts can be moderately optimized. For games that are generally not updated, or for small games that are sensitive to package size, font optimization often has obvious effects.
Optimization degree:
30,000 characters——8M
8,000 characters——2.2M
255 characters——430KB
ReplaceTheFont.cs contains font replacement and character statistics functions.

using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;
using UnityEngine.UI;
using System.Text;

public class ReplaceTheFont : EditorWindow
{
    
    
    private static ReplaceTheFont window = null;
    private static List<string> prefafbPathList = new List<string>();
    private static Font targetFont;
    private static Font curFont;

    [MenuItem("Tools/字体工具")]
    public static void ShowWindow()
    {
    
    
        if (window == null)
            window = GetWindow(typeof(ReplaceTheFont)) as ReplaceTheFont;
        window.titleContent = new GUIContent("字体工具");
        window.Show();
    }
    void OnGUI()
    {
    
    
        targetFont = (Font)EditorGUILayout.ObjectField("替换字体:", targetFont, typeof(Font), true);
        curFont = (Font)EditorGUILayout.ObjectField("被替换字体(不指定全部替换)", curFont, typeof(Font), true);
        if (GUILayout.Button("替换场景中Text的字体"))
        {
    
    
            //寻找Hierarchy面板下所有的Text
            var tArray = Resources.FindObjectsOfTypeAll(typeof(Text));
            for (int i = 0; i < tArray.Length; i++)
            {
    
    
                Text t = tArray[i] as Text;
                //记录对象
                Undo.RecordObject(t, t.gameObject.name);
                if (curFont != null)
                {
    
    
                    if (t.font.name == curFont.name)
                    {
    
    
                        t.font = targetFont;
                    }
                }
                else
                {
    
    
                    t.font = targetFont;
                }
                //设置已改变
                EditorUtility.SetDirty(t);
            }
            Debug.Log("完成");
        }
        if (GUILayout.Button("替换预制体中Text的字体"))
        {
    
    
            GetFiles(new DirectoryInfo(Application.dataPath), "*.prefab", ref prefafbPathList);
            for (int i = 0; i < prefafbPathList.Count; i++)
            {
    
    
                GameObject gameObj = AssetDatabase.LoadAssetAtPath<GameObject>(prefafbPathList[i]);

                Text[] t = gameObj.GetComponentsInChildren<Text>();
                if (t != null)
                {
    
    
                    foreach (Object item in t)
                    {
    
    
                        Text text = (Text)item;
                        //记录对象
                        Undo.RecordObject(text, text.gameObject.name);
                        if (curFont != null)
                        {
    
    
                            if (text.font.name == curFont.name)
                            {
    
    
                                text.font = targetFont;
                            }
                        }
                        else
                        {
    
    
                            text.font = targetFont;
                        }
                        //设置已改变
                        EditorUtility.SetDirty(item);
                    }
                }
            }
            AssetDatabase.SaveAssets();
            Debug.Log("完成");
        }
    }

    /// <summary>
    /// 获得Asset目录下所有预制体对象
    /// </summary>
    /// <param name="directory"></param>
    /// <param name="pattern"></param>
    /// <param name="fileList"></param>
    public static void GetFiles(DirectoryInfo directory, string pattern, ref List<string> fileList)
    {
    
    
        if (directory != null && directory.Exists && !string.IsNullOrEmpty(pattern))
        {
    
    
            try
            {
    
    
                foreach (FileInfo info in directory.GetFiles(pattern))
                {
    
    
                    string path = info.FullName.ToString();
                    fileList.Add(path.Substring(path.IndexOf("Assets")));
                }
            }
            catch (System.Exception)
            {
    
    
                throw;
            }
            foreach (DirectoryInfo info in directory.GetDirectories())
            {
    
    
                GetFiles(info, pattern, ref fileList);
            }
        }
    }

    [MenuItem("Tools/文本统计")]
    public static void GetAllText()
    {
    
    
        StringBuilder SymbolText = new StringBuilder();
        StringBuilder Chanesetext = new StringBuilder(",、。.?!~ $ %? ;‥︰…﹐﹒˙?‘’“”〝〞()~〃$¥‰%℃");
        //Text数组列表
        List<Object[]> tArray = new List<Object[]>();
        //寻找Hierarchy面板下所有的Text
        tArray.Add(Resources.FindObjectsOfTypeAll(typeof(Text)));
        //寻找预制件下的所有Text
        GetFiles(new DirectoryInfo(Application.dataPath), "*.prefab", ref prefafbPathList);
        for (int i = 0; i < prefafbPathList.Count; i++)
        {
    
    
            GameObject gameObj = AssetDatabase.LoadAssetAtPath<GameObject>(prefafbPathList[i]);
            tArray.Add(gameObj.GetComponentsInChildren<Text>());
        }
        //寻找语言表下所有文字
        List<string> language = Language.GetAll();
        for (int i = 0; i < language.Count; i++)
        {
    
    
            for (int j = 0; j < language[i].Length; j++)
            {
    
    
                if ((int)language[i][j] > 127)
                {
    
    
                    //是汉字
                    Chanesetext.Append(language[i][j]);
                }
                else
                {
    
    
                    //非汉字
                    SymbolText.Append(language[i][j]);
                }
            }
        }
        //字符全部统计:分为汉字和符号
        for (int k = 0; k < tArray.Count; k++)
        {
    
    
            for (int i = 0; i < tArray[k].Length; i++)
            {
    
    
                string text = (tArray[k][i] as Text).text;
                for (int j = 0; j < text.Length; j++)
                {
    
    
                    if ((int)text[j] > 127)
                    {
    
    
                        //是汉字
                        Chanesetext.Append(text[j]);
                    }
                    else
                    {
    
    
                        //非汉字
                        SymbolText.Append(text[j]);
                    }
                }
            }
        }

        //符号去重
        duplicateRemoval(ref SymbolText);
        duplicateRemoval(ref Chanesetext);
        //可将以下日志打出的字符作为字体文件内容
        Debug.Log(SymbolText);
        Debug.Log(Chanesetext);
    }

    private static void duplicateRemoval(ref StringBuilder text)
    {
    
    
        for (int i = text.Length - 1; i > 0; i--)
        {
    
    
            for (int j = 0; j < i; j++)
            {
    
    
                if (text[j] == text[i])
                {
    
    
                    text.Remove(i, 1);
                    break;
                }
            }
        }
    }
}

The font replacement and character statistics of the ReplaceTheFont script can include four optimization directions. With a little modification, each font character can be counted separately, multi-language, and language statistics can be switched. If there is only one language and one font, just use it directly.
Implementation process:
1. Text statistics: output two logs, the first log counts characters, the second counts Chinese characters, and some common punctuation is added in front of the logs. Copy the characters of the two logs to a txt file, which will be used later.
[picture]
[picture]
2. FontSubsetPack
Use FontSubsetPack to generate streamlined font files.
FontSubsetPack plug-in, FontCreator font format conversion tool Baidu can check. FontSubsetPack can only use ttf fonts, you can convert OTF fonts to ttf fonts first.
[picture]

3. Replace the font file
Import the new font into Unity and replace the original font
[picture]

Guess you like

Origin blog.csdn.net/qq_37619255/article/details/131848575