2026-11-12 12:28:40 +08:00
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using UnityEditor;
|
|
|
|
|
using UnityEditor.SceneManagement;
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
using UnityEngine.TextCore.LowLevel;
|
|
|
|
|
using UnityEngine.TextCore.Text;
|
|
|
|
|
using UnityEngine.UIElements;
|
|
|
|
|
|
|
|
|
|
namespace Ironspore.Editor
|
|
|
|
|
{
|
|
|
|
|
public static class LocalizationBuilder
|
|
|
|
|
{
|
|
|
|
|
public const string CatalogPath = "Assets/Game/Localization/LocalizationCatalog.asset";
|
|
|
|
|
const string FontPath = "Assets/ThirdParty/Adobe/NotoSansSC/NotoSansSC-VF.ttf";
|
|
|
|
|
const string FontAssetPath = "Assets/Game/UI/Fonts/GameFont.asset";
|
|
|
|
|
|
|
|
|
|
[MenuItem("Ironspore/Configure Localization")]
|
|
|
|
|
public static string Configure()
|
|
|
|
|
{
|
|
|
|
|
if (EditorApplication.isPlaying)
|
|
|
|
|
throw new InvalidOperationException("Stop play mode before configuring localization.");
|
|
|
|
|
Directory.CreateDirectory("Assets/Game/UI/Fonts");
|
|
|
|
|
AssetDatabase.Refresh();
|
|
|
|
|
var importer = AssetImporter.GetAtPath(FontPath) as TrueTypeFontImporter;
|
|
|
|
|
if (!importer)
|
|
|
|
|
throw new FileNotFoundException("Archived OFL font has not imported.", FontPath);
|
|
|
|
|
importer.fontTextureCase = FontTextureCase.Dynamic;
|
|
|
|
|
importer.fontSize = 40;
|
|
|
|
|
importer.includeFontData = true;
|
|
|
|
|
importer.SaveAndReimport();
|
|
|
|
|
var font = AssetDatabase.LoadAssetAtPath<Font>(FontPath);
|
|
|
|
|
var catalog = AssetDatabase.LoadAssetAtPath<LocalizationCatalog>(CatalogPath);
|
|
|
|
|
if (!catalog)
|
|
|
|
|
{
|
|
|
|
|
catalog = ScriptableObject.CreateInstance<LocalizationCatalog>();
|
|
|
|
|
AssetDatabase.CreateAsset(catalog, CatalogPath);
|
|
|
|
|
}
|
|
|
|
|
catalog.locales = new[]
|
|
|
|
|
{
|
|
|
|
|
new GameLocale { code = "zh-Hans", nativeName = "简体中文", table = AssetDatabase.LoadAssetAtPath<UnityEngine.TextAsset>("Assets/Game/Localization/Tables/zh-Hans.json") },
|
|
|
|
|
new GameLocale { code = "en", nativeName = "English", table = AssetDatabase.LoadAssetAtPath<UnityEngine.TextAsset>("Assets/Game/Localization/Tables/en.json") }
|
|
|
|
|
};
|
|
|
|
|
var uiFont = AssetDatabase.LoadAssetAtPath<FontAsset>(FontAssetPath);
|
|
|
|
|
if (!uiFont)
|
|
|
|
|
{
|
|
|
|
|
uiFont = FontAsset.CreateFontAsset(font, 64, 7, GlyphRenderMode.SDFAA, 2048, 2048, AtlasPopulationMode.Dynamic, true);
|
|
|
|
|
uiFont.name = "GameFont";
|
|
|
|
|
AssetDatabase.CreateAsset(uiFont, FontAssetPath);
|
|
|
|
|
uiFont.material.name = "GameFont Material";
|
|
|
|
|
AssetDatabase.AddObjectToAsset(uiFont.material, uiFont);
|
|
|
|
|
}
|
|
|
|
|
uiFont.isMultiAtlasTexturesEnabled = true;
|
2026-11-12 18:03:04 +08:00
|
|
|
var fontSettings = new SerializedObject(uiFont);
|
|
|
|
|
fontSettings.FindProperty("m_ClearDynamicDataOnBuild").boolValue = false;
|
|
|
|
|
fontSettings.ApplyModifiedPropertiesWithoutUndo();
|
2026-11-12 12:28:40 +08:00
|
|
|
var characters = new HashSet<char>(Enumerable.Range(32, 95).Select(x => (char)x));
|
|
|
|
|
foreach (var locale in catalog.locales)
|
|
|
|
|
{
|
|
|
|
|
foreach (char c in locale.nativeName)
|
|
|
|
|
characters.Add(c);
|
|
|
|
|
foreach (var entry in GameLocalization.ReadTable(locale.table))
|
|
|
|
|
foreach (char c in entry.Value)
|
|
|
|
|
if (!char.IsControl(c))
|
|
|
|
|
characters.Add(c);
|
|
|
|
|
}
|
|
|
|
|
string absent;
|
|
|
|
|
uiFont.TryAddCharacters(new string(characters.OrderBy(x => x).ToArray()), out absent);
|
|
|
|
|
if (!string.IsNullOrEmpty(absent))
|
|
|
|
|
throw new InvalidOperationException("Font is missing required glyphs: " + absent);
|
|
|
|
|
foreach (var atlas in uiFont.atlasTextures)
|
|
|
|
|
{
|
|
|
|
|
if (!atlas || AssetDatabase.Contains(atlas))
|
|
|
|
|
continue;
|
|
|
|
|
atlas.name = "GameFont Atlas";
|
|
|
|
|
AssetDatabase.AddObjectToAsset(atlas, uiFont);
|
|
|
|
|
}
|
|
|
|
|
EditorUtility.SetDirty(uiFont);
|
|
|
|
|
catalog.worldFont = font;
|
|
|
|
|
catalog.uiFont = uiFont;
|
|
|
|
|
EditorUtility.SetDirty(catalog);
|
|
|
|
|
var textSettings = AssetDatabase.LoadAssetAtPath<PanelTextSettings>("Assets/Game/UI/Fonts/GameTextSettings.asset");
|
|
|
|
|
if (!textSettings)
|
|
|
|
|
{
|
|
|
|
|
textSettings = ScriptableObject.CreateInstance<PanelTextSettings>();
|
|
|
|
|
AssetDatabase.CreateAsset(textSettings, "Assets/Game/UI/Fonts/GameTextSettings.asset");
|
|
|
|
|
}
|
|
|
|
|
textSettings.fallbackFontAssets = new List<FontAsset> { uiFont };
|
|
|
|
|
textSettings.clearDynamicDataOnBuild = false;
|
|
|
|
|
EditorUtility.SetDirty(textSettings);
|
|
|
|
|
var panel = AssetDatabase.LoadAssetAtPath<PanelSettings>("Assets/Game/UI/GamePanelSettings.asset");
|
|
|
|
|
panel.textSettings = textSettings;
|
|
|
|
|
EditorUtility.SetDirty(panel);
|
|
|
|
|
string previousScene = EditorSceneManager.GetActiveScene().path;
|
|
|
|
|
foreach (string name in new[] { "Bootstrap", "MechTestRange", "WarzonePrototype" })
|
|
|
|
|
{
|
|
|
|
|
var scene = EditorSceneManager.OpenScene("Assets/Scenes/" + name + ".unity");
|
|
|
|
|
var session = UnityEngine.Object.FindAnyObjectByType<GameSession>();
|
|
|
|
|
session.localization = catalog;
|
|
|
|
|
EditorUtility.SetDirty(session);
|
|
|
|
|
EditorSceneManager.MarkSceneDirty(scene);
|
|
|
|
|
EditorSceneManager.SaveScene(scene);
|
|
|
|
|
}
|
|
|
|
|
AssetDatabase.SaveAssets();
|
|
|
|
|
EditorSceneManager.OpenScene(string.IsNullOrEmpty(previousScene) ? "Assets/Scenes/Bootstrap.unity" : previousScene);
|
|
|
|
|
return "Configured two locales, " + characters.Count + " glyphs, UI Toolkit and TextMesh fonts, and all three scenes.";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|