zoukankan      html  css  js  c++  java
  • Unity TextMeshPro 一键生成工具

    本文参考了这片博客文章,在此基础上进行优化和改进:

    https://blog.csdn.net/akof1314/article/details/80868869

    先截张效果图:

    TextMeshPro在之前的博客中有介绍:

    https://www.cnblogs.com/koshio0219/p/11643268.html

    思来想去,这东西还是有些使用不方便的地方,问题的根本还是在于中文字库太多,虽然缩减为7000简体字库或3500简体字库可以解决问题。

    但无论怎么说,游戏中大量的字其实是没有用到的,这势必会造成资源浪费。

    于是,接下来的想法也就应运而生——为什不能找到游戏中所有用到的字,只将这些字渲染进纹理图呢,有新增的字就更新下字库和纹理图就好了。

    这也就是上面这个工具诞生的最主要原因,它主要为了实现:

    1.批量查找游戏中Canvas或其他GameObject上的文字内容

    2.扫描查找指定路径下的配置文件中文本内容

    3.将这些文本去除重复字符后保存到一个固定的输出路径

    4.根据输出的游戏文本内容按照想要生成的TMP字体类型批量一键生产和更新

    5.随时批量修改Canvas上的字体资源

    下面是一些相对具体的思路:

    查找Canvas中的文字资源很简单,只需要遍历所有的对应组件上的内容就行了:

    1 string newText = "";
    2 foreach (var targetCanvas in targetCanvasList)
    3 {
    4     TextMeshProUGUI[] textMeshProUGUIs = targetCanvas.GetComponentsInChildren<TextMeshProUGUI>(true);
    5     foreach (var item in textMeshProUGUIs)
    6     {
    7         newText += item.text;
    8     }
    9}

    对于配置文件,需要在指定的文件夹路径中查找:

     1 private void FindTextAssets(string textAssetPath)
     2 {
     3     textAssetList.Clear();
     4     if (Directory.Exists(textAssetPath))
     5     {
     6         DirectoryInfo info = new DirectoryInfo(textAssetPath);
     7         FileInfo[] files = info.GetFiles("*", SearchOption.AllDirectories);
     8 
     9         for (int i = 0; i < files.Length; i++)
    10         {
    11             //去除meat文件
    12             if (!files[i].Name.EndsWith(".csv.meat"))
    13             {
    14                 var str = File.ReadAllText(files[i].ToString());
    15                 Debug.Log(str);
    16                 textAssetList.Add(str);
    17             }
    18         }
    19     }
    20 }

    对于得到的文字,每次更新时进行字符去重,去空格,去换行:

    1 private string StrCutRepeat(string oriStr)
    2 {
    3     return string.Join("", oriStr.ToArray().Distinct().ToArray()).Replace("
    ", "").Replace(" ", "");
    4 }

    上面的需要用到以下命名空间:

    using System.IO;
    using System.Linq;

    每次更新完文本内容需要刷新资源:

    AssetDatabase.Refresh();

    清理文本资源:

    1 private void ClearTextAsset(string path)
    2 {
    3     if (File.Exists(path))
    4     {
    5         File.WriteAllText(path, "X");
    6         AssetDatabase.Refresh();
    7     }
    8 }

    单个TMP字体纹理的生成功能在插件中已经有了,这里只需要实现选择控制和按顺序批量生产就可以了。

    在Updata()中检测上一个字体资源的生成进度,按百分比显示,当生成完成时循环生成下一个即可:

     1 private void MyUpdate()
     2 {
     3     if (m_IsRepaintNeeded)
     4     {
     5         m_IsRepaintNeeded = false;
     6         Repaint();
     7     }
     8 
     9     // 第一步创建字体渲染数组
    10     if (m_IsProcessing)
    11     {
    12         m_AtlasGenerationProgress = FontEngine.generationProgress;
    13         m_FontAssetInfos[m_CurGenerateIndex].genPercent = m_AtlasGenerationProgress * 100;
    14 
    15         m_IsRepaintNeeded = true;
    16     }
    17 
    18     // 是否生成完
    19     if (m_IsRenderingDone)
    20     {
    21         m_IsProcessing = false;
    22         m_IsRenderingDone = false;
    23 
    24         if (m_IsGenerationCancelled == false)
    25         {
    26             // 第二步输出渲染结果
    27             UpdateRenderFeedbackWindow();
    28             // 第三步将渲染数组填充到纹理贴图(注意,贴图共享不删除)
    29             CreateFontTexture();
    30             foreach (var asset in m_FontAssetInfos[m_CurGenerateIndex].assets)
    31             {
    32                 //保存信息到字体资产
    33                 Save_SDF_FontAsset(asset);
    34             }
    35             // 最后置空
    36             m_FontAtlasTexture = null;
    37         }
    38         Repaint();
    39         //开始循环生成下一个资源
    40         GenerateNext();
    41     }
    42     else if (m_LoadFontFaceInMainThread == 1)
    43     {
    44         FontEngineError errorCode = FontEngine.LoadFontFace(m_LoadFontFaceInMainThreadFontPath);
    45         m_LoadFontFaceInMainThread = errorCode == FontEngineError.Success ? 2 : 3;
    46     }
    47 }
     1 private void GenerateNext()
     2 {
     3     m_CurGenerateIndex++;
     4     //判断是否所有资源序列已经执行完
     5     if (m_CurGenerateIndex >= m_FontAssetInfos.Count)
     6     {
     7         EditorUtility.DisplayDialog("提示", "生成字库资产成功!", "OK");
     8         return;
     9     }
    10 
    11     //判断资源信息的生成开关是否已经开启
    12     var info = m_FontAssetInfos[m_CurGenerateIndex];
    13     if (!info.toggle)
    14     {
    15         GenerateNext();
    16         return;
    17     }
    18 
    19     //得到资源路径下的文件
    20     m_SourceFontFile = AssetDatabase.LoadAssetAtPath<Font>(info.fontPath);
    21     //具体生成流程
    22     GenerateFontAtlasButton();
    23 }

    批量赋值字体资源:

     1 private void AttachFontAsset()
     2 {
     3     foreach (var targetCanvas in attachCanvasList)
     4     {
     5         TextMeshProUGUI[] textMeshProUGUIs = targetCanvas.GetComponentsInChildren<TextMeshProUGUI>(true);
     6         foreach (var item in textMeshProUGUIs)
     7         {
     8             item.font = tmpFontAsset;
     9         }
    10     }
    11 }

    Editor窗口显示部分:

     大部分的Editor显示功能都是非常基础的,这里记录下几个不太常用的功能的实现:

    1.显示需要序列化的属性,例如要序列化List:

    先定义两个属性,一个是需要序列化的属性,另一个是该序列化属性对应的对象,如下:

    [SerializeField]
    List<GameObject> targetCanvasList = new List<GameObject>();
    
    SerializedProperty _canvasPropertyt;

    在OnEnable()中进行序列化对象的初始化和对应属性查找赋值:

    1 public void OnEnable()
    2 {
    3     _serializedObject = new SerializedObject(this);
    4     _canvasPropertyt = _serializedObject.FindProperty("targetCanvasList");
    5 }

    在绘制函数中对序列化对象进行修改更新的检测,并随时提交修改,例如:

     1 public void OnGUI()
     2 {
     3     _serializedObject.Update();
     4 
     5     EditorGUI.BeginChangeCheck();
     6 
     7     EditorGUILayout.PropertyField(_canvasPropertyt, true);
     8 
     9     if (EditorGUI.EndChangeCheck())
    10         _serializedObject.ApplyModifiedProperties();
    11 }

    注意在函数EditorGUILayout.PropertyField中第二个参数一定要赋值为true,不然序列化属性的子对象无法显示。

    2.点击控制标题——折叠式开关的开启与关闭:

    1 public void OnGUI()
    2 {
    3     bShowFontWorld = EditorGUILayout.Foldout(bShowFontWorld, "Font Word", true, EditorStyles.foldoutPreDrop);
    4 
    5     if (bShowFontWorld)
    6     {
    7         //折叠开关开启时需要显示的内容   
    8     }
    9 }

    函数EditorGUILayout.Foldout会自动在显示内容左侧创建小三角Icon,四个参数意义分别是:

    1.控制点击时是否显示折叠的内容,为true时显示折叠内容

    2.具体的显示内容标题

    3.控制开关的检测区域是否包含标题内容区域本身,还是只包含开关Icon部分,为true时全部包含

    4.风格设置,这里的风格最好选择带有foldout类型的,不然无法自动创建小三角Icon

    函数返回当前用户的操作——是否开启折叠内容。

    3.Object拾取器:

    tmpFontAsset = (TMP_FontAsset)EditorGUILayout.ObjectField("Font Asset", tmpFontAsset, typeof(TMP_FontAsset),false);

    最后一个参数为是否允许拾取场景中的物体,返回的是一个Object对象,需要进行强制类型转换。

  • 相关阅读:
    HDU 1850 Being a Good Boy in Spring Festival
    UESTC 1080 空心矩阵
    HDU 2491 Priest John's Busiest Day
    UVALive 6181
    ZOJ 2674 Strange Limit
    UVA 12532 Interval Product
    UESTC 1237 质因子分解
    UESTC 1014 Shot
    xe5 android listbox的 TMetropolisUIListBoxItem
    xe5 android tts(Text To Speech)
  • 原文地址:https://www.cnblogs.com/koshio0219/p/11673466.html
Copyright © 2011-2022 走看看