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对象,需要进行强制类型转换。

  • 相关阅读:
    HTML语义化
    OKAY take it away `electron-builder`一直提示这个
    gitbash选中不了自己想要的选择
    vue挂载
    vue关闭eslint
    第二天-5大浏览器内核和浏览器的组成
    第一天-JavaScript简介与历史
    bootstrap模态框遇到做复制的功能失效
    对象的key【键】和分别获取数组的key【键】和值
    AngularJS教程
  • 原文地址:https://www.cnblogs.com/koshio0219/p/11673466.html
Copyright © 2011-2022 走看看