原理:
1. 掃描項目中用到的字符集;
2. 把字體文件裁剪掉沒用到的字符,僅保留項目中用到的字符;
3. 生成裁剪后的字體文件;
工具功能設計:
1. 支持通過拖拽字體文件或文件夾批量選擇需要裁剪的字體文件。
2. 掃描工程中使用到的字符集:主要是獲取prefab中Text、TextMeshPro的文本,配置表和數(shù)據(jù)表中的文本,多語言表的文本以及代碼中的字符串。
3. 支持設置基礎字符集文件:把需要強制保留的常用的字符集放進文本文件作為基礎字符集,可在編輯器界面由用戶選擇自定義基礎字符集文件。
4. 把掃描出的字符集和基礎字符集合并,生成裁剪后的字體文件。
功能實現(xiàn):
1. 字體選擇功能參考工具集主界面邏輯:【Unity編輯器擴展】包體優(yōu)化神器,圖片壓縮,批量生成圖集/圖集變體,動畫壓縮_unity 圖片壓縮_TopGames的博客-CSDN博客
2. 掃描項目中使用過的字符集,并保存到文件:
?分別掃描prefab、數(shù)據(jù)表、配置表、多語言表、代碼中使用的字符集。
private void ScanProjectCharSets()
{
if (string.IsNullOrWhiteSpace(EditorToolSettings.Instance.FontCroppingCharSetsOutput) || !Directory.Exists(EditorToolSettings.Instance.FontCroppingCharSetsOutput))
{
GF.LogWarning("跳過掃描字符集: 字符輸出目錄為空或目錄不存在");
return;
}
StringBuilder strBuilder = new StringBuilder();
//掃描prefab中文本組件用到的字符
var prefabGuids = AssetDatabase.FindAssets("t:prefab", new string[] { ConstEditor.PrefabsPath });
foreach (var guid in prefabGuids)
{
var assetPath = AssetDatabase.GUIDToAssetPath(guid);
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(assetPath);
var allTexts = prefab.GetComponentsInChildren<UnityEngine.UI.Text>(true);
var allTmpTexts = prefab.GetComponentsInChildren<TMPro.TMP_Text>(true);
foreach (var item in allTexts)
{
if (string.IsNullOrEmpty(item.text)) continue;
strBuilder.Append(item.text);
}
foreach (var item in allTmpTexts)
{
if (string.IsNullOrEmpty(item.text)) continue;
strBuilder.Append(item.text);
}
}
//掃描配置表,數(shù)據(jù)表,多語言文件中的字符
var txtFiles = new List<string>();
var configs = Directory.GetFiles(ConstEditor.GameConfigPath, "*.txt");
if (configs.Length > 0) txtFiles.AddRange(configs);
var dataTables = Directory.GetFiles(ConstEditor.DataTablePath, "*.txt");
if (dataTables.Length > 0) txtFiles.AddRange(dataTables);
var languages = Directory.GetFiles(ConstEditor.LanguagePath, "*.json");
if (languages.Length > 0) txtFiles.AddRange(languages);
foreach (var item in txtFiles)
{
var text = File.ReadAllText(item, Encoding.UTF8);
if (string.IsNullOrEmpty(text)) continue;
strBuilder.Append(text);
}
//掃描代碼中使用的字符
var scriptGuids = AssetDatabase.FindAssets("t:script", new string[] { Path.GetDirectoryName(ConstEditor.HotfixAssembly), Path.GetDirectoryName(ConstEditor.BuiltinAssembly) });
string charsetsPattern = "\"(.*?)\"";
foreach (var item in scriptGuids)
{
var assetPath = AssetDatabase.GUIDToAssetPath(item);
if (Path.GetExtension(assetPath).ToLower() != ".cs") continue;
var codeTxt = File.ReadAllText(assetPath);
MatchCollection matches = Regex.Matches(codeTxt, charsetsPattern);
foreach (Match match in matches)
{
string text = match.Groups[1].Value;
if (string.IsNullOrEmpty(text)) continue;
strBuilder.Append(text);
}
}
var resultFile = UtilityBuiltin.ResPath.GetCombinePath(EditorToolSettings.Instance.FontCroppingCharSetsOutput, CharSetsFile);
var result = strBuilder.ToString();
var unicodeCharSets = String2UnicodeCharSets(result);
unicodeCharSets = unicodeCharSets.Distinct().ToArray();
result = UnicodeCharSets2String(unicodeCharSets);
File.WriteAllText(resultFile, result, Encoding.UTF8);
GF.LogInfo($"掃描字符集完成,共[{unicodeCharSets.Length}]個字符. 已保存到字符集文件:{resultFile}");
}
需要注意的是,ttf是Unicode編碼方式,需要把字符串轉(zhuǎn)為Unicode編碼,一個中文占2個字節(jié)。把字符串轉(zhuǎn)換為Unicode編碼保存在uint[]數(shù)組中,并且還需要進行去重。
字符串轉(zhuǎn)換為Unicode編碼:
/// <summary>
/// 把字符串轉(zhuǎn)換為
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
private static uint[] String2UnicodeCharSets(string str)
{
var bytesDt = System.Text.Encoding.Unicode.GetBytes(str);
uint[] charSets = new uint[bytesDt.Length / System.Text.UnicodeEncoding.CharSize];
for (int idx = 0, i = 0; i < bytesDt.Length; i += System.Text.UnicodeEncoding.CharSize)
{
charSets[idx++] = BitConverter.ToUInt16(bytesDt, i);
}
return charSets;
}
把Unicode編碼轉(zhuǎn)換為字符串:
/// <summary>
/// 把Unicode數(shù)值轉(zhuǎn)換為字符串
/// </summary>
/// <param name="charsets"></param>
/// <returns></returns>
private static string UnicodeCharSets2String(uint[] charsets)
{
StringBuilder strBuilder = new StringBuilder();
for (int i = 0; i < charsets.Length; i++)
{
var unicodeChar = char.ConvertFromUtf32((int)charsets[i]);
strBuilder.Append(unicodeChar);
}
return strBuilder.ToString();
}
3. 使用Aspose.Font庫裁剪字體文件:
Aspose.Font是一個支持對字體文件讀取、創(chuàng)建、合并、格式轉(zhuǎn)換等操作的.net庫,下載后把dll導入Unity即可調(diào)用。
/// <summary>
/// 裁剪字體
/// </summary>
/// <param name="ttf"></param>
/// <param name="unicodeCharSets"></param>
/// <returns></returns>
public static bool CroppingFont(string ttf, uint[] unicodeCharSets)
{
if (Path.GetExtension(ttf).ToLower() != ".ttf")
{
Debug.LogWarning($"生成裁剪字體[{ttf}]失敗:只支持裁剪ttf格式字體");
return false;
}
try
{
var font = Aspose.Font.Font.Open(Aspose.Font.FontType.TTF, ttf) as TtfFont;
var merger = HelpersFactory.GetFontCharactersMerger(font, font);
var charsets = unicodeCharSets.Distinct().ToArray();
var newFont = merger.MergeFonts(charsets, new uint[0], font.FontName);
var newTtf = GenerateNewFontFileName(ttf);
newFont.Save(newTtf);
AssetDatabase.Refresh();
return true;
}
catch (Exception e)
{
Debug.LogWarning($"生成裁剪字體[{ttf}]失敗:{e.Message}");
return false;
}
}
字體文件裁剪前后對比:
工程中用到字符個數(shù)為485個,裁剪后76KB,完整字體9525KB:
?工具代碼:
using Aspose.Font.Ttf;
using Aspose.Font.TtfHelpers;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEngine;
namespace UGF.EditorTools
{
[EditorToolMenu("字體裁剪", typeof(CompressToolEditor), 6)]
public class FontMinifyPanel : CompressToolSubPanel
{
/// <summary>
/// 掃描出的字符保存到文件
/// </summary>
const string CharSetsFile = "CharSets_ScanFromProject.txt";
public override string AssetSelectorTypeFilter => "t:font t:folder";
public override string DragAreaTips => "拖拽添加字體文件/文件夾";
public override string ReadmeText => "自動掃描項目中使用的字符,裁剪掉字體資源中未使用的字符";
protected override Type[] SupportAssetTypes => new Type[] { typeof(UnityEngine.Font) };
public override void DrawBottomButtonsPanel()
{
EditorGUILayout.BeginHorizontal("box");
{
var layoutHeight = GUILayout.Height(30);
if (GUILayout.Button("掃描字符", layoutHeight))
{
ScanProjectCharSets();
}
if (GUILayout.Button("裁剪字體", layoutHeight))
{
GenerateMinifyFont();
}
if (GUILayout.Button("掃描并裁剪", layoutHeight))
{
ScanAndGenerateMinifyFont();
}
EditorGUILayout.EndHorizontal();
}
}
public override void DrawSettingsPanel()
{
EditorGUILayout.BeginVertical("box");
{
EditorGUILayout.BeginHorizontal();
{
EditorGUILayout.LabelField("掃描字符集輸出:", GUILayout.Width(100));
EditorGUILayout.LabelField(EditorToolSettings.Instance.FontCroppingCharSetsOutput, EditorStyles.selectionRect);
if (GUILayout.Button("選擇路徑", GUILayout.Width(100)))
{
EditorToolSettings.Instance.FontCroppingCharSetsOutput = EditorUtilityExtension.OpenRelativeFolderPanel("選擇字符集保存目錄", EditorToolSettings.Instance.FontCroppingCharSetsOutput);
}
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.BeginHorizontal();
{
EditorGUILayout.LabelField("基礎字符集文件:", GUILayout.Width(100));
EditorGUILayout.LabelField(EditorToolSettings.Instance.FontCroppingCharSetsFile, EditorStyles.selectionRect);
if (GUILayout.Button("選擇文件", GUILayout.Width(100)))
{
EditorToolSettings.Instance.FontCroppingCharSetsFile = EditorUtilityExtension.OpenRelativeFilePanel("選擇字符集文件", EditorToolSettings.Instance.FontCroppingCharSetsFile, "txt");
}
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.EndVertical();
}
}
private void ScanAndGenerateMinifyFont()
{
ScanProjectCharSets();
GenerateMinifyFont();
}
private void ScanProjectCharSets()
{
if (string.IsNullOrWhiteSpace(EditorToolSettings.Instance.FontCroppingCharSetsOutput) || !Directory.Exists(EditorToolSettings.Instance.FontCroppingCharSetsOutput))
{
GF.LogWarning("跳過掃描字符集: 字符輸出目錄為空或目錄不存在");
return;
}
StringBuilder strBuilder = new StringBuilder();
//掃描prefab中文本組件用到的字符
var prefabGuids = AssetDatabase.FindAssets("t:prefab", new string[] { ConstEditor.PrefabsPath });
foreach (var guid in prefabGuids)
{
var assetPath = AssetDatabase.GUIDToAssetPath(guid);
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(assetPath);
var allTexts = prefab.GetComponentsInChildren<UnityEngine.UI.Text>(true);
var allTmpTexts = prefab.GetComponentsInChildren<TMPro.TMP_Text>(true);
foreach (var item in allTexts)
{
if (string.IsNullOrEmpty(item.text)) continue;
strBuilder.Append(item.text);
}
foreach (var item in allTmpTexts)
{
if (string.IsNullOrEmpty(item.text)) continue;
strBuilder.Append(item.text);
}
}
//掃描配置表,數(shù)據(jù)表,多語言文件中的字符
var txtFiles = new List<string>();
var configs = Directory.GetFiles(ConstEditor.GameConfigPath, "*.txt");
if (configs.Length > 0) txtFiles.AddRange(configs);
var dataTables = Directory.GetFiles(ConstEditor.DataTablePath, "*.txt");
if (dataTables.Length > 0) txtFiles.AddRange(dataTables);
var languages = Directory.GetFiles(ConstEditor.LanguagePath, "*.json");
if (languages.Length > 0) txtFiles.AddRange(languages);
foreach (var item in txtFiles)
{
var text = File.ReadAllText(item, Encoding.UTF8);
if (string.IsNullOrEmpty(text)) continue;
strBuilder.Append(text);
}
//掃描代碼中使用的字符
var scriptGuids = AssetDatabase.FindAssets("t:script", new string[] { Path.GetDirectoryName(ConstEditor.HotfixAssembly), Path.GetDirectoryName(ConstEditor.BuiltinAssembly) });
string charsetsPattern = "\"(.*?)\"";
foreach (var item in scriptGuids)
{
var assetPath = AssetDatabase.GUIDToAssetPath(item);
if (Path.GetExtension(assetPath).ToLower() != ".cs") continue;
var codeTxt = File.ReadAllText(assetPath);
MatchCollection matches = Regex.Matches(codeTxt, charsetsPattern);
foreach (Match match in matches)
{
string text = match.Groups[1].Value;
if (string.IsNullOrEmpty(text)) continue;
strBuilder.Append(text);
}
}
var resultFile = UtilityBuiltin.ResPath.GetCombinePath(EditorToolSettings.Instance.FontCroppingCharSetsOutput, CharSetsFile);
var result = strBuilder.ToString();
var unicodeCharSets = String2UnicodeCharSets(result);
unicodeCharSets = unicodeCharSets.Distinct().ToArray();
result = UnicodeCharSets2String(unicodeCharSets);
File.WriteAllText(resultFile, result, Encoding.UTF8);
GF.LogInfo($"掃描字符集完成,共[{unicodeCharSets.Length}]個字符. 已保存到字符集文件:{resultFile}");
}
private void GenerateMinifyFont()
{
var fontAsssts = this.GetSelectedAssets();
if (fontAsssts.Count < 1)
{
GF.LogWarning($"請先把需要裁剪的字體資源添加到列表");
return;
}
var projRoot = Directory.GetParent(Application.dataPath).FullName;
var charSetString = GetCharSetStringFromFiles();
if (string.IsNullOrWhiteSpace(charSetString))
{
GF.LogWarning($"要裁剪的字符集為空, 請設置字符集文件或檢查字符集內(nèi)容");
return;
}
var unicodeCharSets = String2UnicodeCharSets(charSetString);
GF.LogInfo($"字符集包含字符個數(shù):{unicodeCharSets.Length}");
foreach (var asset in fontAsssts)
{
var fontFile = Path.GetFullPath(asset, projRoot);
if (CroppingFont(fontFile, unicodeCharSets))
{
GF.LogInfo($"生成裁剪字體成功:{fontFile}");
}
}
}
private string GetCharSetStringFromFiles()
{
StringBuilder strBuilder = new StringBuilder();
if (!string.IsNullOrWhiteSpace(EditorToolSettings.Instance.FontCroppingCharSetsOutput))
{
var projCharsFile = UtilityBuiltin.ResPath.GetCombinePath(EditorToolSettings.Instance.FontCroppingCharSetsOutput, CharSetsFile);
if (File.Exists(projCharsFile))
{
var str = File.ReadAllText(projCharsFile);
strBuilder.Append(str);
}
}
if (!string.IsNullOrWhiteSpace(EditorToolSettings.Instance.FontCroppingCharSetsFile) && File.Exists(EditorToolSettings.Instance.FontCroppingCharSetsFile))
{
var str = File.ReadAllText(EditorToolSettings.Instance.FontCroppingCharSetsFile);
strBuilder.Append(str);
}
return strBuilder.ToString();
}
/// <summary>
/// 裁剪字體
/// </summary>
/// <param name="ttf"></param>
/// <param name="unicodeCharSets"></param>
/// <returns></returns>
public static bool CroppingFont(string ttf, uint[] unicodeCharSets)
{
if (Path.GetExtension(ttf).ToLower() != ".ttf")
{
Debug.LogWarning($"生成裁剪字體[{ttf}]失敗:只支持裁剪ttf格式字體");
return false;
}
try
{
var font = Aspose.Font.Font.Open(Aspose.Font.FontType.TTF, ttf) as TtfFont;
var merger = HelpersFactory.GetFontCharactersMerger(font, font);
var charsets = unicodeCharSets.Distinct().ToArray();
var newFont = merger.MergeFonts(charsets, new uint[0], font.FontName);
var newTtf = GenerateNewFontFileName(ttf);
newFont.Save(newTtf);
AssetDatabase.Refresh();
return true;
}
catch (Exception e)
{
Debug.LogWarning($"生成裁剪字體[{ttf}]失敗:{e.Message}");
return false;
}
}
/// <summary>
/// 根據(jù)字符集裁剪字體
/// </summary>
/// <param name="ttf"></param>
/// <param name="charSets"></param>
public static bool CroppingFont(string ttf, string charSets)
{
var unicodeChars = String2UnicodeCharSets(charSets);
return CroppingFont(ttf, unicodeChars);
}
private static string GenerateNewFontFileName(string ttf)
{
var newFontSavePath = Path.GetFullPath(Path.GetDirectoryName(ttf), Directory.GetParent(Application.dataPath).FullName);
var newFontFileName = Path.GetFileNameWithoutExtension(ttf) + "_mini";
var newFontExt = Path.GetExtension(ttf);
var newTtf = UtilityBuiltin.ResPath.GetCombinePath(newFontSavePath, newFontFileName + newFontExt);
return newTtf;
}
/// <summary>
/// 把字符串轉(zhuǎn)換為
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
private static uint[] String2UnicodeCharSets(string str)
{
var bytesDt = System.Text.Encoding.Unicode.GetBytes(str);
uint[] charSets = new uint[bytesDt.Length / System.Text.UnicodeEncoding.CharSize];
for (int idx = 0, i = 0; i < bytesDt.Length; i += System.Text.UnicodeEncoding.CharSize)
{
charSets[idx++] = BitConverter.ToUInt16(bytesDt, i);
}
return charSets;
}
/// <summary>
/// 把Unicode數(shù)值轉(zhuǎn)換為字符串
/// </summary>
/// <param name="charsets"></param>
/// <returns></returns>
private static string UnicodeCharSets2String(uint[] charsets)
{
StringBuilder strBuilder = new StringBuilder();
for (int i = 0; i < charsets.Length; i++)
{
var unicodeChar = char.ConvertFromUtf32((int)charsets[i]);
strBuilder.Append(unicodeChar);
}
return strBuilder.ToString();
}
}
}
4. 字體批量替換工具
批處理工具,可以把裁剪過的字體一鍵應用到Text、TextMeshPro組件上。
功能很簡單,先設置需要搜索文本組件的Prefab,然后配置要替換成的字體文件。
批處理工具集主編輯器代碼基類,主要用于自動把子工具面板顯示在主編輯器的工具欄:
using GameFramework;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Unity.VisualScripting;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
namespace UGF.EditorTools
{
/// <summary>
/// 批處理操作工具
/// </summary>
public abstract class UtilityToolEditorBase : EditorToolBase
{
//public override string ToolName => "批處理工具集";
public override Vector2Int WinSize => new Vector2Int(600, 800);
GUIStyle centerLabelStyle;
ReorderableList srcScrollList;
Vector2 srcScrollPos;
private int SelectOjbWinId => this.GetType().GetHashCode();
private bool settingFoldout = true;
List<Type> subPanelsClass;
string[] subPanelTitles;
UtilitySubToolBase[] subPanels;
UtilitySubToolBase curPanel;
private int mCompressMode;
private List<UnityEngine.Object> selectList;
private void OnEnable()
{
selectList = new List<UnityEngine.Object>();
subPanelsClass = new List<Type>();
centerLabelStyle = new GUIStyle();
centerLabelStyle.alignment = TextAnchor.MiddleCenter;
centerLabelStyle.fontSize = 25;
centerLabelStyle.normal.textColor = Color.gray;
srcScrollList = new ReorderableList(selectList, typeof(UnityEngine.Object), true, true, true, true);
srcScrollList.drawHeaderCallback = DrawScrollListHeader;
srcScrollList.onAddCallback = AddItem;
srcScrollList.drawElementCallback = DrawItems;
srcScrollList.multiSelect = true;
ScanSubPanelClass();
SwitchSubPanel(0);
}
private void ScanSubPanelClass()
{
subPanelsClass.Clear();
var editorDll = Utility.Assembly.GetAssemblies().First(dll => dll.GetName().Name.CompareTo("Assembly-CSharp-Editor") == 0);
var allEditorTool = editorDll.GetTypes().Where(tp => (tp.IsSubclassOf(typeof(UtilitySubToolBase)) && tp.HasAttribute<EditorToolMenuAttribute>() && tp.GetCustomAttribute<EditorToolMenuAttribute>().OwnerType == this.GetType()));
subPanelsClass.AddRange(allEditorTool);
subPanelsClass.Sort((x, y) =>
{
int xOrder = x.GetCustomAttribute<EditorToolMenuAttribute>().MenuOrder;
int yOrder = y.GetCustomAttribute<EditorToolMenuAttribute>().MenuOrder;
return xOrder.CompareTo(yOrder);
});
subPanels = new UtilitySubToolBase[subPanelsClass.Count];
subPanelTitles = new string[subPanelsClass.Count];
for (int i = 0; i < subPanelsClass.Count; i++)
{
var toolAttr = subPanelsClass[i].GetCustomAttribute<EditorToolMenuAttribute>();
subPanelTitles[i] = toolAttr.ToolMenuPath;
}
}
private void OnDisable()
{
foreach (var panel in subPanels)
{
panel?.OnExit();
}
}
private void OnGUI()
{
if (curPanel == null) return;
EditorGUILayout.BeginVertical();
EditorGUILayout.BeginHorizontal("box");
{
EditorGUI.BeginChangeCheck();
mCompressMode = GUILayout.Toolbar(mCompressMode, subPanelTitles, GUILayout.Height(30));
if (EditorGUI.EndChangeCheck())
{
SwitchSubPanel(mCompressMode);
}
EditorGUILayout.EndHorizontal();
}
srcScrollPos = EditorGUILayout.BeginScrollView(srcScrollPos);
srcScrollList.DoLayoutList();
EditorGUILayout.EndScrollView();
DrawDropArea();
EditorGUILayout.Space(10);
if (settingFoldout = EditorGUILayout.Foldout(settingFoldout, "展開設置項:"))
{
curPanel.DrawSettingsPanel();
}
curPanel.DrawBottomButtonsPanel();
EditorGUILayout.EndVertical();
}
/// <summary>
/// 繪制拖拽添加文件區(qū)域
/// </summary>
private void DrawDropArea()
{
var dragRect = EditorGUILayout.BeginVertical("box");
{
GUILayout.FlexibleSpace();
EditorGUILayout.LabelField(curPanel.DragAreaTips, centerLabelStyle, GUILayout.MinHeight(200));
if (dragRect.Contains(UnityEngine.Event.current.mousePosition))
{
if (UnityEngine.Event.current.type == EventType.DragUpdated)
{
DragAndDrop.visualMode = DragAndDropVisualMode.Generic;
}
else if (UnityEngine.Event.current.type == EventType.DragExited)
{
if (DragAndDrop.objectReferences != null && DragAndDrop.objectReferences.Length > 0)
{
OnItemsDrop(DragAndDrop.objectReferences);
}
}
}
GUILayout.FlexibleSpace();
EditorGUILayout.EndVertical();
}
}
/// <summary>
/// 拖拽松手
/// </summary>
/// <param name="objectReferences"></param>
/// <exception cref="NotImplementedException"></exception>
private void OnItemsDrop(UnityEngine.Object[] objectReferences)
{
foreach (var item in objectReferences)
{
var itemPath = AssetDatabase.GetAssetPath(item);
if (curPanel.GetSelectedItemType(itemPath) == ItemType.NoSupport)
{
Debug.LogWarningFormat("添加失敗! 不支持的文件格式:{0}", itemPath);
continue;
}
AddItem(item);
}
}
private void AddItem(UnityEngine.Object obj)
{
if (obj == null || selectList.Contains(obj)) return;
selectList.Add(obj);
}
private void DrawItems(Rect rect, int index, bool isActive, bool isFocused)
{
var item = selectList[index];
EditorGUI.ObjectField(rect, item, typeof(UnityEngine.Object), false);
}
private void DrawScrollListHeader(Rect rect)
{
if (GUI.Button(rect, "清除列表"))
{
selectList?.Clear();
}
}
private void OnSelectAsset(UnityEngine.Object obj)
{
AddItem(obj);
}
private void AddItem(ReorderableList list)
{
if (!EditorUtilityExtension.OpenAssetSelector(typeof(UnityEngine.Object), curPanel.AssetSelectorTypeFilter, OnSelectAsset, SelectOjbWinId))
{
Debug.LogWarning("打開資源選擇界面失敗!");
}
}
private void SwitchSubPanel(int panelIdx)
{
if (subPanelsClass.Count <= 0) return;
mCompressMode = Mathf.Clamp(panelIdx, 0, subPanelsClass.Count);
this.titleContent.text = subPanelTitles[mCompressMode];
if (curPanel != null)
{
curPanel.OnExit();
}
if (subPanels[mCompressMode] != null)
{
curPanel = subPanels[mCompressMode];
}
else
{
curPanel = subPanels[mCompressMode] = Activator.CreateInstance(subPanelsClass[mCompressMode], new object[] { this }) as UtilitySubToolBase;
}
curPanel.OnEnter();
}
/// <summary>
/// 獲取當前選擇的資源文件列表
/// </summary>
/// <returns></returns>
public List<string> GetSelectedAssets()
{
return curPanel.FilterSelectedAssets(selectList);
}
}
}
批處理主編輯器:
namespace UGF.EditorTools
{
[EditorToolMenu("資源/批處理工具集", null, 4)]
public class BatchOperateToolEditor : UtilityToolEditorBase
{
public override string ToolName => "批處理工具集";
}
}
子工具面板基類:文章來源:http://www.zghlxwxcb.cn/news/detail-553614.html
using GameFramework;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
namespace UGF.EditorTools
{
public abstract class UtilitySubToolBase
{
protected UtilityToolEditorBase OwnerEditor { get; private set; }
public abstract string AssetSelectorTypeFilter { get; }//"t:sprite t:texture2d t:folder"
public abstract string DragAreaTips { get; }
protected abstract Type[] SupportAssetTypes { get; }
public UtilitySubToolBase(UtilityToolEditorBase ownerEditor)
{
OwnerEditor = ownerEditor;
}
public virtual void OnEnter() { }
public virtual void OnExit() { SaveSettings(); }
public abstract void DrawSettingsPanel();
public abstract void DrawBottomButtonsPanel();
/// <summary>
/// 通過AssetDatabase判斷是否支持, 注意如果是Assets之外的文件判斷需要重寫此方法
/// </summary>
/// <param name="assetPath"></param>
/// <returns></returns>
public virtual bool IsSupportAsset(string assetPath)
{
var assetType = AssetDatabase.GetMainAssetTypeAtPath(assetPath);
return SupportAssetTypes.Contains(assetType);
}
/// <summary>
/// 獲取當前選擇的資源文件列表
/// </summary>
/// <returns></returns>
public virtual List<string> FilterSelectedAssets(List<UnityEngine.Object> selectedObjs)
{
List<string> images = new List<string>();
foreach (var item in selectedObjs)
{
if (item == null) continue;
var assetPath = AssetDatabase.GetAssetPath(item);
var itmTp = GetSelectedItemType(assetPath);
if (itmTp == ItemType.File)
{
string imgFileName = Utility.Path.GetRegularPath(assetPath);
if (IsSupportAsset(imgFileName) && !images.Contains(imgFileName))
{
images.Add(imgFileName);
}
}
else if (itmTp == ItemType.Folder)
{
string imgFolder = AssetDatabase.GetAssetPath(item);
var assets = AssetDatabase.FindAssets(GetFindAssetsFilter(), new string[] { imgFolder });
for (int i = assets.Length - 1; i >= 0; i--)
{
assets[i] = AssetDatabase.GUIDToAssetPath(assets[i]);
}
images.AddRange(assets);
}
}
return images.Distinct().ToList();//把結(jié)果去重處理
}
protected string GetFindAssetsFilter()
{
string filter = "";
foreach (var item in SupportAssetTypes)
{
filter += $"t:{item.Name} ";
}
filter.Trim(' ');
return filter;
}
public virtual void SaveSettings()
{
if (EditorToolSettings.Instance)
{
EditorToolSettings.Save();
}
}
internal ItemType GetSelectedItemType(string assetPath)
{
if (string.IsNullOrEmpty(assetPath)) return ItemType.NoSupport;
if ((File.GetAttributes(assetPath) & FileAttributes.Directory) == FileAttributes.Directory) return ItemType.Folder;
if (IsSupportAsset(assetPath)) return ItemType.File;
return ItemType.NoSupport;
}
}
}
字體批量替換子工具:文章來源地址http://www.zghlxwxcb.cn/news/detail-553614.html
using System;
using TMPro;
using UnityEditor;
using UnityEngine;
namespace UGF.EditorTools
{
[EditorToolMenu("替換字體", typeof(BatchOperateToolEditor), 0)]
public class FontReplaceTool : UtilitySubToolBase
{
public override string AssetSelectorTypeFilter => "t:prefab t:folder";
public override string DragAreaTips => "拖拽添加Prefab文件或文件夾";
protected override Type[] SupportAssetTypes => new Type[] { typeof(GameObject) };
UnityEngine.Font textFont;
TMP_FontAsset tmpFont;
TMP_SpriteAsset tmpFontSpriteAsset;
TMP_StyleSheet tmpFontStyleSheet;
public FontReplaceTool(BatchOperateToolEditor ownerEditor) : base(ownerEditor)
{
}
public override void DrawBottomButtonsPanel()
{
if (GUILayout.Button("一鍵替換", GUILayout.Height(30)))
{
ReplaceFont();
}
}
public override void DrawSettingsPanel()
{
EditorGUILayout.BeginHorizontal("box");
{
textFont = EditorGUILayout.ObjectField("Text字體替換:", textFont, typeof(UnityEngine.Font), false) as UnityEngine.Font;
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.BeginVertical("box");
{
tmpFont = EditorGUILayout.ObjectField("TextMeshPro字體替換:", tmpFont, typeof(TMP_FontAsset), false) as TMP_FontAsset;
tmpFontSpriteAsset = EditorGUILayout.ObjectField("Sprite Asset替換:", tmpFontSpriteAsset, typeof(TMP_SpriteAsset), false) as TMP_SpriteAsset;
tmpFontStyleSheet = EditorGUILayout.ObjectField("Style Sheet替換:", tmpFontStyleSheet, typeof(TMP_StyleSheet), false) as TMP_StyleSheet;
EditorGUILayout.EndVertical();
}
}
private void ReplaceFont()
{
var prefabs = OwnerEditor.GetSelectedAssets();
if (prefabs == null || prefabs.Count < 1) return;
int taskIdx = 0;
int totalTaskCount = prefabs.Count;
bool batTmpfont = tmpFont != null || tmpFontSpriteAsset != null || tmpFontStyleSheet != null;
foreach (var item in prefabs)
{
var pfb = AssetDatabase.LoadAssetAtPath<GameObject>(item); //PrefabUtility.LoadPrefabContents(item);
if (pfb == null) continue;
EditorUtility.DisplayProgressBar($"進度({taskIdx++}/{totalTaskCount})", item, taskIdx / (float)totalTaskCount);
bool hasChanged = false;
if (textFont != null)
{
foreach (var textCom in pfb.GetComponentsInChildren<UnityEngine.UI.Text>(true))
{
textCom.font = textFont;
hasChanged = true;
}
}
if (batTmpfont)
{
foreach (var tmpTextCom in pfb.GetComponentsInChildren<TMPro.TMP_Text>(true))
{
if (tmpFont != null) tmpTextCom.font = tmpFont;
if (tmpFontSpriteAsset != null) tmpTextCom.spriteAsset = tmpFontSpriteAsset;
if (tmpFontStyleSheet != null) tmpTextCom.styleSheet = tmpFontStyleSheet;
hasChanged = true;
}
}
if (hasChanged)
{
PrefabUtility.SavePrefabAsset(pfb);
}
}
EditorUtility.ClearProgressBar();
}
}
}
到了這里,關于【Unity編輯器擴展】字庫裁剪工具, 優(yōu)化字體文件大小,批量修改文本組件字體的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!