Initial commit: Client Doc docs Server Tools
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,269 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user