unity字体优化

优化背景:
字体全字符在3万个字符左右,包含了英文字母、汉字和各种语言字符、数字、数学符号、标点符号等。一般一个游戏会用到的字符在最多一千字符左右。比如一个战斗类的游戏,游戏中不会用到乒乓球这三个字,那么字体文件里面就多余了三个字符,所以需要精简。
优化方向:
1、字体文件只包含场景、预制件及语言表中用到的字符,没有多余字符。
2、对于多语言游戏,可针对每个语言制作字体文件,只下载所需语言的字体文件。
3、多字体游戏,可针对每个字体单独统计字符,文本和对应字体管理这一块。
4、聊天功能游戏,聊天文本的字体文件可包含常用8000字。
上面的优化方向是字体字符精简的终点了,没有丝毫多余,有一些劣势也需酌情考虑,例如:语言表只增加了一个字,原来的字体文件需要更新,热更的内容反而变多了,对于经常热更的游戏,一般包含8000个常用汉字和数字字母常用符号即可。
对于重度游戏,字体适度优化即可。对于一般不热更的游戏,或对包体大小比较敏感的小游戏字体优化往往有明显效果。
优化程度:
3万字符——8M
8000字符——2.2M
255字符——430KB
ReplaceTheFont.cs包含字体替换和字符统计功能。

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;
                }
            }
        }
    }
}

ReplaceTheFont脚本的字体替换和字符统计可以包含四条优化方向,稍加改造即可单独统计每个字体字符,多语言,切换语言统计即可。如果只有一种语言,一种字体,直接使用即可。
实现流程:
1、文本统计:输出两条日志,第一条日志统计了字符,第二条统计了汉字,日志前面加了一些常用标点。将两条日志的字符复制到txt文档,后续会用到该文档。
[图片]
[图片]
2、FontSubsetPack
使用FontSubsetPack生成精简后的字体文件。
FontSubsetPack插件,FontCreator字体格式转换工具百度可查。FontSubsetPack只能用ttf字体,可以先将OTF字体转换为ttf字体。
[图片]

3、替换字体文件
新字体导入unity,替换原来的字体
[图片]

猜你喜欢

转载自blog.csdn.net/qq_37619255/article/details/131848575