using System.Collections.Generic; using UnityEditor; using UnityEngine; using System.IO; using TMPro; using System; using UnityEngine.TextCore.LowLevel; using TMPro.EditorUtilities; using Object = UnityEngine.Object; namespace BFEditor.Resource { public class TMPTools { #region GenTMPFontAsset - version 1.4.1 private const int FontSize = 32; private const int Padding = 4; private const GlyphRenderMode GRenderMode = GlyphRenderMode.SDFAA; private const string tmpFontsPath = "Assets/arts/fonts/tmpfonts/"; private const string subFontPath = "/tmpfont"; private const string subTTFPath = "/tmpfont"; private const string AssetCNName = "font_cn_sdf.asset"; private const string AssetDefaultName = "font_sdf.asset"; private const string AssetNumberName = "font_number_sdf.asset"; private const string AssetBattleName = "font_battle_sdf.asset"; private const string AssetThaiName = "font_thai_sdf.asset"; private const string DevStrPath = "Assets/Editor/BFResourceTools/FontTools/cn_3900.txt"; private const string CommonCNWordsPath = "Assets/Editor/BFResourceTools/FontTools/cn_1200.txt"; private const string CommonZHWordsPath = "Assets/Editor/BFResourceTools/FontTools/zh_1200.txt"; private const string commonStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789¥…()—:”“‘《》?、·!【】;’,。+-*/`-=!@#$%^&*()_+[];',.\\~{}|:<>?\" "; private const string defaultFontPath = "Assets/arts/fonts/tmpfonts/default/tmpfont/font_sdf.asset"; private class TTFInfo { public string language; public string fontFolder; public string ttfPath; public int atlasSize; public List wordsPath; public TTFInfo(string language, int atlasSize, List words) { this.language = language; fontFolder = tmpFontsPath + language + subFontPath; ttfPath = tmpFontsPath + language + subTTFPath; this.atlasSize = atlasSize; wordsPath = new List(); if (words != null) { foreach (var word in words) { var tmp = Application.dataPath + "/Developer/lua/app/config/strings/" + word; wordsPath.Add(tmp); } } } public TTFInfo(string language, string fontFolder, string ttfPath, int atlasSize, List wordsPath) { this.language = language; this.fontFolder = fontFolder; this.ttfPath = ttfPath; this.atlasSize = atlasSize; this.wordsPath = wordsPath; } } private static List ttfInfos = new List() { new TTFInfo("en", 512, new List(){"en"}), new TTFInfo("cn", 2048, new List(){"cn"}), // new TTFInfo("fr", 512, new List(){"fr"}), // new TTFInfo("de", 512, new List(){"de"}), // new TTFInfo("it", 512, new List(){"it"}), // new TTFInfo("ms", 512, new List(){"ms"}), // new TTFInfo("pt", 512, new List(){"pt"}), // new TTFInfo("es", 512, new List(){"es"}), // new TTFInfo("tr", 512, new List(){"tr"}), new TTFInfo("id", 512, new List(){"id"}), new TTFInfo("ru", 512, new List(){"ru"}), // new TTFInfo("kr", 512, new List(){"kr"}), new TTFInfo("zh", 2048, new List(){"zh"}), new TTFInfo("th", 512, new List(){"th"}), // new TTFInfo("ja", 512, new List(){"ja"}), new TTFInfo("vi", 512, new List(){"vi"}), }; #if UNITY_EDITOR_OSX private static bool ProgramerOrDesigner = true; #else private static bool ProgramerOrDesigner = false; #endif static public void GenFormalFontAsset(bool isdevelop) { Debug.Log("[bfinfo]生成TMP字体..."); GenAllTMPFontAsset(isdevelop); } static private void GenAllTMPFontAsset(bool isdevelop) { // foreach (var ttfInfo in ttfInfos) // { // var dir = new DirectoryInfo(ttfInfo.ttfPath); // var allFiles = dir.GetFiles("*", SearchOption.AllDirectories); // if (null != allFiles) // { // foreach (var ttfPath in allFiles) // { // var fullName = ttfPath.FullName; // if (fullName.ToLower().EndsWith(".ttf") || fullName.ToLower().EndsWith(".otf")) // { // var path = BF.Utils.GetAssetPathByFullPath(fullName); // var font = AssetDatabase.LoadAssetAtPath(path); // if (!font) // { // Debug.LogError("Can't find font sourcefile !"); // continue; // } // if(fullName.IndexOf("title") != -1) // { // GenTMPFontAsset(font, ttfInfo, AssetNumberName, false, false); // } // else // { // GenTMPFontAsset(font, ttfInfo, AssetDefaultName, false, false); // } // } // } // } // } // var atlasSize = isdevelop ? 4096 : 256; // 常用中文 // var defaultCNFont = AssetDatabase.LoadAssetAtPath("Assets/arts/fonts/tmpfonts/default/tmpfont/font_cn.TTF"); // GenTMPFontAsset(defaultCNFont, new TTFInfo("cn", "Assets/arts/fonts/tmpfonts/default/tmpfont", "Assets/arts/fonts/tmpfonts/default/tmpfont", 256, new List()), AssetCNName, isdevelop); // 常用 var defaultFont = AssetDatabase.LoadAssetAtPath("Assets/arts/fonts/tmpfonts/default/tmpfont/font_default.TTF"); GenTMPFontAsset(defaultFont, new TTFInfo("cn", "Assets/arts/fonts/tmpfonts/default/tmpfont", "Assets/arts/fonts/tmpfonts/default/tmpfont", 2048, new List()), AssetDefaultName, isdevelop); // 数字用 var defaultNumberFont = AssetDatabase.LoadAssetAtPath("Assets/arts/fonts/tmpfonts/default/tmpfont/font_number.TTF"); GenTMPFontAsset(defaultNumberFont, new TTFInfo("cn", "Assets/arts/fonts/tmpfonts/default/tmpfont", "Assets/arts/fonts/tmpfonts/default/tmpfont", 256, new List()), AssetNumberName, isdevelop); // 泰语 var thaiFont = AssetDatabase.LoadAssetAtPath("Assets/arts/fonts/tmpfonts/default/tmpfont/font_thai.ttf"); GenTMPFontAsset(thaiFont, new TTFInfo("cn", "Assets/arts/fonts/tmpfonts/default/tmpfont", "Assets/arts/fonts/tmpfonts/default/tmpfont", 512, new List()), AssetThaiName, isdevelop); // 战斗用 // var battleFont = AssetDatabase.LoadAssetAtPath("Assets/arts/fonts/tmpfonts/battle/font_battle.ttf"); // GenTMPFontAsset(battleFont, new TTFInfo("cn", "Assets/arts/fonts/tmpfonts/battle", "Assets/arts/fonts/tmpfonts/battle", 1024, new List()), AssetBattleName, isdevelop); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); } static private void GetWords(bool isDevelop, bool isDefaultFont, List luaFolderPaths, string language, out string words) { words = string.Empty; //是否开发模式 if (!isDevelop) { var text = LocalizationMenuTools.CatWordsFromLuaConfig(luaFolderPaths); foreach (var w in text) { if (!words.Contains(w.ToString())) words += w; } if(language.CompareTo("cn") != -1) { // 简体常用字 var textAsset = AssetDatabase.LoadAssetAtPath(CommonCNWordsPath); foreach (var w in textAsset.text) { if (!words.Contains(w.ToString())) words += w; } } else if(language.CompareTo("zh") != -1) { // 繁体常用字 var textAsset = AssetDatabase.LoadAssetAtPath(CommonZHWordsPath); foreach (var w in textAsset.text) { if (!words.Contains(w.ToString())) words += w; } } } else { // 常用字母和数字还有符号 foreach (var w in commonStr) { if (!words.Contains(w.ToString())) words += w; } var luaConfigText = LocalizationMenuTools.CatWordsFromLuaConfig(luaFolderPaths); foreach (var w in luaConfigText) { if (!words.Contains(w.ToString())) words += w; } if(isDefaultFont) { // 常用字 var textAsset = AssetDatabase.LoadAssetAtPath(DevStrPath); foreach (var w in textAsset.text) { if (!words.Contains(w.ToString())) words += w; } // 中文配置 var text = LocalizationMenuTools.CatWordsFromLuaConfig(new List(){Application.dataPath + "/Developer/lua/app/config/strings/cn"}); foreach (var w in text) { if (!words.Contains(w.ToString())) words += w; } } } } static private void GenTMPFontAsset(Font font, TTFInfo ttfInfo, string assetName, bool isdevelop) { EditorUtility.DisplayProgressBar("生成TMP字体", "正在生成中...", 0f); var assetDirPath = ttfInfo.fontFolder; var assetPath = Path.Combine(assetDirPath, assetName); var texturePath = assetPath.Replace(".asset", "_atlas.asset"); var matPath = assetPath.Replace(".asset", ".mat"); var fontAsset = AssetDatabase.LoadAssetAtPath(assetPath); if (!fontAsset) { if (!Directory.Exists(assetDirPath)) Directory.CreateDirectory(assetDirPath); var targetSize = ttfInfo.atlasSize; fontAsset = TMP_FontAsset.CreateFontAsset(font, FontSize, Padding, GRenderMode, targetSize, targetSize); AssetDatabase.CreateAsset(fontAsset, assetPath); var texture = fontAsset.atlasTextures[0]; texture.name = assetName.Replace(".asset", ".png"); if(File.Exists(texturePath)) { File.Delete(texturePath); } AssetDatabase.CreateAsset(texture, texturePath); fontAsset.atlasTextures[0] = AssetDatabase.LoadAssetAtPath(texturePath); var mat = fontAsset.material; mat.name = assetName.Replace(".asset", ".mat"); if(File.Exists(matPath)) { File.Delete(matPath); } AssetDatabase.CreateAsset(mat, matPath); fontAsset.material = AssetDatabase.LoadAssetAtPath(matPath); fontAsset.material.SetTexture(ShaderUtilities.ID_MainTex, texture); fontAsset.material.SetFloat(ShaderUtilities.ID_TextureWidth, targetSize); fontAsset.material.SetFloat(ShaderUtilities.ID_TextureHeight, targetSize); fontAsset.material.SetFloat(ShaderUtilities.ID_GradientScale, Padding + 1); fontAsset.ReadFontAssetDefinition(); } else { fontAsset.atlasPopulationMode = AtlasPopulationMode.Dynamic; //设置成动态 AddSourceFontRef(assetPath, font); UpdateFontAsset(fontAsset, font, assetDirPath, ttfInfo.atlasSize); fontAsset.ClearFontAssetData(); EditorUtility.SetDirty(fontAsset); } if (null != fontAsset) { // DealWithMetric(fontAsset); var fontssetPath = AssetDatabase.GetAssetPath(fontAsset); AssetDatabase.ImportAsset(fontssetPath); // DeleteSourceFontRef(fontssetPath); fontAsset.fallbackFontAssetTable.Clear(); if(assetName == AssetDefaultName) { var fallbackAsset = AssetDatabase.LoadAssetAtPath(Path.Combine(assetDirPath, AssetThaiName)); if (fallbackAsset != null) { fontAsset.fallbackFontAssetTable.Add(fallbackAsset); } } } EditorUtility.ClearProgressBar(); } static void DeleteSourceFontRef(string path) { var fontAsset = AssetDatabase.LoadAssetAtPath(path); if (fontAsset != null) { var assetInfo = new SerializedObject(fontAsset); var property = assetInfo.FindProperty("m_SourceFontFileGUID"); property.stringValue = null; var property2 = assetInfo.FindProperty("m_SourceFontFile_EditorRef"); property2.objectReferenceValue = null; var property3 = assetInfo.FindProperty("m_SourceFontFile"); property3.objectReferenceValue = null; assetInfo.ApplyModifiedProperties(); EditorUtility.SetDirty(fontAsset); } } static void AddSourceFontRef(string path, Font font) { var fontAsset = AssetDatabase.LoadAssetAtPath(path); if (fontAsset != null) { string guid; long localID; UnityEditor.AssetDatabase.TryGetGUIDAndLocalFileIdentifier(font, out guid, out localID); var assetInfo = new SerializedObject(fontAsset); var property = assetInfo.FindProperty("m_SourceFontFileGUID"); property.stringValue = guid; var property2 = assetInfo.FindProperty("m_SourceFontFile_EditorRef"); property2.objectReferenceValue = font; var property3 = assetInfo.FindProperty("m_SourceFontFile"); property3.objectReferenceValue = font; assetInfo.ApplyModifiedProperties(); } } static private void UpdateFontAsset(TMP_FontAsset fontAsset, Font font, string assetDirPath, int tarAtlasSize) { var targetSize = tarAtlasSize; var oldFontSize = fontAsset.faceInfo.pointSize; if (oldFontSize != FontSize) { FontEngine.LoadFontFace(font, FontSize); var Type = fontAsset.GetType(); var f = Type.GetProperty("faceInfo"); f.SetValue(fontAsset, FontEngine.GetFaceInfo()); } var altasSize = fontAsset.atlasWidth; var altasPadding = fontAsset.atlasPadding; if (altasSize != targetSize || altasPadding != Padding) { var Type = fontAsset.GetType(); var w = Type.GetProperty("atlasWidth"); w.SetValue(fontAsset, targetSize); var h = Type.GetProperty("atlasHeight"); h.SetValue(fontAsset, targetSize); var p = Type.GetProperty("atlasPadding"); p.SetValue(fontAsset, Padding); fontAsset.material.SetFloat(ShaderUtilities.ID_TextureWidth, targetSize); fontAsset.material.SetFloat(ShaderUtilities.ID_TextureHeight, targetSize); fontAsset.material.SetFloat(ShaderUtilities.ID_GradientScale, Padding + 1); } //keep update Material[] materialPresets = TMP_EditorUtility.FindMaterialReferences(fontAsset); for (int i = 0; i < materialPresets.Length; i++) { Material mat = materialPresets[i]; var path = AssetDatabase.GetAssetPath(mat); if (!path.Contains(assetDirPath)) continue; BF.BFLog.Log("UpdateFontAsset material " + path); mat.SetFloat(ShaderUtilities.ID_TextureWidth, targetSize); mat.SetFloat(ShaderUtilities.ID_TextureHeight, targetSize); mat.SetFloat(ShaderUtilities.ID_GradientScale, Padding + 1); } var renderMode = fontAsset.atlasRenderMode; if (renderMode != GRenderMode) { var Type = fontAsset.GetType(); var r = Type.GetProperty("altasRenderMode"); r.SetValue(fontAsset, GRenderMode); } } /// /// 对于一些特定的字符进行布局调整 /// /// static private void DealWithMetric(TMP_FontAsset fontAsset) { //数字"1" if (fontAsset.characterLookupTable.ContainsKey(0x31)) { var character = fontAsset.characterLookupTable[0x31]; var metrics = character.glyph.metrics; metrics.horizontalBearingX = 4f; metrics.horizontalAdvance = 19f; character.glyph.metrics = metrics; } } #endregion #region TransforFontAssets /// /// 设置所有prefab的FontAssets /// /// /// /// static public void TransforAllFontAsset() { var filter = "Assets/prefabs/ui/"; string[] guids = AssetDatabase.FindAssets("t:prefab", new string[] { }); foreach (string guid in guids) { string assetPath = AssetDatabase.GUIDToAssetPath(guid); if (assetPath.Contains(filter)) { var obj = Object.Instantiate(AssetDatabase.LoadAssetAtPath(assetPath)); TransforFontAsset(obj, assetPath); Object.DestroyImmediate(obj); } } AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); } static private void TransforFontAsset(GameObject selectObj, string assetPath = "", bool keepHierachy = false) { if (null == selectObj) return; var fakeParent = EditorUtility.GetPrefabParent(selectObj); var parent = PrefabUtility.GetNearestPrefabInstanceRoot(selectObj); parent = parent == null ? selectObj : parent; //处理一层结构 assetPath = string.IsNullOrEmpty(assetPath) ? AssetDatabase.GetAssetPath(fakeParent) : assetPath; var childPath = string.Empty; BF.Utils.GetTransformPath(selectObj.transform, parent.transform, ref childPath); var index = childPath.IndexOf("/"); childPath = childPath.Substring(index + 1, childPath.Length - index - 1); var tmpComs = parent.GetComponentsInChildren(true); if (null != tmpComs) { var replace = false; foreach (var com in tmpComs) { string desPath = string.Empty; string srcPath = AssetDatabase.GetAssetPath(com.font); TMP_FontAsset targetFont; targetFont = AssetDatabase.LoadAssetAtPath(defaultFontPath); if (targetFont && com.font != targetFont) { var matName = com.fontSharedMaterial.name; var matPath = ""; targetFont = AssetDatabase.LoadAssetAtPath(defaultFontPath); matPath = "Assets/arts/fonts/tmpfonts/default/font/" + matName + ".mat"; var targetMat = AssetDatabase.LoadAssetAtPath(matPath); com.font = targetFont; if (targetMat) { com.fontSharedMaterial = targetMat; replace = true; } } } if (replace) { Debug.Log("Font chg, prefab asset name = " + assetPath); PrefabUtility.SaveAsPrefabAsset(parent, assetPath); if (keepHierachy) { //重新替换下 var pParent = parent.transform.parent; var pIndex = parent.transform.GetSiblingIndex(); UnityEngine.Object.DestroyImmediate(parent); var prefab = AssetDatabase.LoadAssetAtPath(assetPath); var obj = PrefabUtility.InstantiatePrefab(prefab) as GameObject; obj.transform.SetParent(pParent, false); obj.transform.SetSiblingIndex(pIndex); var go = obj.transform.Find(childPath); Selection.activeObject = go; } } } } static public void RemoveTMPUISubMesh() { var filter = "Assets/prefabs/ui/"; string[] guids = AssetDatabase.FindAssets("t:prefab", new string[] { }); foreach (string guid in guids) { string assetPath = AssetDatabase.GUIDToAssetPath(guid); if (assetPath.Contains(filter)) { var gameObject = AssetDatabase.LoadAssetAtPath(assetPath); var subMeshUIs = gameObject.GetComponentsInChildren(true); var beChecked = (null != subMeshUIs && subMeshUIs.Length > 0) ? true : false; if (beChecked) { gameObject = Object.Instantiate(gameObject); subMeshUIs = gameObject.GetComponentsInChildren(true); var len = subMeshUIs.Length; for (int i = 0; i < len; i++) { var target = subMeshUIs[i].gameObject; var tarParent = target.transform.parent; if (tarParent && tarParent.GetComponent()) { var childPath = string.Empty; BF.Utils.GetTransformPath(target.transform, gameObject.transform, ref childPath); BF.BFLog.Log("检查到字体图集没有包含!!!!!---------- {0}, 来源{1}", tarParent.GetComponent().text, childPath); tarParent.GetComponent().text = ""; } Object.DestroyImmediate(target); } var parent = PrefabUtility.GetNearestPrefabInstanceRoot(gameObject); parent = parent == null ? gameObject : parent; //处理一层结构 PrefabUtility.SaveAsPrefabAsset(parent, assetPath); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); Object.DestroyImmediate(gameObject); } } } } #endregion } }