270 lines
9.6 KiB
C#
270 lines
9.6 KiB
C#
using System.IO;
|
|
using System;
|
|
using System.Reflection;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
using UnityEngine.TextCore.LowLevel;
|
|
using Object = UnityEngine.Object;
|
|
|
|
public static class XWorldUIFontInstaller
|
|
{
|
|
private const string SourceFontPath = "Assets/Game/Art/UI/Font/siyuan_CN_Bold.otf";
|
|
private const string TmpFontAssetPath = "Assets/Game/Art/UI/Font/siyuan_CN_Bold SDF.asset";
|
|
private const string TmpSettingsPath = "Assets/TextMesh Pro/Resources/TMP Settings.asset";
|
|
private static bool s_IsInstalling;
|
|
|
|
[InitializeOnLoadMethod]
|
|
private static void InstallDefaultFontOnLoad()
|
|
{
|
|
EditorApplication.delayCall += () =>
|
|
{
|
|
if (!File.Exists(SourceFontPath))
|
|
return;
|
|
|
|
InstallDefaultFont();
|
|
};
|
|
}
|
|
|
|
[MenuItem("XWorld/UI/Install Default Font", false, 702)]
|
|
public static void InstallDefaultFont()
|
|
{
|
|
if (s_IsInstalling)
|
|
return;
|
|
|
|
s_IsInstalling = true;
|
|
try
|
|
{
|
|
AssetDatabase.ImportAsset(SourceFontPath, ImportAssetOptions.ForceUpdate);
|
|
|
|
Font sourceFont = AssetDatabase.LoadAssetAtPath<Font>(SourceFontPath);
|
|
if (sourceFont == null)
|
|
{
|
|
Debug.LogError($"Default UI font source is missing: {SourceFontPath}");
|
|
return;
|
|
}
|
|
|
|
Type tmpFontAssetType = FindType("TMPro.TMP_FontAsset");
|
|
if (tmpFontAssetType == null)
|
|
{
|
|
Debug.LogError("Unable to find TMPro.TMP_FontAsset. Make sure TextMeshPro is installed.");
|
|
return;
|
|
}
|
|
|
|
Object fontAsset = AssetDatabase.LoadAssetAtPath(TmpFontAssetPath, tmpFontAssetType);
|
|
if (fontAsset == null)
|
|
fontAsset = CreateDynamicTmpFontAsset(sourceFont, tmpFontAssetType);
|
|
|
|
if (fontAsset == null)
|
|
return;
|
|
|
|
SetTmpDefaultFont(fontAsset);
|
|
ApplyFontToKnownPrefabs(fontAsset, tmpFontAssetType);
|
|
|
|
AssetDatabase.SaveAssets();
|
|
AssetDatabase.Refresh();
|
|
Debug.Log($"Installed default UI font: {TmpFontAssetPath}");
|
|
}
|
|
finally
|
|
{
|
|
s_IsInstalling = false;
|
|
}
|
|
}
|
|
|
|
[MenuItem("XWorld/UI/Install Default Font", true)]
|
|
private static bool ValidateInstallDefaultFont()
|
|
{
|
|
return File.Exists(SourceFontPath);
|
|
}
|
|
|
|
private static Object CreateDynamicTmpFontAsset(Font sourceFont, Type tmpFontAssetType)
|
|
{
|
|
string directory = Path.GetDirectoryName(TmpFontAssetPath);
|
|
if (!string.IsNullOrEmpty(directory))
|
|
Directory.CreateDirectory(directory);
|
|
|
|
const int samplingPointSize = 90;
|
|
const int atlasPadding = 9;
|
|
const int atlasSize = 2048;
|
|
const GlyphRenderMode renderMode = GlyphRenderMode.SDFAA;
|
|
|
|
Type atlasPopulationModeType = FindType("TMPro.AtlasPopulationMode");
|
|
if (atlasPopulationModeType == null)
|
|
{
|
|
Debug.LogError("Unable to find TMPro.AtlasPopulationMode.");
|
|
return null;
|
|
}
|
|
|
|
MethodInfo createFontAsset = tmpFontAssetType.GetMethod(
|
|
"CreateFontAsset",
|
|
BindingFlags.Public | BindingFlags.Static,
|
|
null,
|
|
new[]
|
|
{
|
|
typeof(Font),
|
|
typeof(int),
|
|
typeof(int),
|
|
typeof(GlyphRenderMode),
|
|
typeof(int),
|
|
typeof(int),
|
|
atlasPopulationModeType,
|
|
typeof(bool)
|
|
},
|
|
null);
|
|
if (createFontAsset == null)
|
|
{
|
|
Debug.LogError("Unable to find TMP_FontAsset.CreateFontAsset overload.");
|
|
return null;
|
|
}
|
|
|
|
object dynamicMode = Enum.ToObject(atlasPopulationModeType, 1);
|
|
Object fontAsset = createFontAsset.Invoke(
|
|
null,
|
|
new object[]
|
|
{
|
|
sourceFont,
|
|
samplingPointSize,
|
|
atlasPadding,
|
|
renderMode,
|
|
atlasSize,
|
|
atlasSize,
|
|
dynamicMode,
|
|
true
|
|
}) as Object;
|
|
if (fontAsset == null)
|
|
{
|
|
Debug.LogError($"Unable to create TMP font asset from: {SourceFontPath}");
|
|
return null;
|
|
}
|
|
|
|
fontAsset.name = "siyuan_CN_Bold SDF";
|
|
AssetDatabase.CreateAsset(fontAsset, TmpFontAssetPath);
|
|
|
|
Texture2D[] atlasTextures = tmpFontAssetType.GetProperty("atlasTextures")?.GetValue(fontAsset) as Texture2D[];
|
|
Texture2D texture = atlasTextures != null && atlasTextures.Length > 0 ? atlasTextures[0] : null;
|
|
if (texture != null)
|
|
{
|
|
texture.name = "siyuan_CN_Bold Atlas";
|
|
AssetDatabase.AddObjectToAsset(texture, fontAsset);
|
|
}
|
|
|
|
Material material = tmpFontAssetType.GetField("material")?.GetValue(fontAsset) as Material;
|
|
if (material != null)
|
|
{
|
|
material.name = "siyuan_CN_Bold Atlas Material";
|
|
AssetDatabase.AddObjectToAsset(material, fontAsset);
|
|
}
|
|
|
|
SetCreationSettings(fontAsset, tmpFontAssetType, samplingPointSize, atlasPadding, atlasSize, renderMode);
|
|
|
|
EditorUtility.SetDirty(fontAsset);
|
|
return fontAsset;
|
|
}
|
|
|
|
private static void SetCreationSettings(Object fontAsset, Type tmpFontAssetType, int samplingPointSize, int atlasPadding, int atlasSize, GlyphRenderMode renderMode)
|
|
{
|
|
PropertyInfo creationSettingsProperty = tmpFontAssetType.GetProperty("creationSettings");
|
|
if (creationSettingsProperty == null)
|
|
return;
|
|
|
|
object settings = Activator.CreateInstance(creationSettingsProperty.PropertyType);
|
|
SetField(settings, "sourceFontFileGUID", AssetDatabase.AssetPathToGUID(SourceFontPath));
|
|
SetField(settings, "pointSize", samplingPointSize);
|
|
SetField(settings, "pointSizeSamplingMode", 0);
|
|
SetField(settings, "padding", atlasPadding);
|
|
SetField(settings, "packingMode", 0);
|
|
SetField(settings, "atlasWidth", atlasSize);
|
|
SetField(settings, "atlasHeight", atlasSize);
|
|
SetField(settings, "characterSetSelectionMode", 7);
|
|
SetField(settings, "characterSequence", string.Empty);
|
|
SetField(settings, "renderMode", (int)renderMode);
|
|
SetField(settings, "includeFontFeatures", false);
|
|
creationSettingsProperty.SetValue(fontAsset, settings);
|
|
}
|
|
|
|
private static void SetTmpDefaultFont(Object fontAsset)
|
|
{
|
|
Object settings = AssetDatabase.LoadAssetAtPath<Object>(TmpSettingsPath);
|
|
if (settings == null)
|
|
{
|
|
Debug.LogWarning($"TMP Settings asset is missing: {TmpSettingsPath}");
|
|
return;
|
|
}
|
|
|
|
SerializedObject serializedSettings = new SerializedObject(settings);
|
|
serializedSettings.FindProperty("m_defaultFontAsset").objectReferenceValue = fontAsset;
|
|
serializedSettings.ApplyModifiedPropertiesWithoutUndo();
|
|
EditorUtility.SetDirty(settings);
|
|
}
|
|
|
|
private static void ApplyFontToKnownPrefabs(Object fontAsset, Type tmpFontAssetType)
|
|
{
|
|
Type tmpTextType = FindType("TMPro.TMP_Text");
|
|
Type tmpInputFieldType = FindType("TMPro.TMP_InputField");
|
|
Object fontMaterial = tmpFontAssetType.GetField("material")?.GetValue(fontAsset) as Object;
|
|
string[] searchFolders =
|
|
{
|
|
"Assets/Game/Art/UI/Prefab",
|
|
"Assets/Base/Prefab"
|
|
};
|
|
|
|
string[] prefabGuids = AssetDatabase.FindAssets("t:Prefab", searchFolders);
|
|
foreach (string prefabGuid in prefabGuids)
|
|
{
|
|
string prefabPath = AssetDatabase.GUIDToAssetPath(prefabGuid);
|
|
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
|
|
if (prefab == null)
|
|
continue;
|
|
|
|
bool changed = false;
|
|
foreach (Component component in prefab.GetComponentsInChildren<Component>(true))
|
|
{
|
|
if (component == null)
|
|
continue;
|
|
|
|
Type componentType = component.GetType();
|
|
if (tmpTextType != null && tmpTextType.IsAssignableFrom(componentType))
|
|
{
|
|
changed |= SetSerializedObjectReference(component, "m_fontAsset", fontAsset);
|
|
changed |= SetSerializedObjectReference(component, "m_sharedMaterial", fontMaterial);
|
|
}
|
|
|
|
if (tmpInputFieldType != null && tmpInputFieldType.IsAssignableFrom(componentType))
|
|
changed |= SetSerializedObjectReference(component, "m_GlobalFontAsset", fontAsset);
|
|
}
|
|
|
|
if (changed)
|
|
EditorUtility.SetDirty(prefab);
|
|
}
|
|
}
|
|
|
|
private static bool SetSerializedObjectReference(Object target, string propertyName, Object value)
|
|
{
|
|
SerializedObject serializedObject = new SerializedObject(target);
|
|
SerializedProperty property = serializedObject.FindProperty(propertyName);
|
|
if (property == null || property.objectReferenceValue == value)
|
|
return false;
|
|
|
|
property.objectReferenceValue = value;
|
|
serializedObject.ApplyModifiedPropertiesWithoutUndo();
|
|
EditorUtility.SetDirty(target);
|
|
return true;
|
|
}
|
|
|
|
private static void SetField(object target, string fieldName, object value)
|
|
{
|
|
target.GetType().GetField(fieldName)?.SetValue(target, value);
|
|
}
|
|
|
|
private static Type FindType(string fullName)
|
|
{
|
|
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
|
|
{
|
|
Type type = assembly.GetType(fullName);
|
|
if (type != null)
|
|
return type;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|