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("\n", "").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對象,需要進行強制類型轉換。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM