feat: 本地化字体兼容
This commit is contained in:
@@ -15,7 +15,7 @@ MonoBehaviour:
|
|||||||
m_DefaultGroup: 09546aca93a801441859af2a811fe53b
|
m_DefaultGroup: 09546aca93a801441859af2a811fe53b
|
||||||
m_currentHash:
|
m_currentHash:
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
Hash: ae2a22805ec7ff55174c38995aa3e54d
|
Hash: 00000000000000000000000000000000
|
||||||
m_OptimizeCatalogSize: 0
|
m_OptimizeCatalogSize: 0
|
||||||
m_BuildRemoteCatalog: 0
|
m_BuildRemoteCatalog: 0
|
||||||
m_BundleLocalCatalog: 0
|
m_BundleLocalCatalog: 0
|
||||||
@@ -71,6 +71,7 @@ MonoBehaviour:
|
|||||||
- {fileID: 11400000, guid: 089fc86f714d6a94bafea3b117fbd639, type: 2}
|
- {fileID: 11400000, guid: 089fc86f714d6a94bafea3b117fbd639, type: 2}
|
||||||
- {fileID: 11400000, guid: 1c592abe924a856439461f3785ec53da, type: 2}
|
- {fileID: 11400000, guid: 1c592abe924a856439461f3785ec53da, type: 2}
|
||||||
- {fileID: 11400000, guid: 26fd03652560e7f4abec092229fc38f2, type: 2}
|
- {fileID: 11400000, guid: 26fd03652560e7f4abec092229fc38f2, type: 2}
|
||||||
|
- {fileID: 11400000, guid: 1051221ba75fcea47b67648acedc5383, type: 2}
|
||||||
m_BuildSettings:
|
m_BuildSettings:
|
||||||
m_CompileScriptsInVirtualMode: 0
|
m_CompileScriptsInVirtualMode: 0
|
||||||
m_CleanupStreamingAssetsAfterBuilds: 1
|
m_CleanupStreamingAssetsAfterBuilds: 1
|
||||||
@@ -121,6 +122,7 @@ MonoBehaviour:
|
|||||||
- Locale-pt-BR
|
- Locale-pt-BR
|
||||||
- Locale-ru
|
- Locale-ru
|
||||||
- Locale-es
|
- Locale-es
|
||||||
|
- Typography
|
||||||
m_SchemaTemplates: []
|
m_SchemaTemplates: []
|
||||||
m_GroupTemplateObjects:
|
m_GroupTemplateObjects:
|
||||||
- {fileID: 11400000, guid: 0c158052680a3e94184c450d00291514, type: 2}
|
- {fileID: 11400000, guid: 0c158052680a3e94184c450d00291514, type: 2}
|
||||||
|
|||||||
+45
@@ -0,0 +1,45 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!114 &11400000
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 0}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: e5d17a21594effb4e9591490b009e7aa, type: 3}
|
||||||
|
m_Name: Shared_Typography_BundledAssetGroupSchema
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
m_Group: {fileID: 11400000, guid: 1051221ba75fcea47b67648acedc5383, type: 2}
|
||||||
|
m_InternalBundleIdMode: 1
|
||||||
|
m_Compression: 1
|
||||||
|
m_IncludeAddressInCatalog: 1
|
||||||
|
m_IncludeGUIDInCatalog: 1
|
||||||
|
m_IncludeLabelsInCatalog: 1
|
||||||
|
m_InternalIdNamingMode: 0
|
||||||
|
m_CacheClearBehavior: 0
|
||||||
|
m_IncludeInBuild: 1
|
||||||
|
m_BundledAssetProviderType:
|
||||||
|
m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||||
|
m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.BundledAssetProvider
|
||||||
|
m_ForceUniqueProvider: 0
|
||||||
|
m_UseAssetBundleCache: 1
|
||||||
|
m_UseAssetBundleCrc: 1
|
||||||
|
m_UseAssetBundleCrcForCachedBundles: 1
|
||||||
|
m_UseUWRForLocalBundles: 0
|
||||||
|
m_Timeout: 0
|
||||||
|
m_ChunkedTransfer: 0
|
||||||
|
m_RedirectLimit: -1
|
||||||
|
m_RetryCount: 0
|
||||||
|
m_BuildPath:
|
||||||
|
m_Id: 2ae6a7549097a9b4caefe44b61b1fcc5
|
||||||
|
m_LoadPath:
|
||||||
|
m_Id: 631ec5c677ab8084fb9be9b9ee39c6ef
|
||||||
|
m_BundleMode: 0
|
||||||
|
m_AssetBundleProviderType:
|
||||||
|
m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||||
|
m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.AssetBundleProvider
|
||||||
|
m_BundleNaming: 0
|
||||||
|
m_AssetLoadMode: 0
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b39cbcc4de75ad6468842c36c7e5138d
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!114 &11400000
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 0}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 5834b5087d578d24c926ce20cd31e6d6, type: 3}
|
||||||
|
m_Name: Shared_Typography_ContentUpdateGroupSchema
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
m_Group: {fileID: 11400000, guid: 1051221ba75fcea47b67648acedc5383, type: 2}
|
||||||
|
m_StaticContent: 0
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6bd7e9bcac1e24042b79a54f47f52106
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!114 &11400000
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 0}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: bbb281ee3bf0b054c82ac2347e9e782c, type: 3}
|
||||||
|
m_Name: Shared_Typography
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
m_GroupName: Shared_Typography
|
||||||
|
m_Data:
|
||||||
|
m_SerializedData: []
|
||||||
|
m_GUID: 7663edc0b4aef67468c672b641025c85
|
||||||
|
m_SerializeEntries:
|
||||||
|
- m_GUID: e41dce1e1d6e0564b95054b5c8b8bb4c
|
||||||
|
m_Address: Typography/Profile/ZhHans
|
||||||
|
m_ReadOnly: 0
|
||||||
|
m_SerializedLabels:
|
||||||
|
- Typography
|
||||||
|
FlaggedDuringContentUpdateRestriction: 0
|
||||||
|
- m_GUID: 67cd590df91846b4d81698d68426e616
|
||||||
|
m_Address: Typography/Profile/Japanese
|
||||||
|
m_ReadOnly: 0
|
||||||
|
m_SerializedLabels:
|
||||||
|
- Typography
|
||||||
|
FlaggedDuringContentUpdateRestriction: 0
|
||||||
|
- m_GUID: d53382c947b5edc4a88216c5fcf37b38
|
||||||
|
m_Address: Typography/Profile/Latin
|
||||||
|
m_ReadOnly: 0
|
||||||
|
m_SerializedLabels:
|
||||||
|
- Typography
|
||||||
|
FlaggedDuringContentUpdateRestriction: 0
|
||||||
|
- m_GUID: f82150d4b378dc441884eb47d4f67b7c
|
||||||
|
m_Address: Typography/Profile/Russian
|
||||||
|
m_ReadOnly: 0
|
||||||
|
m_SerializedLabels:
|
||||||
|
- Typography
|
||||||
|
FlaggedDuringContentUpdateRestriction: 0
|
||||||
|
m_ReadOnly: 0
|
||||||
|
m_Settings: {fileID: 11400000, guid: 77169ce22e430f64fb36c771815a4a7b, type: 2}
|
||||||
|
m_SchemaSet:
|
||||||
|
m_Schemas:
|
||||||
|
- {fileID: 11400000, guid: b39cbcc4de75ad6468842c36c7e5138d, type: 2}
|
||||||
|
- {fileID: 11400000, guid: 6bd7e9bcac1e24042b79a54f47f52106, type: 2}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1051221ba75fcea47b67648acedc5383
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,182 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using AibisDream.Framework;
|
||||||
|
using AibisDream.EditorTools;
|
||||||
|
using AibisDream.UI;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using TMPro;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.AddressableAssets;
|
||||||
|
using UnityEditor.SceneManagement;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.SceneManagement;
|
||||||
|
|
||||||
|
namespace AibisDream.SystemEditor.Tests
|
||||||
|
{
|
||||||
|
public sealed class TypographyContractTests
|
||||||
|
{
|
||||||
|
private const string ProfileFolder = "Assets/GameContent/Common/Typography/Profiles";
|
||||||
|
|
||||||
|
[TestCase(LocaleKind.Cn, TypographyProfileKind.ZhHans)]
|
||||||
|
[TestCase(LocaleKind.Ja, TypographyProfileKind.Japanese)]
|
||||||
|
[TestCase(LocaleKind.En, TypographyProfileKind.Latin)]
|
||||||
|
[TestCase(LocaleKind.Es, TypographyProfileKind.Latin)]
|
||||||
|
[TestCase(LocaleKind.PtBr, TypographyProfileKind.Latin)]
|
||||||
|
[TestCase(LocaleKind.Ru, TypographyProfileKind.Russian)]
|
||||||
|
public void LocaleMapping_UsesExpectedProfile(LocaleKind locale, TypographyProfileKind expected)
|
||||||
|
{
|
||||||
|
Assert.That(TypographyLocaleMapping.GetProfileKind(locale), Is.EqualTo(expected));
|
||||||
|
Assert.That(
|
||||||
|
TypographyLocaleMapping.GetProfileKey(locale),
|
||||||
|
Is.EqualTo($"Typography/Profile/{expected}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Resolve_MissingRoleFallsBackToBody()
|
||||||
|
{
|
||||||
|
TMP_FontAsset font = AssetDatabase.LoadAssetAtPath<TMP_FontAsset>(
|
||||||
|
"Assets/Font/Assets/ChillBitmap_16px SDF.asset");
|
||||||
|
var profile = ScriptableObject.CreateInstance<TypographyProfile>();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var body = new TypographyStyle(TypographyRole.Body, font);
|
||||||
|
profile.Configure(TypographyProfileKind.Latin, new[] { body });
|
||||||
|
Assert.That(profile.Resolve(TypographyRole.Dialogue), Is.SameAs(body));
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
UnityEngine.Object.DestroyImmediate(profile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Binding_ReapplyUsesCapturedBaselineWithoutAccumulatingMetrics()
|
||||||
|
{
|
||||||
|
TMP_FontAsset font = AssetDatabase.LoadAssetAtPath<TMP_FontAsset>(
|
||||||
|
"Assets/Font/Assets/ChillBitmap_16px SDF.asset");
|
||||||
|
var profile = ScriptableObject.CreateInstance<TypographyProfile>();
|
||||||
|
var go = new GameObject("TypographyMetricContract", typeof(RectTransform));
|
||||||
|
try
|
||||||
|
{
|
||||||
|
TextMeshProUGUI text = go.AddComponent<TextMeshProUGUI>();
|
||||||
|
text.fontSize = 40f;
|
||||||
|
text.lineSpacing = 3f;
|
||||||
|
text.characterSpacing = 2f;
|
||||||
|
LocalizedTypography binding = go.AddComponent<LocalizedTypography>();
|
||||||
|
profile.Configure(
|
||||||
|
TypographyProfileKind.Latin,
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
new TypographyStyle(
|
||||||
|
TypographyRole.Body,
|
||||||
|
font,
|
||||||
|
fontSizeMultiplier: 0.75f,
|
||||||
|
lineSpacingOffset: 1f,
|
||||||
|
characterSpacingOffset: 2f)
|
||||||
|
});
|
||||||
|
|
||||||
|
binding.ApplyProfile(profile);
|
||||||
|
binding.ApplyProfile(profile);
|
||||||
|
|
||||||
|
Assert.That(text.fontSize, Is.EqualTo(30f));
|
||||||
|
Assert.That(text.lineSpacing, Is.EqualTo(4f));
|
||||||
|
Assert.That(text.characterSpacing, Is.EqualTo(4f));
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
UnityEngine.Object.DestroyImmediate(go);
|
||||||
|
UnityEngine.Object.DestroyImmediate(profile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void FirstRoundProfiles_AreCompleteAndAddressableByConvention()
|
||||||
|
{
|
||||||
|
foreach (TypographyProfileKind kind in Enum.GetValues(typeof(TypographyProfileKind)))
|
||||||
|
{
|
||||||
|
TypographyProfile profile = AssetDatabase.LoadAssetAtPath<TypographyProfile>(
|
||||||
|
$"{ProfileFolder}/{kind}.asset");
|
||||||
|
Assert.That(profile, Is.Not.Null, kind.ToString());
|
||||||
|
Assert.That(profile.ProfileKind, Is.EqualTo(kind));
|
||||||
|
Assert.That(profile.GetValidationErrors(), Is.Empty, kind.ToString());
|
||||||
|
Assert.That(
|
||||||
|
Enum.GetValues(typeof(TypographyRole)).Cast<TypographyRole>()
|
||||||
|
.All(role => profile.Resolve(role) != null),
|
||||||
|
Is.True,
|
||||||
|
kind.ToString());
|
||||||
|
|
||||||
|
string guid = AssetDatabase.AssetPathToGUID($"{ProfileFolder}/{kind}.asset");
|
||||||
|
var entry = AddressableAssetSettingsDefaultObject.Settings.FindAssetEntry(guid);
|
||||||
|
Assert.That(entry, Is.Not.Null, kind.ToString());
|
||||||
|
Assert.That(entry.address, Is.EqualTo($"Typography/Profile/{kind}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TerminalPrefabs_BindEveryTextExactlyOnce()
|
||||||
|
{
|
||||||
|
foreach (string path in TerminalTypographyMigration.PrefabPaths)
|
||||||
|
{
|
||||||
|
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(path);
|
||||||
|
Assert.That(prefab, Is.Not.Null, path);
|
||||||
|
|
||||||
|
TMP_Text[] texts = prefab.GetComponentsInChildren<TMP_Text>(true);
|
||||||
|
Assert.That(texts, Is.Not.Empty, path);
|
||||||
|
foreach (TMP_Text text in texts)
|
||||||
|
{
|
||||||
|
LocalizedTypography[] bindings = text.GetComponents<LocalizedTypography>();
|
||||||
|
Assert.That(bindings, Has.Length.EqualTo(1), $"{path}: {text.name}");
|
||||||
|
Assert.That(bindings[0].Role, Is.EqualTo(TypographyRole.Terminal),
|
||||||
|
$"{path}: {text.name}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void PersistenceTerminalComposition_InheritsTypographyBindings()
|
||||||
|
{
|
||||||
|
Scene scene = SceneManager.GetSceneByPath(TerminalTypographyMigration.PersistenceScenePath);
|
||||||
|
bool openedForTest = !scene.IsValid() || !scene.isLoaded;
|
||||||
|
if (openedForTest)
|
||||||
|
{
|
||||||
|
scene = EditorSceneManager.OpenScene(
|
||||||
|
TerminalTypographyMigration.PersistenceScenePath,
|
||||||
|
OpenSceneMode.Additive);
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Component[] hosts = scene.GetRootGameObjects()
|
||||||
|
.SelectMany(root => root.GetComponentsInChildren<Component>(true))
|
||||||
|
.Where(component => component is TerminalPanel || component is InGameTerminalPanel)
|
||||||
|
.ToArray();
|
||||||
|
Assert.That(hosts, Has.Length.EqualTo(2));
|
||||||
|
|
||||||
|
var violations = new List<string>();
|
||||||
|
foreach (Component host in hosts)
|
||||||
|
{
|
||||||
|
foreach (TMP_Text text in host.GetComponentsInChildren<TMP_Text>(true))
|
||||||
|
{
|
||||||
|
LocalizedTypography[] bindings = text.GetComponents<LocalizedTypography>();
|
||||||
|
if (bindings.Length != 1)
|
||||||
|
{
|
||||||
|
violations.Add($"{host.name}: {text.name} has {bindings.Length} bindings");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bindings[0].Role != TypographyRole.Terminal)
|
||||||
|
violations.Add($"{host.name}: {text.name} uses {bindings[0].Role}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.That(violations, Is.Empty, string.Join("\n", violations));
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (openedForTest)
|
||||||
|
EditorSceneManager.CloseScene(scene, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 895c6fa68b9b4ed4286362a258247f4a
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1ccca9c8cbbc67e47be376cc9db7ce55
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,229 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using AibisDream.Framework;
|
||||||
|
using AibisDream.UI;
|
||||||
|
using TMPro;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.SceneManagement;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.SceneManagement;
|
||||||
|
|
||||||
|
namespace AibisDream.EditorTools
|
||||||
|
{
|
||||||
|
public static class TerminalTypographyMigration
|
||||||
|
{
|
||||||
|
public const string PrefabFolder = "Assets/GameContent/Feature_MainUI/Prefabs";
|
||||||
|
public const string PersistenceScenePath = "Assets/Scenes/Persistence.unity";
|
||||||
|
|
||||||
|
// Sources must be migrated before their consumers so existing added-component
|
||||||
|
// overrides can be replaced by the component inherited from the source prefab.
|
||||||
|
public static readonly IReadOnlyList<string> PrefabPaths = new[]
|
||||||
|
{
|
||||||
|
$"{PrefabFolder}/Common/Select Item.prefab",
|
||||||
|
$"{PrefabFolder}/Common/Slider Item.prefab",
|
||||||
|
$"{PrefabFolder}/Common/TerminalButton.prefab",
|
||||||
|
$"{PrefabFolder}/Common/TerminalChapterCard.prefab",
|
||||||
|
$"{PrefabFolder}/Common/TerminalHeader.prefab",
|
||||||
|
$"{PrefabFolder}/Common/TerminalMenuItem.prefab",
|
||||||
|
$"{PrefabFolder}/Common/TerminalOptionItem.prefab",
|
||||||
|
$"{PrefabFolder}/Common/TerminalRecordItem.prefab",
|
||||||
|
$"{PrefabFolder}/Common/TerminalSliderItem.prefab",
|
||||||
|
$"{PrefabFolder}/Common/TerminalChapterPanel.prefab",
|
||||||
|
$"{PrefabFolder}/TerminalChapterPanel.prefab",
|
||||||
|
$"{PrefabFolder}/TerminalLoadingPanel.prefab",
|
||||||
|
$"{PrefabFolder}/TerminalRecordPanel.prefab",
|
||||||
|
$"{PrefabFolder}/TerminalSettingPanel.prefab",
|
||||||
|
$"{PrefabFolder}/TerminalSettingPanelMini.prefab",
|
||||||
|
$"{PrefabFolder}/TerminalStartPanel.prefab"
|
||||||
|
};
|
||||||
|
|
||||||
|
[MenuItem("Tools/AIBIS Dream/Typography/Migrate Terminal Prefabs")]
|
||||||
|
public static void MigrateTerminalPrefabs()
|
||||||
|
{
|
||||||
|
int textCount = 0;
|
||||||
|
int addedCount = 0;
|
||||||
|
int removedOverrideCount = 0;
|
||||||
|
|
||||||
|
foreach (string path in PrefabPaths)
|
||||||
|
{
|
||||||
|
MigrationResult result = MigratePrefab(path);
|
||||||
|
textCount += result.TextCount;
|
||||||
|
addedCount += result.AddedCount;
|
||||||
|
removedOverrideCount += result.RemovedOverrideCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sceneBindingCount = MigratePersistenceScene();
|
||||||
|
AssetDatabase.SaveAssets();
|
||||||
|
Debug.Log(
|
||||||
|
$"[TerminalTypographyMigration] Migrated {PrefabPaths.Count} prefabs and " +
|
||||||
|
$"{textCount} TMP texts. Added {addedCount} bindings; removed " +
|
||||||
|
$"{removedOverrideCount} redundant nested overrides; added " +
|
||||||
|
$"{sceneBindingCount} scene-owned bindings.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void MigrateFromCommandLine()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
MigrateTerminalPrefabs();
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Debug.LogException(exception);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MigrationResult MigratePrefab(string path)
|
||||||
|
{
|
||||||
|
GameObject root = PrefabUtility.LoadPrefabContents(path);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
TMP_Text[] texts = root.GetComponentsInChildren<TMP_Text>(true);
|
||||||
|
if (texts.Length == 0)
|
||||||
|
throw new InvalidOperationException($"Terminal prefab contains no TMP text: {path}");
|
||||||
|
|
||||||
|
int addedCount = 0;
|
||||||
|
int removedOverrideCount = 0;
|
||||||
|
foreach (TMP_Text text in texts)
|
||||||
|
{
|
||||||
|
int removedForText = RemoveRedundantNestedOverrides(text);
|
||||||
|
removedOverrideCount += removedForText;
|
||||||
|
|
||||||
|
LocalizedTypography[] bindings = text.GetComponents<LocalizedTypography>();
|
||||||
|
if (bindings.Length == 0)
|
||||||
|
{
|
||||||
|
// Saving the removed override makes the source component visible
|
||||||
|
// again when this consumer prefab is next loaded.
|
||||||
|
if (removedForText > 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
LocalizedTypography binding = text.gameObject.AddComponent<LocalizedTypography>();
|
||||||
|
binding.ConfigureForEditor(TypographyRole.Terminal);
|
||||||
|
addedCount++;
|
||||||
|
}
|
||||||
|
else if (bindings.Length == 1)
|
||||||
|
{
|
||||||
|
ConfigureIfOwnedByCurrentPrefab(bindings[0]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
$"TMP text has multiple typography bindings after cleanup: {path} / {text.name}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PrefabUtility.SaveAsPrefabAsset(root, path);
|
||||||
|
return new MigrationResult(texts.Length, addedCount, removedOverrideCount);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
PrefabUtility.UnloadPrefabContents(root);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int MigratePersistenceScene()
|
||||||
|
{
|
||||||
|
Scene scene = SceneManager.GetSceneByPath(PersistenceScenePath);
|
||||||
|
bool openedForMigration = !scene.IsValid() || !scene.isLoaded;
|
||||||
|
if (openedForMigration)
|
||||||
|
scene = EditorSceneManager.OpenScene(PersistenceScenePath, OpenSceneMode.Additive);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int addedCount = 0;
|
||||||
|
var visited = new HashSet<TMP_Text>();
|
||||||
|
foreach (GameObject root in scene.GetRootGameObjects())
|
||||||
|
{
|
||||||
|
Component[] hosts = root.GetComponentsInChildren<Component>(true);
|
||||||
|
foreach (Component host in hosts)
|
||||||
|
{
|
||||||
|
if (host is not TerminalPanel && host is not InGameTerminalPanel)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
foreach (TMP_Text text in host.GetComponentsInChildren<TMP_Text>(true))
|
||||||
|
{
|
||||||
|
if (!visited.Add(text))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
LocalizedTypography[] bindings = text.GetComponents<LocalizedTypography>();
|
||||||
|
if (bindings.Length == 1)
|
||||||
|
continue;
|
||||||
|
if (bindings.Length > 1)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
$"Scene TMP text has multiple typography bindings: {text.name}");
|
||||||
|
}
|
||||||
|
if (PrefabUtility.IsPartOfPrefabInstance(text))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
$"Terminal prefab instance is missing its source binding: {text.name}");
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalizedTypography binding = text.gameObject.AddComponent<LocalizedTypography>();
|
||||||
|
binding.ConfigureForEditor(TypographyRole.Terminal);
|
||||||
|
addedCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addedCount > 0)
|
||||||
|
{
|
||||||
|
EditorSceneManager.MarkSceneDirty(scene);
|
||||||
|
EditorSceneManager.SaveScene(scene);
|
||||||
|
}
|
||||||
|
|
||||||
|
return addedCount;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (openedForMigration)
|
||||||
|
EditorSceneManager.CloseScene(scene, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int RemoveRedundantNestedOverrides(TMP_Text text)
|
||||||
|
{
|
||||||
|
GameObject sourceObject = PrefabUtility.GetCorrespondingObjectFromSource(text.gameObject);
|
||||||
|
if (sourceObject == null || sourceObject.GetComponent<LocalizedTypography>() == null)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
int removed = 0;
|
||||||
|
LocalizedTypography[] bindings = text.GetComponents<LocalizedTypography>();
|
||||||
|
foreach (LocalizedTypography binding in bindings)
|
||||||
|
{
|
||||||
|
if (!PrefabUtility.IsAddedComponentOverride(binding))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
UnityEngine.Object.DestroyImmediate(binding, true);
|
||||||
|
removed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ConfigureIfOwnedByCurrentPrefab(LocalizedTypography binding)
|
||||||
|
{
|
||||||
|
if (PrefabUtility.IsPartOfPrefabInstance(binding) &&
|
||||||
|
PrefabUtility.GetCorrespondingObjectFromSource(binding) != null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.ConfigureForEditor(TypographyRole.Terminal);
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly struct MigrationResult
|
||||||
|
{
|
||||||
|
public MigrationResult(int textCount, int addedCount, int removedOverrideCount)
|
||||||
|
{
|
||||||
|
TextCount = textCount;
|
||||||
|
AddedCount = addedCount;
|
||||||
|
RemovedOverrideCount = removedOverrideCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int TextCount { get; }
|
||||||
|
public int AddedCount { get; }
|
||||||
|
public int RemovedOverrideCount { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 72a6797e130a7f643bd42bf648543ec2
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,169 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using AibisDream.Framework;
|
||||||
|
using TMPro;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.AddressableAssets;
|
||||||
|
using UnityEditor.AddressableAssets.Settings;
|
||||||
|
using UnityEditor.AddressableAssets.Settings.GroupSchemas;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace AibisDream.EditorTools
|
||||||
|
{
|
||||||
|
public static class TypographyFirstRoundBuilder
|
||||||
|
{
|
||||||
|
private const string GroupName = "Shared_Typography";
|
||||||
|
private const string ProfileFolder = "Assets/GameContent/Common/Typography/Profiles";
|
||||||
|
private const string WenQuanYiPath = "Assets/Font/Assets/WenQuanYi Bitmap Song 14px SDF.asset";
|
||||||
|
private const string ChillBitmapPath = "Assets/Font/Assets/ChillBitmap_16px SDF.asset";
|
||||||
|
private const string ArkPixelPath = "Assets/Font/TTF/ark-pixel-16px-proportional-latin SDF.asset";
|
||||||
|
|
||||||
|
[MenuItem("Tools/AIBIS Dream/Typography/Rebuild First Round Assets")]
|
||||||
|
public static void BuildFirstRoundAssets()
|
||||||
|
{
|
||||||
|
EnsureFolder(ProfileFolder);
|
||||||
|
|
||||||
|
TMP_FontAsset wenQuanYi = LoadRequiredFont(WenQuanYiPath);
|
||||||
|
TMP_FontAsset chillBitmap = LoadRequiredFont(ChillBitmapPath);
|
||||||
|
TMP_FontAsset arkPixel = LoadRequiredFont(ArkPixelPath);
|
||||||
|
|
||||||
|
AddressableAssetSettings settings = AddressableAssetSettingsDefaultObject.Settings;
|
||||||
|
if (settings == null)
|
||||||
|
throw new InvalidOperationException("AddressableAssetSettings 不存在。");
|
||||||
|
|
||||||
|
AddressableAssetGroup group = GetOrCreateGroup(settings);
|
||||||
|
CreateOrUpdateProfile(
|
||||||
|
settings,
|
||||||
|
group,
|
||||||
|
TypographyProfileKind.ZhHans,
|
||||||
|
wenQuanYi,
|
||||||
|
new[] { chillBitmap });
|
||||||
|
CreateOrUpdateProfile(
|
||||||
|
settings,
|
||||||
|
group,
|
||||||
|
TypographyProfileKind.Japanese,
|
||||||
|
chillBitmap,
|
||||||
|
new[] { wenQuanYi });
|
||||||
|
CreateOrUpdateProfile(
|
||||||
|
settings,
|
||||||
|
group,
|
||||||
|
TypographyProfileKind.Latin,
|
||||||
|
arkPixel,
|
||||||
|
new[] { chillBitmap });
|
||||||
|
CreateOrUpdateProfile(
|
||||||
|
settings,
|
||||||
|
group,
|
||||||
|
TypographyProfileKind.Russian,
|
||||||
|
arkPixel,
|
||||||
|
new[] { chillBitmap });
|
||||||
|
|
||||||
|
TerminalTypographyMigration.MigrateTerminalPrefabs();
|
||||||
|
|
||||||
|
EditorUtility.SetDirty(settings);
|
||||||
|
AssetDatabase.SaveAssets();
|
||||||
|
AssetDatabase.Refresh();
|
||||||
|
Debug.Log("[TypographyFirstRoundBuilder] Profile、Addressables 和首轮试点绑定已更新。");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void BuildFromCommandLine()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
BuildFirstRoundAssets();
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Debug.LogException(exception);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TMP_FontAsset LoadRequiredFont(string path)
|
||||||
|
{
|
||||||
|
TMP_FontAsset font = AssetDatabase.LoadAssetAtPath<TMP_FontAsset>(path);
|
||||||
|
if (font == null)
|
||||||
|
throw new InvalidOperationException($"找不到字体资产:{path}");
|
||||||
|
return font;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AddressableAssetGroup GetOrCreateGroup(AddressableAssetSettings settings)
|
||||||
|
{
|
||||||
|
AddressableAssetGroup group = settings.FindGroup(GroupName);
|
||||||
|
if (group == null)
|
||||||
|
{
|
||||||
|
group = settings.CreateGroup(
|
||||||
|
GroupName,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
typeof(BundledAssetGroupSchema),
|
||||||
|
typeof(ContentUpdateGroupSchema));
|
||||||
|
}
|
||||||
|
|
||||||
|
BundledAssetGroupSchema bundled = group.GetSchema<BundledAssetGroupSchema>();
|
||||||
|
if (bundled != null)
|
||||||
|
{
|
||||||
|
bundled.IncludeInBuild = true;
|
||||||
|
bundled.BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackTogether;
|
||||||
|
EditorUtility.SetDirty(bundled);
|
||||||
|
}
|
||||||
|
|
||||||
|
ContentUpdateGroupSchema update = group.GetSchema<ContentUpdateGroupSchema>();
|
||||||
|
if (update != null)
|
||||||
|
{
|
||||||
|
update.StaticContent = false;
|
||||||
|
EditorUtility.SetDirty(update);
|
||||||
|
}
|
||||||
|
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CreateOrUpdateProfile(
|
||||||
|
AddressableAssetSettings settings,
|
||||||
|
AddressableAssetGroup group,
|
||||||
|
TypographyProfileKind kind,
|
||||||
|
TMP_FontAsset font,
|
||||||
|
IReadOnlyCollection<TMP_FontAsset> fallbacks)
|
||||||
|
{
|
||||||
|
string path = $"{ProfileFolder}/{kind}.asset";
|
||||||
|
TypographyProfile profile = AssetDatabase.LoadAssetAtPath<TypographyProfile>(path);
|
||||||
|
if (profile == null)
|
||||||
|
{
|
||||||
|
profile = ScriptableObject.CreateInstance<TypographyProfile>();
|
||||||
|
AssetDatabase.CreateAsset(profile, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
var styles = new List<TypographyStyle>();
|
||||||
|
foreach (TypographyRole role in Enum.GetValues(typeof(TypographyRole)))
|
||||||
|
{
|
||||||
|
styles.Add(new TypographyStyle(
|
||||||
|
role,
|
||||||
|
font,
|
||||||
|
fallbackFonts: fallbacks,
|
||||||
|
fontSizeMultiplier: 1f));
|
||||||
|
}
|
||||||
|
|
||||||
|
profile.Configure(kind, styles);
|
||||||
|
EditorUtility.SetDirty(profile);
|
||||||
|
|
||||||
|
string guid = AssetDatabase.AssetPathToGUID(path);
|
||||||
|
AddressableAssetEntry entry = settings.CreateOrMoveEntry(guid, group, false, false);
|
||||||
|
entry.address = $"Typography/Profile/{kind}";
|
||||||
|
entry.SetLabel("Typography", true, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EnsureFolder(string path)
|
||||||
|
{
|
||||||
|
string[] segments = path.Split('/');
|
||||||
|
string current = segments[0];
|
||||||
|
for (int i = 1; i < segments.Length; i++)
|
||||||
|
{
|
||||||
|
string next = $"{current}/{segments[i]}";
|
||||||
|
if (!AssetDatabase.IsValidFolder(next))
|
||||||
|
AssetDatabase.CreateFolder(current, segments[i]);
|
||||||
|
current = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a84a91ac052fc3444aa281c7d6a4f29e
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 07d6e97bcfca9a1438f18cb05bb5d75a
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b6b0068deeac87d4b9557c7d5d38a29d
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!114 &11400000
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 0}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 5d25e769249077945915a5c2c793b0e6, type: 3}
|
||||||
|
m_Name: Japanese
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
profileKind: 1
|
||||||
|
styles:
|
||||||
|
- role: 0
|
||||||
|
font: {fileID: 11400000, guid: 1625bd86098eb0843b1a69eb1b3354e4, type: 2}
|
||||||
|
materialPreset: {fileID: 0}
|
||||||
|
fallbackFonts: []
|
||||||
|
fontSizeMultiplier: 1
|
||||||
|
lineSpacingOffset: 0
|
||||||
|
characterSpacingOffset: 0
|
||||||
|
- role: 1
|
||||||
|
font: {fileID: 11400000, guid: c7fc4cbba4b38a04c8985d4dbce12a73, type: 2}
|
||||||
|
materialPreset: {fileID: 0}
|
||||||
|
fallbackFonts: []
|
||||||
|
fontSizeMultiplier: 1
|
||||||
|
lineSpacingOffset: 0
|
||||||
|
characterSpacingOffset: 0
|
||||||
|
- role: 2
|
||||||
|
font: {fileID: 11400000, guid: 1625bd86098eb0843b1a69eb1b3354e4, type: 2}
|
||||||
|
materialPreset: {fileID: 0}
|
||||||
|
fallbackFonts: []
|
||||||
|
fontSizeMultiplier: 1
|
||||||
|
lineSpacingOffset: 0
|
||||||
|
characterSpacingOffset: 0
|
||||||
|
- role: 3
|
||||||
|
font: {fileID: 11400000, guid: 1625bd86098eb0843b1a69eb1b3354e4, type: 2}
|
||||||
|
materialPreset: {fileID: 0}
|
||||||
|
fallbackFonts:
|
||||||
|
- {fileID: 11400000, guid: f242fb3dde1933640859f1c54591c9c8, type: 2}
|
||||||
|
fontSizeMultiplier: 1
|
||||||
|
lineSpacingOffset: 0
|
||||||
|
characterSpacingOffset: 0
|
||||||
|
- role: 4
|
||||||
|
font: {fileID: 11400000, guid: 1625bd86098eb0843b1a69eb1b3354e4, type: 2}
|
||||||
|
materialPreset: {fileID: 0}
|
||||||
|
fallbackFonts:
|
||||||
|
- {fileID: 11400000, guid: f242fb3dde1933640859f1c54591c9c8, type: 2}
|
||||||
|
fontSizeMultiplier: 1
|
||||||
|
lineSpacingOffset: 0
|
||||||
|
characterSpacingOffset: 0
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 67cd590df91846b4d81698d68426e616
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!114 &11400000
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 0}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 5d25e769249077945915a5c2c793b0e6, type: 3}
|
||||||
|
m_Name: Latin
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
profileKind: 2
|
||||||
|
styles:
|
||||||
|
- role: 0
|
||||||
|
font: {fileID: 11400000, guid: 1625bd86098eb0843b1a69eb1b3354e4, type: 2}
|
||||||
|
materialPreset: {fileID: 0}
|
||||||
|
fallbackFonts:
|
||||||
|
- {fileID: 11400000, guid: c7fc4cbba4b38a04c8985d4dbce12a73, type: 2}
|
||||||
|
fontSizeMultiplier: 1
|
||||||
|
lineSpacingOffset: 0
|
||||||
|
characterSpacingOffset: 0
|
||||||
|
- role: 1
|
||||||
|
font: {fileID: 11400000, guid: c7fc4cbba4b38a04c8985d4dbce12a73, type: 2}
|
||||||
|
materialPreset: {fileID: 0}
|
||||||
|
fallbackFonts:
|
||||||
|
- {fileID: 11400000, guid: 1625bd86098eb0843b1a69eb1b3354e4, type: 2}
|
||||||
|
fontSizeMultiplier: 1
|
||||||
|
lineSpacingOffset: 0
|
||||||
|
characterSpacingOffset: 0
|
||||||
|
- role: 2
|
||||||
|
font: {fileID: 11400000, guid: 1625bd86098eb0843b1a69eb1b3354e4, type: 2}
|
||||||
|
materialPreset: {fileID: 0}
|
||||||
|
fallbackFonts:
|
||||||
|
- {fileID: 11400000, guid: c7fc4cbba4b38a04c8985d4dbce12a73, type: 2}
|
||||||
|
fontSizeMultiplier: 1
|
||||||
|
lineSpacingOffset: 0
|
||||||
|
characterSpacingOffset: 0
|
||||||
|
- role: 3
|
||||||
|
font: {fileID: 11400000, guid: 1625bd86098eb0843b1a69eb1b3354e4, type: 2}
|
||||||
|
materialPreset: {fileID: 0}
|
||||||
|
fallbackFonts:
|
||||||
|
- {fileID: 11400000, guid: c7fc4cbba4b38a04c8985d4dbce12a73, type: 2}
|
||||||
|
fontSizeMultiplier: 1
|
||||||
|
lineSpacingOffset: 0
|
||||||
|
characterSpacingOffset: 0
|
||||||
|
- role: 4
|
||||||
|
font: {fileID: 11400000, guid: 1625bd86098eb0843b1a69eb1b3354e4, type: 2}
|
||||||
|
materialPreset: {fileID: 0}
|
||||||
|
fallbackFonts:
|
||||||
|
- {fileID: 11400000, guid: c7fc4cbba4b38a04c8985d4dbce12a73, type: 2}
|
||||||
|
fontSizeMultiplier: 1
|
||||||
|
lineSpacingOffset: 0
|
||||||
|
characterSpacingOffset: 0
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d53382c947b5edc4a88216c5fcf37b38
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!114 &11400000
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 0}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 5d25e769249077945915a5c2c793b0e6, type: 3}
|
||||||
|
m_Name: Russian
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
profileKind: 3
|
||||||
|
styles:
|
||||||
|
- role: 0
|
||||||
|
font: {fileID: 11400000, guid: 1625bd86098eb0843b1a69eb1b3354e4, type: 2}
|
||||||
|
materialPreset: {fileID: 0}
|
||||||
|
fallbackFonts: []
|
||||||
|
fontSizeMultiplier: 1
|
||||||
|
lineSpacingOffset: 0
|
||||||
|
characterSpacingOffset: 0
|
||||||
|
- role: 1
|
||||||
|
font: {fileID: 11400000, guid: c7fc4cbba4b38a04c8985d4dbce12a73, type: 2}
|
||||||
|
materialPreset: {fileID: 0}
|
||||||
|
fallbackFonts: []
|
||||||
|
fontSizeMultiplier: 1
|
||||||
|
lineSpacingOffset: 0
|
||||||
|
characterSpacingOffset: 0
|
||||||
|
- role: 2
|
||||||
|
font: {fileID: 11400000, guid: 1625bd86098eb0843b1a69eb1b3354e4, type: 2}
|
||||||
|
materialPreset: {fileID: 0}
|
||||||
|
fallbackFonts:
|
||||||
|
- {fileID: 11400000, guid: c7fc4cbba4b38a04c8985d4dbce12a73, type: 2}
|
||||||
|
fontSizeMultiplier: 1
|
||||||
|
lineSpacingOffset: 0
|
||||||
|
characterSpacingOffset: 0
|
||||||
|
- role: 3
|
||||||
|
font: {fileID: 11400000, guid: 1625bd86098eb0843b1a69eb1b3354e4, type: 2}
|
||||||
|
materialPreset: {fileID: 0}
|
||||||
|
fallbackFonts:
|
||||||
|
- {fileID: 11400000, guid: c7fc4cbba4b38a04c8985d4dbce12a73, type: 2}
|
||||||
|
fontSizeMultiplier: 1
|
||||||
|
lineSpacingOffset: 0
|
||||||
|
characterSpacingOffset: 0
|
||||||
|
- role: 4
|
||||||
|
font: {fileID: 11400000, guid: 1625bd86098eb0843b1a69eb1b3354e4, type: 2}
|
||||||
|
materialPreset: {fileID: 0}
|
||||||
|
fallbackFonts:
|
||||||
|
- {fileID: 11400000, guid: c7fc4cbba4b38a04c8985d4dbce12a73, type: 2}
|
||||||
|
fontSizeMultiplier: 1
|
||||||
|
lineSpacingOffset: 0
|
||||||
|
characterSpacingOffset: 0
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f82150d4b378dc441884eb47d4f67b7c
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!114 &11400000
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 0}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 5d25e769249077945915a5c2c793b0e6, type: 3}
|
||||||
|
m_Name: ZhHans
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
profileKind: 0
|
||||||
|
styles:
|
||||||
|
- role: 0
|
||||||
|
font: {fileID: 11400000, guid: c7fc4cbba4b38a04c8985d4dbce12a73, type: 2}
|
||||||
|
materialPreset: {fileID: 0}
|
||||||
|
fallbackFonts:
|
||||||
|
- {fileID: 11400000, guid: c7fc4cbba4b38a04c8985d4dbce12a73, type: 2}
|
||||||
|
fontSizeMultiplier: 1
|
||||||
|
lineSpacingOffset: 0
|
||||||
|
characterSpacingOffset: 0
|
||||||
|
- role: 1
|
||||||
|
font: {fileID: 11400000, guid: c7fc4cbba4b38a04c8985d4dbce12a73, type: 2}
|
||||||
|
materialPreset: {fileID: 0}
|
||||||
|
fallbackFonts:
|
||||||
|
- {fileID: 11400000, guid: c7fc4cbba4b38a04c8985d4dbce12a73, type: 2}
|
||||||
|
fontSizeMultiplier: 1
|
||||||
|
lineSpacingOffset: 0
|
||||||
|
characterSpacingOffset: 0
|
||||||
|
- role: 2
|
||||||
|
font: {fileID: 11400000, guid: 1625bd86098eb0843b1a69eb1b3354e4, type: 2}
|
||||||
|
materialPreset: {fileID: 0}
|
||||||
|
fallbackFonts:
|
||||||
|
- {fileID: 11400000, guid: c7fc4cbba4b38a04c8985d4dbce12a73, type: 2}
|
||||||
|
fontSizeMultiplier: 1
|
||||||
|
lineSpacingOffset: 0
|
||||||
|
characterSpacingOffset: 0
|
||||||
|
- role: 3
|
||||||
|
font: {fileID: 11400000, guid: 1625bd86098eb0843b1a69eb1b3354e4, type: 2}
|
||||||
|
materialPreset: {fileID: 0}
|
||||||
|
fallbackFonts:
|
||||||
|
- {fileID: 11400000, guid: c7fc4cbba4b38a04c8985d4dbce12a73, type: 2}
|
||||||
|
fontSizeMultiplier: 1
|
||||||
|
lineSpacingOffset: 0
|
||||||
|
characterSpacingOffset: 0
|
||||||
|
- role: 4
|
||||||
|
font: {fileID: 11400000, guid: 1625bd86098eb0843b1a69eb1b3354e4, type: 2}
|
||||||
|
materialPreset: {fileID: 0}
|
||||||
|
fallbackFonts:
|
||||||
|
- {fileID: 11400000, guid: c7fc4cbba4b38a04c8985d4dbce12a73, type: 2}
|
||||||
|
fontSizeMultiplier: 1
|
||||||
|
lineSpacingOffset: 0
|
||||||
|
characterSpacingOffset: 0
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e41dce1e1d6e0564b95054b5c8b8bb4c
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using AibisDream.Utility;
|
using AibisDream.Utility;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
@@ -11,22 +12,14 @@ using UnityEditor;
|
|||||||
|
|
||||||
namespace AibisDream.Framework
|
namespace AibisDream.Framework
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// csv 本地化列类别。集中声明"哪些 Locale 归到哪一列",csv 数据类按此枚举取字段。
|
|
||||||
/// </summary>
|
|
||||||
public enum LocaleKind
|
|
||||||
{
|
|
||||||
Cn,
|
|
||||||
En,
|
|
||||||
Ja,
|
|
||||||
Ru,
|
|
||||||
Es,
|
|
||||||
PtBr
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class LocalizationKit
|
public static class LocalizationKit
|
||||||
{
|
{
|
||||||
private const string LocalizationPrefix = "l10n.";
|
private const string LocalizationPrefix = "l10n.";
|
||||||
|
private static Task<bool> currentLanguageSwitchTask;
|
||||||
|
private static int languageSwitchVersion;
|
||||||
|
private static bool managedLocaleChange;
|
||||||
|
private static bool typographyLoaderConfigured;
|
||||||
|
private static bool localeChangedSubscribed;
|
||||||
|
|
||||||
public static string CurrentLanguageCode { get; private set; } = "zh-Hans";
|
public static string CurrentLanguageCode { get; private set; } = "zh-Hans";
|
||||||
public static Locale CurrentLocale => LocalizationSettings.SelectedLocale;
|
public static Locale CurrentLocale => LocalizationSettings.SelectedLocale;
|
||||||
@@ -78,8 +71,14 @@ namespace AibisDream.Framework
|
|||||||
{
|
{
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
EditorApplication.playModeStateChanged += OnPlayModeChanged;
|
EditorApplication.playModeStateChanged += OnPlayModeChanged;
|
||||||
|
if (EditorApplication.isPlayingOrWillChangePlaymode)
|
||||||
|
{
|
||||||
|
EnsureTypographyLoaderConfigured();
|
||||||
|
SubscribeLocaleChanged();
|
||||||
|
RefreshCurrentLanguageCodeAsync();
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
LocalizationSettings.SelectedLocaleChanged += OnLocaleChanged;
|
SubscribeLocaleChanged();
|
||||||
RefreshCurrentLanguageCodeAsync();
|
RefreshCurrentLanguageCodeAsync();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -88,6 +87,14 @@ namespace AibisDream.Framework
|
|||||||
{
|
{
|
||||||
UpdateCurrentLanguageCode(locale);
|
UpdateCurrentLanguageCode(locale);
|
||||||
EnumEventSystem.Global.Send(SettingChangeEvent.LocaleChanged, locale);
|
EnumEventSystem.Global.Send(SettingChangeEvent.LocaleChanged, locale);
|
||||||
|
|
||||||
|
if (!managedLocaleChange && locale != null)
|
||||||
|
{
|
||||||
|
EnsureTypographyLoaderConfigured();
|
||||||
|
LocaleKind localeKind = GetLocaleKind(locale.Identifier.Code);
|
||||||
|
if (!TypographyService.IsActiveFor(localeKind))
|
||||||
|
currentLanguageSwitchTask = TypographyService.LoadAndActivateAsync(localeKind);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void UpdateCurrentLanguageCode(Locale locale)
|
private static void UpdateCurrentLanguageCode(Locale locale)
|
||||||
@@ -109,32 +116,95 @@ namespace AibisDream.Framework
|
|||||||
{
|
{
|
||||||
if (playMode == PlayModeStateChange.EnteredPlayMode)
|
if (playMode == PlayModeStateChange.EnteredPlayMode)
|
||||||
{
|
{
|
||||||
LocalizationSettings.SelectedLocaleChanged += OnLocaleChanged;
|
EnsureTypographyLoaderConfigured();
|
||||||
|
SubscribeLocaleChanged();
|
||||||
RefreshCurrentLanguageCodeAsync();
|
RefreshCurrentLanguageCodeAsync();
|
||||||
}
|
}
|
||||||
else if (playMode == PlayModeStateChange.ExitingPlayMode)
|
else if (playMode == PlayModeStateChange.ExitingPlayMode)
|
||||||
{
|
{
|
||||||
LocalizationSettings.SelectedLocaleChanged -= OnLocaleChanged;
|
UnsubscribeLocaleChanged();
|
||||||
|
TypographyService.ResetForTests();
|
||||||
|
typographyLoaderConfigured = false;
|
||||||
|
currentLanguageSwitchTask = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
public static async void SwitchLanguage(string localeCode)
|
public static void SwitchLanguage(string localeCode)
|
||||||
|
{
|
||||||
|
_ = SwitchLanguageAsync(localeCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task<bool> SwitchLanguageAsync(string localeCode)
|
||||||
|
{
|
||||||
|
int requestVersion = Interlocked.Increment(ref languageSwitchVersion);
|
||||||
|
currentLanguageSwitchTask = SwitchLanguageCoreAsync(localeCode, requestVersion);
|
||||||
|
return currentLanguageSwitchTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<bool> SwitchLanguageCoreAsync(string localeCode, int requestVersion)
|
||||||
{
|
{
|
||||||
// 等待本地化系统初始化完成
|
|
||||||
await LocalizationSettings.InitializationOperation.Task;
|
await LocalizationSettings.InitializationOperation.Task;
|
||||||
|
|
||||||
var selectedLocale =
|
var selectedLocale =
|
||||||
LocalizationSettings.AvailableLocales.Locales.Find(locale => locale.Identifier.Code == localeCode);
|
LocalizationSettings.AvailableLocales.Locales.Find(locale => locale.Identifier.Code == localeCode);
|
||||||
|
if (!selectedLocale)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"Locale with code {localeCode} not found.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (selectedLocale)
|
EnsureTypographyLoaderConfigured();
|
||||||
|
bool typographyReady = await TypographyService.LoadAndActivateAsync(GetLocaleKind(localeCode));
|
||||||
|
if (requestVersion != languageSwitchVersion)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
managedLocaleChange = true;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
LocalizationSettings.SelectedLocale = selectedLocale;
|
LocalizationSettings.SelectedLocale = selectedLocale;
|
||||||
}
|
}
|
||||||
else
|
finally
|
||||||
{
|
{
|
||||||
Debug.LogWarning($"Locale with code {localeCode} not found.");
|
managedLocaleChange = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await Task.Yield();
|
||||||
|
Canvas.ForceUpdateCanvases();
|
||||||
|
if (!typographyReady)
|
||||||
|
{
|
||||||
|
Debug.LogWarning(
|
||||||
|
$"[LocalizationKit] Locale 已切换为 {localeCode},但字体 Profile 加载失败,保留上一套字体。");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EnsureTypographyLoaderConfigured()
|
||||||
|
{
|
||||||
|
if (typographyLoaderConfigured)
|
||||||
|
return;
|
||||||
|
|
||||||
|
TypographyService.ConfigureLoader(new ResourceSystemTypographyProfileLoader());
|
||||||
|
typographyLoaderConfigured = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SubscribeLocaleChanged()
|
||||||
|
{
|
||||||
|
if (localeChangedSubscribed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
LocalizationSettings.SelectedLocaleChanged += OnLocaleChanged;
|
||||||
|
localeChangedSubscribed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void UnsubscribeLocaleChanged()
|
||||||
|
{
|
||||||
|
if (!localeChangedSubscribed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
LocalizationSettings.SelectedLocaleChanged -= OnLocaleChanged;
|
||||||
|
localeChangedSubscribed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -250,6 +320,24 @@ namespace AibisDream.Framework
|
|||||||
public static IEnumerator WaitUntilReady()
|
public static IEnumerator WaitUntilReady()
|
||||||
{
|
{
|
||||||
yield return LocalizationSettings.InitializationOperation;
|
yield return LocalizationSettings.InitializationOperation;
|
||||||
|
|
||||||
|
Task<bool> languageTask;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
languageTask = currentLanguageSwitchTask;
|
||||||
|
while (languageTask != null && !languageTask.IsCompleted)
|
||||||
|
yield return null;
|
||||||
|
} while (languageTask != currentLanguageSwitchTask);
|
||||||
|
|
||||||
|
EnsureTypographyLoaderConfigured();
|
||||||
|
if (!TypographyService.IsReady)
|
||||||
|
{
|
||||||
|
Task<bool> typographyTask = TypographyService.LoadAndActivateAsync(
|
||||||
|
GetLocaleKind(LocalizationSettings.SelectedLocale?.Identifier.Code));
|
||||||
|
while (!typographyTask.IsCompleted)
|
||||||
|
yield return null;
|
||||||
|
}
|
||||||
|
|
||||||
yield return null;
|
yield return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||||
|
|
||||||
|
namespace AibisDream.Framework
|
||||||
|
{
|
||||||
|
internal sealed class ResourceSystemTypographyProfileLoader : ITypographyProfileLoader
|
||||||
|
{
|
||||||
|
public async Task<TypographyProfile> LoadAsync(string key)
|
||||||
|
{
|
||||||
|
AsyncOperationHandle<TypographyProfile> handle =
|
||||||
|
ResourceSystem.LoadPersistentAsync<TypographyProfile>(key);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await handle.Task;
|
||||||
|
if (handle.Status == AsyncOperationStatus.Succeeded && handle.Result != null)
|
||||||
|
return handle.Result;
|
||||||
|
|
||||||
|
ResourceSystem.EarlyReleasePersistent(key);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
ResourceSystem.EarlyReleasePersistent(key);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Release(string key)
|
||||||
|
{
|
||||||
|
ResourceSystem.EarlyReleasePersistent(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c9dd51810ef0cc94398175f3597a978e
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -140,6 +140,15 @@ namespace AibisDream.Framework
|
|||||||
public static void EarlyRelease<T>(AsyncOperationHandle<T> handle)
|
public static void EarlyRelease<T>(AsyncOperationHandle<T> handle)
|
||||||
=> ActiveLoader.EarlyRelease(handle);
|
=> ActiveLoader.EarlyRelease(handle);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 提前释放持久化 Loader 中指定 Key 的资源。
|
||||||
|
/// </summary>
|
||||||
|
public static void EarlyReleasePersistent(string key)
|
||||||
|
{
|
||||||
|
if (_persistentLoader != null)
|
||||||
|
_persistentLoader.EarlyRelease(key);
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2964c7221d2d9d44aaa0da470729509b
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"name": "AibisDream.Typography.Runtime",
|
||||||
|
"rootNamespace": "AibisDream.Framework",
|
||||||
|
"references": [
|
||||||
|
"Unity.TextMeshPro"
|
||||||
|
],
|
||||||
|
"includePlatforms": [],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": false
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 118b0de59b1eb6e438aec021b8e3f39b
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace AibisDream.Framework
|
||||||
|
{
|
||||||
|
public interface ITypographyProfileLoader
|
||||||
|
{
|
||||||
|
Task<TypographyProfile> LoadAsync(string key);
|
||||||
|
void Release(string key);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0de13fe1f11da524abe267418428556b
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace AibisDream.Framework
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 项目支持的本地化语言类别。多个 Locale 可以共享同一套排版资源。
|
||||||
|
/// </summary>
|
||||||
|
public enum LocaleKind
|
||||||
|
{
|
||||||
|
Cn,
|
||||||
|
En,
|
||||||
|
Ja,
|
||||||
|
Ru,
|
||||||
|
Es,
|
||||||
|
PtBr
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum TypographyProfileKind
|
||||||
|
{
|
||||||
|
ZhHans,
|
||||||
|
Japanese,
|
||||||
|
Latin,
|
||||||
|
Russian
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class TypographyLocaleMapping
|
||||||
|
{
|
||||||
|
public static TypographyProfileKind GetProfileKind(LocaleKind localeKind) => localeKind switch
|
||||||
|
{
|
||||||
|
LocaleKind.Cn => TypographyProfileKind.ZhHans,
|
||||||
|
LocaleKind.Ja => TypographyProfileKind.Japanese,
|
||||||
|
LocaleKind.Ru => TypographyProfileKind.Russian,
|
||||||
|
LocaleKind.En or LocaleKind.Es or LocaleKind.PtBr => TypographyProfileKind.Latin,
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(localeKind), localeKind, null)
|
||||||
|
};
|
||||||
|
|
||||||
|
public static string GetProfileKey(LocaleKind localeKind) =>
|
||||||
|
$"Typography/Profile/{GetProfileKind(localeKind)}";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 65c00ba464255484193f0350427b5e11
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
using TMPro;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace AibisDream.Framework
|
||||||
|
{
|
||||||
|
[DisallowMultipleComponent]
|
||||||
|
[RequireComponent(typeof(TMP_Text))]
|
||||||
|
public sealed class LocalizedTypography : MonoBehaviour
|
||||||
|
{
|
||||||
|
[SerializeField] private TypographyRole role = TypographyRole.Body;
|
||||||
|
[SerializeField] private bool applyMetrics = true;
|
||||||
|
[SerializeField, HideInInspector] private bool baselineCaptured;
|
||||||
|
[SerializeField, HideInInspector] private float baseFontSize;
|
||||||
|
[SerializeField, HideInInspector] private float baseLineSpacing;
|
||||||
|
[SerializeField, HideInInspector] private float baseCharacterSpacing;
|
||||||
|
|
||||||
|
private TMP_Text target;
|
||||||
|
|
||||||
|
public TypographyRole Role => role;
|
||||||
|
public TMP_Text Target => target != null ? target : target = GetComponent<TMP_Text>();
|
||||||
|
|
||||||
|
private void Reset()
|
||||||
|
{
|
||||||
|
target = GetComponent<TMP_Text>();
|
||||||
|
CaptureBaseline();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Awake()
|
||||||
|
{
|
||||||
|
target = GetComponent<TMP_Text>();
|
||||||
|
// Prefab variants can override TMP metrics without overriding the
|
||||||
|
// baseline serialized by the source prefab. Capture the fully
|
||||||
|
// resolved instance values before the first profile is applied.
|
||||||
|
CaptureBaseline();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEnable()
|
||||||
|
{
|
||||||
|
if (!baselineCaptured)
|
||||||
|
CaptureBaseline();
|
||||||
|
TypographyService.Register(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDisable()
|
||||||
|
{
|
||||||
|
TypographyService.Unregister(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ApplyProfile(TypographyProfile profile)
|
||||||
|
{
|
||||||
|
TMP_Text text = Target;
|
||||||
|
TypographyStyle style = profile != null ? profile.Resolve(role) : null;
|
||||||
|
if (text == null || style == null || !style.IsUsable)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!baselineCaptured)
|
||||||
|
CaptureBaseline();
|
||||||
|
TypographyService.ApplyStyle(
|
||||||
|
text,
|
||||||
|
style,
|
||||||
|
applyMetrics,
|
||||||
|
baseFontSize,
|
||||||
|
baseLineSpacing,
|
||||||
|
baseCharacterSpacing);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CaptureBaseline()
|
||||||
|
{
|
||||||
|
TMP_Text text = Target;
|
||||||
|
if (text == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
baseFontSize = text.fontSize;
|
||||||
|
baseLineSpacing = text.lineSpacing;
|
||||||
|
baseCharacterSpacing = text.characterSpacing;
|
||||||
|
baselineCaptured = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
public void ConfigureForEditor(TypographyRole configuredRole, bool shouldApplyMetrics = true)
|
||||||
|
{
|
||||||
|
role = configuredRole;
|
||||||
|
applyMetrics = shouldApplyMetrics;
|
||||||
|
CaptureBaseline();
|
||||||
|
UnityEditor.EditorUtility.SetDirty(this);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9fe642db0d3d2b54fa6d1afaf8c85e4a
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using TMPro;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace AibisDream.Framework
|
||||||
|
{
|
||||||
|
[CreateAssetMenu(fileName = "TypographyProfile", menuName = "AIBIS Dream/Typography/Profile")]
|
||||||
|
public sealed class TypographyProfile : ScriptableObject
|
||||||
|
{
|
||||||
|
[SerializeField] private TypographyProfileKind profileKind;
|
||||||
|
[SerializeField] private List<TypographyStyle> styles = new();
|
||||||
|
|
||||||
|
public TypographyProfileKind ProfileKind => profileKind;
|
||||||
|
public IReadOnlyList<TypographyStyle> Styles => styles;
|
||||||
|
|
||||||
|
public TypographyStyle Resolve(TypographyRole role)
|
||||||
|
{
|
||||||
|
TypographyStyle body = null;
|
||||||
|
if (styles == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
foreach (TypographyStyle style in styles)
|
||||||
|
{
|
||||||
|
if (style == null || !style.IsUsable)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (style.Role == role)
|
||||||
|
return style;
|
||||||
|
if (style.Role == TypographyRole.Body)
|
||||||
|
body = style;
|
||||||
|
}
|
||||||
|
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<string> GetValidationErrors()
|
||||||
|
{
|
||||||
|
if (styles == null || styles.Count == 0)
|
||||||
|
{
|
||||||
|
yield return $"{name}: 没有配置任何 TypographyStyle。";
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var seenRoles = new HashSet<TypographyRole>();
|
||||||
|
foreach (TypographyStyle style in styles)
|
||||||
|
{
|
||||||
|
if (style == null)
|
||||||
|
{
|
||||||
|
yield return $"{name}: 存在空的 TypographyStyle。";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!seenRoles.Add(style.Role))
|
||||||
|
yield return $"{name}: Role {style.Role} 重复配置。";
|
||||||
|
if (!style.IsUsable)
|
||||||
|
yield return $"{name}: Role {style.Role} 没有字体。";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!seenRoles.Contains(TypographyRole.Body))
|
||||||
|
yield return $"{name}: 缺少 Body 回退样式。";
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Configure(TypographyProfileKind kind, IEnumerable<TypographyStyle> configuredStyles)
|
||||||
|
{
|
||||||
|
profileKind = kind;
|
||||||
|
styles = configuredStyles != null
|
||||||
|
? new List<TypographyStyle>(configuredStyles)
|
||||||
|
: new List<TypographyStyle>();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void ApplyFallbacks()
|
||||||
|
{
|
||||||
|
if (styles == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var configuredFonts = new HashSet<TMP_FontAsset>();
|
||||||
|
foreach (TypographyStyle style in styles)
|
||||||
|
{
|
||||||
|
if (style?.Font != null && configuredFonts.Add(style.Font))
|
||||||
|
style.ApplyFallbacks();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5d25e769249077945915a5c2c793b0e6
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
namespace AibisDream.Framework
|
||||||
|
{
|
||||||
|
public enum TypographyRole
|
||||||
|
{
|
||||||
|
Body,
|
||||||
|
Dialogue,
|
||||||
|
Terminal,
|
||||||
|
WorldText,
|
||||||
|
MiniGame
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e847b9aaee116be4c8d8d135ae0defb9
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,179 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using TMPro;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace AibisDream.Framework
|
||||||
|
{
|
||||||
|
public static class TypographyService
|
||||||
|
{
|
||||||
|
private static readonly HashSet<LocalizedTypography> Bindings = new();
|
||||||
|
private static ITypographyProfileLoader loader;
|
||||||
|
private static int switchVersion;
|
||||||
|
private static string currentProfileKey;
|
||||||
|
private static string latestRequestedProfileKey;
|
||||||
|
private static TypographyProfile currentProfile;
|
||||||
|
|
||||||
|
public static TypographyProfile CurrentProfile => currentProfile;
|
||||||
|
public static bool IsReady => currentProfile != null;
|
||||||
|
public static string CurrentProfileKey => currentProfileKey;
|
||||||
|
|
||||||
|
public static void ConfigureLoader(ITypographyProfileLoader profileLoader)
|
||||||
|
{
|
||||||
|
loader = profileLoader ?? throw new ArgumentNullException(nameof(profileLoader));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsActiveFor(LocaleKind localeKind) =>
|
||||||
|
currentProfile != null &&
|
||||||
|
string.Equals(
|
||||||
|
currentProfileKey,
|
||||||
|
TypographyLocaleMapping.GetProfileKey(localeKind),
|
||||||
|
StringComparison.Ordinal);
|
||||||
|
|
||||||
|
public static TypographyStyle Resolve(TypographyRole role) => currentProfile?.Resolve(role);
|
||||||
|
|
||||||
|
public static bool Apply(TMP_Text text, TypographyRole role, bool applyMetrics = true)
|
||||||
|
{
|
||||||
|
if (text == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
TypographyStyle style = Resolve(role);
|
||||||
|
if (style == null || !style.IsUsable)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ApplyStyle(text, style, applyMetrics, text.fontSize, text.lineSpacing, text.characterSpacing);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Register(LocalizedTypography binding)
|
||||||
|
{
|
||||||
|
if (binding == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Bindings.Add(binding);
|
||||||
|
if (currentProfile != null)
|
||||||
|
binding.ApplyProfile(currentProfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Unregister(LocalizedTypography binding)
|
||||||
|
{
|
||||||
|
if (binding != null)
|
||||||
|
Bindings.Remove(binding);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<bool> LoadAndActivateAsync(LocaleKind localeKind)
|
||||||
|
{
|
||||||
|
int requestVersion = ++switchVersion;
|
||||||
|
string profileKey = TypographyLocaleMapping.GetProfileKey(localeKind);
|
||||||
|
latestRequestedProfileKey = profileKey;
|
||||||
|
|
||||||
|
if (IsActiveFor(localeKind))
|
||||||
|
return true;
|
||||||
|
if (loader == null)
|
||||||
|
{
|
||||||
|
Debug.LogError("[TypographyService] 尚未配置 ITypographyProfileLoader。");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TypographyProfile loadedProfile;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
loadedProfile = await loader.LoadAsync(profileKey);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Debug.LogError($"[TypographyService] 加载字体 Profile 失败:{profileKey}\n{exception}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requestVersion != switchVersion)
|
||||||
|
{
|
||||||
|
if (!string.Equals(profileKey, currentProfileKey, StringComparison.Ordinal) &&
|
||||||
|
!string.Equals(profileKey, latestRequestedProfileKey, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
loader.Release(profileKey);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (loadedProfile == null)
|
||||||
|
{
|
||||||
|
Debug.LogError($"[TypographyService] 字体 Profile 不存在或加载失败:{profileKey}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
string previousKey = currentProfileKey;
|
||||||
|
loadedProfile.ApplyFallbacks();
|
||||||
|
currentProfile = loadedProfile;
|
||||||
|
currentProfileKey = profileKey;
|
||||||
|
RefreshBindings();
|
||||||
|
Canvas.ForceUpdateCanvases();
|
||||||
|
|
||||||
|
await Task.Yield();
|
||||||
|
if (!string.IsNullOrEmpty(previousKey) &&
|
||||||
|
!string.Equals(previousKey, profileKey, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
loader.Release(previousKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void ApplyStyle(
|
||||||
|
TMP_Text text,
|
||||||
|
TypographyStyle style,
|
||||||
|
bool applyMetrics,
|
||||||
|
float baseFontSize,
|
||||||
|
float baseLineSpacing,
|
||||||
|
float baseCharacterSpacing)
|
||||||
|
{
|
||||||
|
text.font = style.Font;
|
||||||
|
text.fontSharedMaterial = style.MaterialPreset != null
|
||||||
|
? style.MaterialPreset
|
||||||
|
: style.Font.material;
|
||||||
|
|
||||||
|
if (applyMetrics)
|
||||||
|
{
|
||||||
|
text.fontSize = baseFontSize * style.FontSizeMultiplier;
|
||||||
|
text.lineSpacing = baseLineSpacing + style.LineSpacingOffset;
|
||||||
|
text.characterSpacing = baseCharacterSpacing + style.CharacterSpacingOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
text.SetAllDirty();
|
||||||
|
if (text.gameObject.activeInHierarchy)
|
||||||
|
text.ForceMeshUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RefreshBindings()
|
||||||
|
{
|
||||||
|
if (Bindings.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var stale = new List<LocalizedTypography>();
|
||||||
|
foreach (LocalizedTypography binding in Bindings)
|
||||||
|
{
|
||||||
|
if (binding == null)
|
||||||
|
stale.Add(binding);
|
||||||
|
else
|
||||||
|
binding.ApplyProfile(currentProfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (LocalizedTypography binding in stale)
|
||||||
|
Bindings.Remove(binding);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
public static void ResetForTests()
|
||||||
|
{
|
||||||
|
if (loader != null && !string.IsNullOrEmpty(currentProfileKey))
|
||||||
|
loader.Release(currentProfileKey);
|
||||||
|
loader = null;
|
||||||
|
currentProfile = null;
|
||||||
|
currentProfileKey = null;
|
||||||
|
latestRequestedProfileKey = null;
|
||||||
|
switchVersion++;
|
||||||
|
Bindings.Clear();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9c15158900e46184da7832703b02d0a1
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using TMPro;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace AibisDream.Framework
|
||||||
|
{
|
||||||
|
[Serializable]
|
||||||
|
public sealed class TypographyStyle
|
||||||
|
{
|
||||||
|
[SerializeField] private TypographyRole role;
|
||||||
|
[SerializeField] private TMP_FontAsset font;
|
||||||
|
[SerializeField] private Material materialPreset;
|
||||||
|
[SerializeField] private List<TMP_FontAsset> fallbackFonts = new();
|
||||||
|
[SerializeField, Min(0.01f)] private float fontSizeMultiplier = 1f;
|
||||||
|
[SerializeField] private float lineSpacingOffset;
|
||||||
|
[SerializeField] private float characterSpacingOffset;
|
||||||
|
|
||||||
|
public TypographyStyle(
|
||||||
|
TypographyRole role,
|
||||||
|
TMP_FontAsset font,
|
||||||
|
Material materialPreset = null,
|
||||||
|
IEnumerable<TMP_FontAsset> fallbackFonts = null,
|
||||||
|
float fontSizeMultiplier = 1f,
|
||||||
|
float lineSpacingOffset = 0f,
|
||||||
|
float characterSpacingOffset = 0f)
|
||||||
|
{
|
||||||
|
this.role = role;
|
||||||
|
this.font = font;
|
||||||
|
this.materialPreset = materialPreset;
|
||||||
|
this.fallbackFonts = fallbackFonts != null
|
||||||
|
? new List<TMP_FontAsset>(fallbackFonts)
|
||||||
|
: new List<TMP_FontAsset>();
|
||||||
|
this.fontSizeMultiplier = Mathf.Max(0.01f, fontSizeMultiplier);
|
||||||
|
this.lineSpacingOffset = lineSpacingOffset;
|
||||||
|
this.characterSpacingOffset = characterSpacingOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TypographyRole Role => role;
|
||||||
|
public TMP_FontAsset Font => font;
|
||||||
|
public Material MaterialPreset => materialPreset;
|
||||||
|
public IReadOnlyList<TMP_FontAsset> FallbackFonts => fallbackFonts;
|
||||||
|
public float FontSizeMultiplier => Mathf.Max(0.01f, fontSizeMultiplier);
|
||||||
|
public float LineSpacingOffset => lineSpacingOffset;
|
||||||
|
public float CharacterSpacingOffset => characterSpacingOffset;
|
||||||
|
public bool IsUsable => font != null;
|
||||||
|
|
||||||
|
internal void ApplyFallbacks()
|
||||||
|
{
|
||||||
|
if (font == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
font.fallbackFontAssetTable ??= new List<TMP_FontAsset>();
|
||||||
|
font.fallbackFontAssetTable.Clear();
|
||||||
|
if (fallbackFonts == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (TMP_FontAsset fallback in fallbackFonts)
|
||||||
|
{
|
||||||
|
if (fallback != null && fallback != font && !font.fallbackFontAssetTable.Contains(fallback))
|
||||||
|
font.fallbackFontAssetTable.Add(fallback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 50206284a91c06448b41453dbf51dc44
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -83,7 +83,7 @@ namespace AibisDream.UI
|
|||||||
|
|
||||||
private void PrepareContent()
|
private void PrepareContent()
|
||||||
{
|
{
|
||||||
_owner?.SetHeader("terminal_header_chapter", "REPAIR_STATION_07 - AUTH_VERIFIED");
|
_owner?.SetHeader("terminal_header_chapter", "AUTH_VERIFIED");
|
||||||
LoadChapters();
|
LoadChapters();
|
||||||
RefreshViews();
|
RefreshViews();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ namespace AibisDream.UI
|
|||||||
|
|
||||||
public void Show()
|
public void Show()
|
||||||
{
|
{
|
||||||
_owner?.SetHeader("termnial_header_record", "REPAIR_STATION_07 - AUTH_VERIFIED");
|
_owner?.SetHeader("termnial_header_record", "AUTH_VERIFIED");
|
||||||
if (panelAnimator != null)
|
if (panelAnimator != null)
|
||||||
{
|
{
|
||||||
panelAnimator.Show(onComplete: () => StartCoroutine(DrawRecordsWhenReady()));
|
panelAnimator.Show(onComplete: () => StartCoroutine(DrawRecordsWhenReady()));
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ namespace AibisDream.UI
|
|||||||
new UIFormOption { prop = "ja-JP", label = "日本語" },
|
new UIFormOption { prop = "ja-JP", label = "日本語" },
|
||||||
new UIFormOption { prop = "ru", label = "Русский" },
|
new UIFormOption { prop = "ru", label = "Русский" },
|
||||||
new UIFormOption { prop = "es", label = "Español" },
|
new UIFormOption { prop = "es", label = "Español" },
|
||||||
new UIFormOption { prop = "pt-BR", label = "Português (Brasil)" },
|
new UIFormOption { prop = "pt-BR", label = "Português" },
|
||||||
};
|
};
|
||||||
|
|
||||||
private static readonly UIFormOption[] TextSpeedOptions =
|
private static readonly UIFormOption[] TextSpeedOptions =
|
||||||
@@ -115,7 +115,7 @@ namespace AibisDream.UI
|
|||||||
|
|
||||||
private void PrepareContent()
|
private void PrepareContent()
|
||||||
{
|
{
|
||||||
_owner?.SetHeader("terminal_header_setting", "REPAIR_STATION_07 - AUTH_VERIFIED");
|
_owner?.SetHeader("terminal_header_setting", "AUTH_VERIFIED");
|
||||||
InitControls();
|
InitControls();
|
||||||
RefreshControls();
|
RefreshControls();
|
||||||
RefreshFooterButtons();
|
RefreshFooterButtons();
|
||||||
|
|||||||
@@ -49,11 +49,11 @@ namespace AibisDream.UI
|
|||||||
|
|
||||||
if (panelAnimator != null)
|
if (panelAnimator != null)
|
||||||
{
|
{
|
||||||
panelAnimator.Show(prepare: () => _owner?.SetHeader("termnial_header_start", "REPAIR_STATION_07 - AUTH_VERIFIED"));
|
panelAnimator.Show(prepare: () => _owner?.SetHeader("termnial_header_start", "AUTH_VERIFIED"));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_owner?.SetHeader("termnial_header_start", "REPAIR_STATION_07 - AUTH_VERIFIED");
|
_owner?.SetHeader("termnial_header_start", "AUTH_VERIFIED");
|
||||||
gameObject.SetActive(true);
|
gameObject.SetActive(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,77 +1,76 @@
|
|||||||
通过key索引配置,中文名,英文名,日文名,俄文名,西班牙文名,巴西葡萄牙文名,"显示位置,Default使用默认,Screen插线屏幕,OldBox基本对话框",人物角色(Player/MainActor/Aside),名称显示时的颜色,头像Pic路径,语音资源路径,气泡序号(默认0)
|
通过key索引配置,中文名,英文名,日文名,俄文名,西班牙文名,巴西葡萄牙文名,"显示位置,Default使用默认,Screen插线屏幕,OldBox基本对话框",人物角色(Player/MainActor/Aside),名称显示时的颜色,头像Pic路径,语音资源路径,气泡序号(默认0)
|
||||||
Key,Cn,En,Ja,Ru,Es,PtBr,DialogViewType,Role,NameColor,HeadPicPath,VoicePath,BubbleIdx
|
Key,Cn,En,Ja,Ru,Es,PtBr,DialogViewType,Role,NameColor,HeadPicPath,VoicePath,BubbleIdx
|
||||||
jg,警告,Warning,Warning,,,,Default,MainActor,#7ae622,,VO/voice_DN,
|
jg,警告,Warning,警告,Предупреждение,Advertencia,Aviso,Default,MainActor,#7ae622,,VO/voice_DN,
|
||||||
门,门,Door,,,,,Default,MainActor,#7ae622,,,
|
门,门,Door,ドア,Дверь,Puerta,Porta,Default,MainActor,#7ae622,,,
|
||||||
矩形,黑暗,Darkness,,,,,Default,MainActor,#7ae622,,VO/voice_DN,
|
矩形,黑暗,Darkness,闇,Тьма,Oscuridad,Escuridão,Default,MainActor,#7ae622,,VO/voice_DN,
|
||||||
记忆,记忆,Memory,,,,,Default,MainActor,#7ae622,,,
|
记忆,记忆,Memory,記憶,Память,Memoria,Memória,Default,MainActor,#7ae622,,,
|
||||||
peipei,佩佩,Peipei,ペイペイ,,,,Default,MainActor,#00FFFF,peipei_head,VO/voice_PP,
|
peipei,佩佩,PeiPei,ペペ,Пэйпэй,Peipei,Peipei,Default,MainActor,#00FFFF,peipei_head,VO/voice_PP,
|
||||||
乌鸦,乌鸦,Crow,,,,,Default,MainActor,#e6ddc1,,VO/voice_WYd,
|
乌鸦,乌鸦,Crow,カラス,Ворон,Cuervo,Corvo,Default,MainActor,#e6ddc1,,VO/voice_WYd,
|
||||||
wyd?,???,???,???,,,,Default,MainActor,#e6ddc1,,VO/voice_WYd,
|
wyd?,???,???,???,???,???,???,Default,MainActor,#e6ddc1,,VO/voice_WYd,
|
||||||
马尾辫女孩,马尾辫女孩,Girl with ponytail,,,,,Default,MainActor,,,VO/voice_JJ,
|
马尾辫女孩,马尾辫女孩,Ponytail Girl,ポニーテールの女の子,Девочка с хвостиком,Chica de la coleta,Garota de Rabo de Cavalo,Default,MainActor,,,VO/voice_JJ,
|
||||||
pp,佩佩,Peipei,ペイペイ,,,,Default,MainActor,#00FFFF,peipei_head,VO/voice_PP,
|
pp,佩佩,PeiPei,ペペ,Пэйпэй,Peipei,Peipei,Default,MainActor,#00FFFF,peipei_head,VO/voice_PP,
|
||||||
ppm,佩佩,Peipei,ペイペイ,,,,Default,MainActor,#00FFFF,peipei_head,VO/voice_PPm,
|
ppm,佩佩,PeiPei,ペペ,Пэйпэй,Peipei,Peipei,Default,MainActor,#00FFFF,peipei_head,VO/voice_PPm,
|
||||||
ppd,女孩,Girl,,,,,Default,MainActor,,,VO/voice_PPd,
|
ppd,女孩,Girl,女の子,Девочка,Niña,Garota,Default,MainActor,,,VO/voice_PPd,
|
||||||
ppd?,女孩,Girl,,,,,Default,MainActor,,,VO/voice_PPd,
|
ppd?,女孩,Girl,女の子,Девочка,Niña,Garota,Default,MainActor,,,VO/voice_PPd,
|
||||||
me,路斯,Luth,ルース,,,,Default,Player,,me_head,VO/voice_ME,
|
me,路斯,Ruth,ルース,Лус,Ruth,Lusi,Default,Player,,me_head,VO/voice_ME,
|
||||||
med,路斯,Luth,ルース,,,,Default,Player,,me_head,VO/voice_MEd,
|
med,路斯,Ruth,ルース,Лус,Ruth,Lusi,Default,Player,,me_head,VO/voice_MEd,
|
||||||
mem,路斯,Luth,ルース,,,,Default,Player,,me_head,VO/voice_MEm,
|
mem,路斯,Ruth,ルース,Лус,Ruth,Lusi,Default,Player,,me_head,VO/voice_MEm,
|
||||||
mecd,女孩,Young Girl,少女,,,,Default,MainActor,,,VO/voice_MECd,
|
mecd,女孩,Girl,女の子,Девочка,Niña,Garota,Default,MainActor,,,VO/voice_MECd,
|
||||||
mecd?,???,???,???,,,,Default,MainActor,,,VO/voice_MECd,
|
mecd?,???,???,???,???,???,???,Default,MainActor,,,VO/voice_MECd,
|
||||||
bb,BB机,Beeper,,,,,Default,Player,,me_head,,
|
bb,BB机,Pager,ポケベル,Пейджер,Bíper,Pager,Default,Player,,me_head,,
|
||||||
jj,姐姐,Sister,姉さん,,,,Default,MainActor,,me_head,VO/voice_JJ,
|
jj,姐姐,Sister,姉さん,Сестра,Hermana mayor,Irmã,Default,MainActor,,me_head,VO/voice_JJ,
|
||||||
jjd,姐姐,Sister,姉さん,,,,Default,MainActor,,me_head,VO/voice_JJd,
|
jjd,姐姐,Sister,姉さん,Сестра,Hermana mayor,Irmã,Default,MainActor,,me_head,VO/voice_JJd,
|
||||||
jjm,姐姐,Sister,姉さん,,,,Default,MainActor,,me_head,VO/voice_JJm,
|
jjm,姐姐,Sister,姉さん,Сестра,Hermana mayor,Irmã,Default,MainActor,,me_head,VO/voice_JJm,
|
||||||
jj?,柔和女声,???,???,,,,Default,MainActor,,me_head,VO/voice_JJ,
|
jj?,柔和女声,Soft Female Voice,柔らかな女の声,Нежный женский голос,Voz suave de mujer,Voz Feminina Suave,Default,MainActor,,me_head,VO/voice_JJ,
|
||||||
hs,火山,HuoShan,火山,,,,Default,MainActor,#FFA800,peipei_head,VO/voice_HS,
|
hs,火山,Cole,火山,Вулкан,Huoshan,Vulcão,Default,MainActor,#FFA800,peipei_head,VO/voice_HS,
|
||||||
hs?,沙哑男声,???,???,,,,Default,MainActor,#FFA800,peipei_head,VO/voice_HS,
|
hs?,沙哑男声,Hoarse Male Voice,しゃがれた男の声,Хриплый мужской голос,Voz ronca de hombre,Voz Masculina Rouca,Default,MainActor,#FFA800,peipei_head,VO/voice_HS,
|
||||||
hsm,火山,HuoShan,火山,,,,Default,MainActor,,,VO/voice_HSm,
|
hsm,火山,Kazan,火山,Вулкан,Huoshan,Vulcão,Default,MainActor,,,VO/voice_HSm,
|
||||||
hsd,阿灿,Young Boy,少年,,,,Default,MainActor,,,VO/voice_HSd,
|
hsd,阿灿,Acan,アーツァン,А-Цань,Acan,Acan,Default,MainActor,,,VO/voice_HSd,
|
||||||
hsd?,???,???,???,,,,Default,MainActor,#FFA800,peipei_head,VO/voice_HSd,
|
hsd?,???,???,???,???,???,???,Default,MainActor,#FFA800,peipei_head,VO/voice_HSd,
|
||||||
yl,英理,Yingli,エリ,,,,Default,MainActor,#4000FF,peipei_head,VO/voice_YL,
|
yl,英理,Yingli,エリ,Инь Ли,Yingli,Yingli,Default,MainActor,#4000FF,peipei_head,VO/voice_YL,
|
||||||
ql?,02号调酒机体,Bartender,,,,,Default,MainActor,#00FFFF,peipei_head,VO/voice_YL,
|
ql?,02号调酒机体,Bartender Entity 02,02番バーテンダー機体,Юнит-бармен №02,Unidad barman 02,Unidade Bartender 02,Default,MainActor,#00FFFF,peipei_head,VO/voice_YL,
|
||||||
ql,奇琳,Kirin,チーリン,,,,Default,MainActor,#00FFFF,peipei_head,VO/voice_YL,
|
ql,奇琳,Qilin,チーリン,Цилинь,Qilin,Qilin,Default,MainActor,#00FFFF,peipei_head,VO/voice_YL,
|
||||||
peipei_novoice,胆怯女声,Timid female voice,怯えた女声,,,,Default,Aside,#00FFFF,,VO/voice_PP,2
|
peipei_novoice,胆怯女声,Timid Female Voice,臆病な女の声,Робкий женский голос,Voz tímida de mujer,Voz Feminina Tímida,Default,Aside,#00FFFF,,VO/voice_PP,2
|
||||||
yingli_novoice,严厉女声,Stern female voice,厳しい女声,,,,Default,Aside,#FFCC00,,VO/voice_YL,1
|
yingli_novoice,严厉女声,Stern Female Voice,厳しい女の声,Строгий женский голос,Voz severa de mujer,Voz Feminina Severa,Default,Aside,#FFCC00,,VO/voice_YL,1
|
||||||
tutorial,教学,Tutorial,,,,,Default,Aside,,,,
|
tutorial,教学,Tutorial,チュートリアル,Обучение,Tutorial,Tutorial,Default,Aside,,,,
|
||||||
docter,儿科医生,Doctor (in memory),医者(記憶の中),,,,Default,Aside,#FFCC00,,,
|
docter,儿科医生,Pediatrician,小児科医,Педиатр,Pediatra,Pediatra,Default,Aside,#FFCC00,,,
|
||||||
peipei_child,童年佩佩,Peipei (child),ペイペイ(幼少期),,,,Default,Aside,#00FFFF,,VO/voice_PPm,
|
peipei_child,童年佩佩,Peipei (child),ペペ(幼少期),Пэйпэй в детстве,Peipei (niña),PeiPei Criança,Default,Aside,#00FFFF,,VO/voice_PPm,
|
||||||
unknown,???,???,???,,,,Default,Aside,#FFCC00,,,
|
unknown,???,???,???,???,???,???,Default,Aside,#FFCC00,,,
|
||||||
???,???,???,???,,,,Default,MainActor,#FFCC00,,,
|
???,???,???,???,???,???,???,Default,MainActor,#FFCC00,,,
|
||||||
zls?,???,???,???,,,,Default,MainActor,#FFCC00,,VO/voice_memCommon,
|
zls?,???,???,???,???,???,???,Default,MainActor,#FFCC00,,VO/voice_memCommon,
|
||||||
zls,治疗师,Therapist,セラピスト,,,,Default,MainActor,#FFCC00,,VO/voice_memCommon,
|
zls,治疗师,Therapist,セラピスト,Терапевт,Terapeuta,Terapeuta,Default,MainActor,#FFCC00,,VO/voice_memCommon,
|
||||||
shitou,石头,Stone,シートウ,,,,Default,MainActor,,,VO/voice_shitou,
|
shitou,石头,Stone,ストーン,Шитоу,Piedra,Pedra,Default,MainActor,,,VO/voice_shitou,
|
||||||
Descent,Descent,Descent,,,,,Default,Aside,#FFCC00,,VO/voice_me,
|
Descent,Descent,Descent,ディセント,Descent,Descent,Descent,Default,Aside,#FFCC00,,VO/voice_me,
|
||||||
e-assistant,电子助手,Electronic Assistant,,,,,Default,Aside,#FFCC00,,VO/voice_me,
|
e-assistant,电子助手,Electronic Assistant,電子アシスタント,Электронный помощник,Asistente electrónico,Assistente Eletrônico,Default,Aside,#FFCC00,,VO/voice_me,
|
||||||
Gotashi_unknow,戴纯金雕塑头的男人,*Gotash,*ゴタシュ,,,,Default,Aside,#FFCC00,,,
|
Gotashi_unknow,戴纯金雕塑头的男人,Man with a Solid Gold Sculpted Head,純金彫刻の頭部を被った男,Мужчина с головой-скульптурой из чистого золота,Hombre que lleva una cabeza de escultura de oro puro,Homem com Cabeça de Escultura de Ouro Maciço,Default,Aside,#FFCC00,,,
|
||||||
dn,无连接,No Connection,No Connection,,,,Screen,Aside,#FFBF5F,,VO/voice_DN,
|
dn,无连接,Disconnected,未接続,Нет подключения,Sin conexión,Desconectado,Screen,Aside,#FFBF5F,,VO/voice_DN,
|
||||||
Linking,连接中……,Connecting...,Connecting...,,,,Screen,Aside,#E6A23C,,VO/voice_DN,
|
Linking,连接中……,Connecting...,接続中……,Подключение...,Conectando...,Conectando...,Screen,Aside,#E6A23C,,VO/voice_DN,
|
||||||
Analysing,分析中,Analyzing...,Analyzing...,,,,Screen,Aside,#E6A23C,,VO/voice_DN,
|
Analysing,分析中,Analyzing...,分析中,Анализ...,Analizando,Analisando,Screen,Aside,#E6A23C,,VO/voice_DN,
|
||||||
Error,错误警告,Error warning,Error warning,,,,Screen,Aside,#E6A23C,,VO/voice_DN,
|
Error,错误警告,Error Warning,エラー警告,Предупреждение об ошибке,Advertencia de error,Aviso de Erro,Screen,Aside,#E6A23C,,VO/voice_DN,
|
||||||
UfModule,Unstable Fusion图像生成模块,Unstable Fusion Module,Unstable Fusion Module,,,,Screen,Aside,#F56C6C,,VO/voice_DN,
|
UfModule,Unstable Fusion图像生成模块,Unstable Fusion Image Generation Module,Unstable Fusion画像生成モジュール,Модуль генерации изображений Unstable Fusion,Módulo de generación de imágenes Unstable Fusion,Módulo de Geração de Imagem Unstable Fusion,Screen,Aside,#F56C6C,,VO/voice_DN,
|
||||||
EyeModule,HS9型视觉模块,HS9 Visual Module,HS9 Visual Module,,,,Screen,Aside,#F56C6C,,VO/voice_DN,
|
EyeModule,HS9型视觉模块,HS9 Vision Module,HS9型視覚モジュール,Модуль зрения HS9,Módulo visual modelo HS9,Módulo de Visão HS9,Screen,Aside,#F56C6C,,VO/voice_DN,
|
||||||
EmoModule,通用型情绪发生模块,Universal Emotion Generation Module,Universal Emotion Generation Module,,,,Screen,Aside,#F56C6C,,VO/voice_DN,
|
EmoModule,通用型情绪发生模块,General-Purpose Emotion Generation Module,汎用型情緒発生モジュール,Универсальный модуль генерации эмоций,Módulo emocional genérico,Módulo de Geração de Emoção Genérico,Screen,Aside,#F56C6C,,VO/voice_DN,
|
||||||
MemModule,通用型记忆模块,Universal Memory Module,Universal Memory Module,,,,Screen,Aside,#F56C6C,,VO/voice_DN,
|
MemModule,通用型记忆模块,Universal Memory Module,汎用型記憶モジュール,Универсальный модуль памяти,Módulo de memoria genérico,Módulo de Memória Genérico,Screen,Aside,#F56C6C,,VO/voice_DN,
|
||||||
LogicModule,MK1型逻辑处理模块,MK1 logic Processing Module,MK1 logic Processing Module,,,,Screen,Aside,#F56C6C,,VO/voice_DN,
|
LogicModule,MK1型逻辑处理模块,Type MK1 Logic Processing Module,MK1型論理処理モジュール,Модуль обработки логики MK1,Módulo de procesamiento lógico modelo MK1,Módulo de Processamento Lógico MK1,Screen,Aside,#F56C6C,,VO/voice_DN,
|
||||||
SpeakerModule,MK1型表达模块,MK1 Expression Module,MK1 Expression Module,,,,Screen,Aside,#F56C6C,,VO/voice_DN,
|
SpeakerModule,MK1型表达模块,Type MK1 Expression Module,MK1型表現モジュール,Модуль выражения MK1,Módulo de expresión modelo MK1,Módulo de Expressão MK1,Screen,Aside,#F56C6C,,VO/voice_DN,
|
||||||
SaleModule,销售语言拓展模块,Sales Language Extension Module,販売言語拡張モジュール,,,,Screen,Aside,#F56C6C,,VO/voice_DN,
|
SaleModule,销售语言拓展模块,Sales Language Extension Module,販売言語拡張モジュール,Модуль расширения языка продаж,Módulo de expansión de lenguaje de ventas,Módulo de Expansão de Linguagem de Vendas,Screen,Aside,#F56C6C,,VO/voice_DN,
|
||||||
ChipModule,4R7型色觉晶圆训练槽,4R7 Color Perception Wafer Training Slot,4R7 Color Perception Wafer Training Slot,,,,Screen,Aside,#F56C6C,,VO/voice_DN,
|
ChipModule,4R7型色觉晶圆训练槽,Type 4R7 Color Vision Wafer Training Slot,4R7型色覚ウェハートレーニングスロット,Тренировочный слот чипа цветовосприятия 4R7,Ranura de entrenamiento de oblea de visión cromática modelo 4R7,Slot de Treinamento do Chip de Visão Cromática 4R7,Screen,Aside,#F56C6C,,VO/voice_DN,
|
||||||
HivemindModule,未知型号<br>Hivemind通讯终端,Unknown type Hivemind communication terminal,Unknown type Hivemind communication terminal,,,,Screen,Aside,#F56C6C,,VO/voice_DN,
|
HivemindModule,未知型号<br>Hivemind通讯终端,Unknown Model<br>Hivemind Communication Terminal,型番不明<br>ハイブマインド通信端末,Терминал связи «Коллективный разум»<br>неизвестной модели,Terminal de comunicación Hivemind<br>de modelo desconocido,Modelo Desconhecido<br>Terminal de Comunicação Hivemind,Screen,Aside,#F56C6C,,VO/voice_DN,
|
||||||
HeartModule,C03UR型特配涡轮增压动力模块,C03UR Special Turbocharged Power Module,C03UR Special Turbocharged Power Module,,,,Screen,Aside,#E6A23C,,VO/voice_DN,
|
HeartModule,C03UR型特配涡轮增压动力模块,Type C03UR Custom Turbocharged Power Module,C03UR型特注ターボチャージャー動力モジュール,Специальный силовой модуль C03UR с турбонаддувом,Módulo de potencia turboalimentado especial modelo C03UR,Módulo de Potência Turbocompressor Personalizado C03UR,Screen,Aside,#E6A23C,,VO/voice_DN,
|
||||||
LungModule,MK1型气体处理模块,MK1 Gas Processing Module,MK1 Gas Processing Module,,,,Screen,Aside,#E6A23C,,VO/voice_DN,
|
LungModule,MK1型气体处理模块,Type MK1 Gas Processing Module,MK1型気体処理モジュール,Модуль обработки газов MK1,Módulo de procesamiento de gas modelo MK1,Módulo de Processamento de Gás MK1,Screen,Aside,#E6A23C,,VO/voice_DN,
|
||||||
LiverModule,MK1型冷却液循环模块,MK1 Coolant Circulation Module,MK1 Coolant Circulation Module,,,,Screen,Aside,#E6A23C,,VO/voice_DN,
|
LiverModule,MK1型冷却液循环模块,Type MK1 Coolant Circulation Module,MK1型冷却液循環モジュール,Модуль циркуляции хладагента MK1,Módulo de circulación de líquido refrigerante modelo MK1,Módulo de Circulação de Líquido de Arrefecimento MK1,Screen,Aside,#E6A23C,,VO/voice_DN,
|
||||||
DigestModule,MK1型物料处理模块,MK1 Material Processing Module,MK1 Material Processing Module,,,,Screen,Aside,#E6A23C,,VO/voice_DN,
|
DigestModule,MK1型物料处理模块,Type MK1 Material Processing Module,MK1型原料処理モジュール,Модуль обработки материалов MK1,Módulo de procesamiento de material modelo MK1,Módulo de Processamento de Material MK1,Screen,Aside,#E6A23C,,VO/voice_DN,
|
||||||
CenterText,,,,,,,CenterText,Aside,,,,
|
CenterText,,,,,,,CenterText,Aside,,,,
|
||||||
CenterOption,,,,,,,CenterOption,Aside,,,,
|
CenterOption,,,,,,,CenterOption,Aside,,,,
|
||||||
Task,,,,,,,Task,Aside,,,,
|
Task,,,,,,,Task,Aside,,,,
|
||||||
TempText,,,,,,,Default,Aside,,,,
|
TempText,,,,,,,Default,Aside,,,,
|
||||||
gts,患者,???,???,,,,Default,MainActor,#A4CD36,,VO/voice_GTS,
|
gts,患者,Patient,患者,Пациент,Paciente,Paciente,Default,MainActor,#A4CD36,,VO/voice_GTS,
|
||||||
gts?,患者,???,???,,,,Default,MainActor,#A4CD36,,VO/voice_GTS_broken,
|
gts?,患者,Patient,患者,Пациент,Paciente,Paciente,Default,MainActor,#A4CD36,,VO/voice_GTS_broken,
|
||||||
gts??,患者??,???,???,,,,Default,MainActor,#E2C22B,,VO/voice_GTS,
|
gts??,患者??,Patient??,患者??,Пациент??,¿¿Paciente??,Paciente??,Default,MainActor,#E2C22B,,VO/voice_GTS,
|
||||||
classmate,同学,Classmate,クラスメート,,,,Default,Aside,,,VO/voice_memCommon,
|
classmate,同学,Classmate,同級生,Одноклассник,Compañero de clase,Colega,Default,Aside,,,VO/voice_memCommon,
|
||||||
jb,酒保,Bartender,バーテンダー,,,,Default,MainActor,,,VO/voice_memCommon,
|
jb,酒保,Bartender,バーテンダー,Бармен,Cantinero,Bartender,Default,MainActor,,,VO/voice_memCommon,
|
||||||
host,主持人,Newscaster,ニュースキャスター,,,,Default,MainActor,,,VO/voice_memCommon,
|
host,主持人,Host,司会者,Ведущий,Presentador,Apresentador,Default,MainActor,,,VO/voice_memCommon,
|
||||||
teacher,教师,Teacher,Teacher,,,,Default,MainActor,#FFCC00,,VO/voice_memCommon,
|
teacher,教师,Teacher,教師,Учитель,Maestro,Professor,Default,MainActor,#FFCC00,,VO/voice_memCommon,
|
||||||
sellerA,销售员A,Seller A,Seller A,,,,Default,MainActor,#FFCC00,,VO/voice_memCommon,
|
sellerA,销售员A,Salesperson A,販売員A,Продавец А,Vendedor A,Vendedor A,Default,MainActor,#FFCC00,,VO/voice_memCommon,
|
||||||
sellerB,销售员B,Seller B,Seller B,,,,Default,MainActor,#FFCC00,,VO/voice_memCommon,
|
sellerB,销售员B,Salesperson B,販売員B,Продавец Б,Vendedor B,Vendedor B,Default,MainActor,#FFCC00,,VO/voice_memCommon,
|
||||||
sellerC,销售员C,Seller C,Seller C,,,,Default,MainActor,#FFCC00,,VO/voice_memCommon,
|
sellerC,销售员C,Salesperson C,販売員C,Продавец В,Vendedor C,Vendedor C,Default,MainActor,#FFCC00,,VO/voice_memCommon,
|
||||||
|
|
||||||
|
|||||||
|
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 480fa26171aa2e242bff1885e9f7df55
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ad75debdd4433f642891c0cbc24a0bb5
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"name": "AibisDream.Typography.Tests.PlayMode",
|
||||||
|
"rootNamespace": "AibisDream.Typography.Tests.PlayMode",
|
||||||
|
"references": [
|
||||||
|
"AibisDream.Typography.Runtime",
|
||||||
|
"Unity.TextMeshPro"
|
||||||
|
],
|
||||||
|
"includePlatforms": [],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": false,
|
||||||
|
"defineConstraints": [
|
||||||
|
"UNITY_INCLUDE_TESTS"
|
||||||
|
],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": false,
|
||||||
|
"optionalUnityReferences": [
|
||||||
|
"TestAssemblies"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4bd93b18a8a4497478c36873cabf5355
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,328 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using AibisDream.Framework;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using TMPro;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.TestTools;
|
||||||
|
using Object = UnityEngine.Object;
|
||||||
|
|
||||||
|
namespace AibisDream.Typography.Tests.PlayMode
|
||||||
|
{
|
||||||
|
public sealed class TypographyServicePlayModeTests
|
||||||
|
{
|
||||||
|
private readonly List<Object> createdObjects = new();
|
||||||
|
|
||||||
|
[UnitySetUp]
|
||||||
|
public IEnumerator SetUp()
|
||||||
|
{
|
||||||
|
TypographyService.ResetForTests();
|
||||||
|
yield return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityTearDown]
|
||||||
|
public IEnumerator TearDown()
|
||||||
|
{
|
||||||
|
TypographyService.ResetForTests();
|
||||||
|
foreach (Object created in createdObjects)
|
||||||
|
{
|
||||||
|
if (created != null)
|
||||||
|
Object.Destroy(created);
|
||||||
|
}
|
||||||
|
createdObjects.Clear();
|
||||||
|
yield return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator RuntimeCreatedText_AppliesCurrentProfile()
|
||||||
|
{
|
||||||
|
TypographyProfile latin = CreateProfile(TypographyProfileKind.Latin, 1.25f);
|
||||||
|
var loader = new ImmediateLoader(new Dictionary<string, TypographyProfile>
|
||||||
|
{
|
||||||
|
[TypographyLocaleMapping.GetProfileKey(LocaleKind.En)] = latin
|
||||||
|
});
|
||||||
|
TypographyService.ConfigureLoader(loader);
|
||||||
|
|
||||||
|
Task<bool> loadTask = TypographyService.LoadAndActivateAsync(LocaleKind.En);
|
||||||
|
yield return new WaitUntil(() => loadTask.IsCompleted);
|
||||||
|
Assert.That(loadTask.Result, Is.True);
|
||||||
|
|
||||||
|
var go = new GameObject("RuntimeTypographyText", typeof(RectTransform));
|
||||||
|
createdObjects.Add(go);
|
||||||
|
TextMeshProUGUI text = go.AddComponent<TextMeshProUGUI>();
|
||||||
|
text.fontSize = 20f;
|
||||||
|
|
||||||
|
Assert.That(TypographyService.Apply(text, TypographyRole.Body), Is.True);
|
||||||
|
Assert.That(text.fontSize, Is.EqualTo(25f));
|
||||||
|
Assert.That(text.font, Is.EqualTo(TMP_Settings.defaultFontAsset));
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator RegisteredBinding_SwitchesProfilesWithoutMetricAccumulationAndReleasesOldKey()
|
||||||
|
{
|
||||||
|
TypographyProfile latin = CreateProfile(TypographyProfileKind.Latin, 0.5f);
|
||||||
|
TypographyProfile russian = CreateProfile(TypographyProfileKind.Russian, 0.75f);
|
||||||
|
var loader = new ImmediateLoader(new Dictionary<string, TypographyProfile>
|
||||||
|
{
|
||||||
|
[TypographyLocaleMapping.GetProfileKey(LocaleKind.En)] = latin,
|
||||||
|
[TypographyLocaleMapping.GetProfileKey(LocaleKind.Ru)] = russian
|
||||||
|
});
|
||||||
|
TypographyService.ConfigureLoader(loader);
|
||||||
|
|
||||||
|
var go = new GameObject("BoundTypographyText", typeof(RectTransform));
|
||||||
|
createdObjects.Add(go);
|
||||||
|
TextMeshProUGUI text = go.AddComponent<TextMeshProUGUI>();
|
||||||
|
text.fontSize = 40f;
|
||||||
|
go.AddComponent<LocalizedTypography>();
|
||||||
|
|
||||||
|
Task<bool> latinTask = TypographyService.LoadAndActivateAsync(LocaleKind.En);
|
||||||
|
yield return new WaitUntil(() => latinTask.IsCompleted);
|
||||||
|
Assert.That(text.fontSize, Is.EqualTo(20f));
|
||||||
|
|
||||||
|
Task<bool> russianTask = TypographyService.LoadAndActivateAsync(LocaleKind.Ru);
|
||||||
|
yield return new WaitUntil(() => russianTask.IsCompleted);
|
||||||
|
Assert.That(text.fontSize, Is.EqualTo(30f));
|
||||||
|
Assert.That(
|
||||||
|
loader.ReleasedKeys,
|
||||||
|
Does.Contain(TypographyLocaleMapping.GetProfileKey(LocaleKind.En)));
|
||||||
|
|
||||||
|
Task<bool> backTask = TypographyService.LoadAndActivateAsync(LocaleKind.En);
|
||||||
|
yield return new WaitUntil(() => backTask.IsCompleted);
|
||||||
|
Assert.That(text.fontSize, Is.EqualTo(20f));
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator PrefabOverride_UsesResolvedInstanceMetricsAsBaseline()
|
||||||
|
{
|
||||||
|
TypographyProfile latin = CreateProfile(TypographyProfileKind.Latin, 1f);
|
||||||
|
var loader = new ImmediateLoader(new Dictionary<string, TypographyProfile>
|
||||||
|
{
|
||||||
|
[TypographyLocaleMapping.GetProfileKey(LocaleKind.En)] = latin
|
||||||
|
});
|
||||||
|
TypographyService.ConfigureLoader(loader);
|
||||||
|
|
||||||
|
Task<bool> loadTask = TypographyService.LoadAndActivateAsync(LocaleKind.En);
|
||||||
|
yield return new WaitUntil(() => loadTask.IsCompleted);
|
||||||
|
Assert.That(loadTask.Result, Is.True);
|
||||||
|
|
||||||
|
var go = new GameObject("PrefabOverrideTypographyText", typeof(RectTransform));
|
||||||
|
createdObjects.Add(go);
|
||||||
|
go.SetActive(false);
|
||||||
|
TextMeshProUGUI text = go.AddComponent<TextMeshProUGUI>();
|
||||||
|
LocalizedTypography binding = go.AddComponent<LocalizedTypography>();
|
||||||
|
|
||||||
|
const float sourceBaseline = 40f;
|
||||||
|
const float resolvedOverride = 36f;
|
||||||
|
SetPrivateField(binding, "baselineCaptured", true);
|
||||||
|
SetPrivateField(binding, "baseFontSize", sourceBaseline);
|
||||||
|
text.fontSize = resolvedOverride;
|
||||||
|
|
||||||
|
go.SetActive(true);
|
||||||
|
yield return null;
|
||||||
|
|
||||||
|
Assert.That(text.fontSize, Is.EqualTo(resolvedOverride));
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator OverlappingLoads_LastRequestWins()
|
||||||
|
{
|
||||||
|
TypographyProfile latin = CreateProfile(TypographyProfileKind.Latin, 1f);
|
||||||
|
TypographyProfile japanese = CreateProfile(TypographyProfileKind.Japanese, 1f);
|
||||||
|
var loader = new DeferredLoader();
|
||||||
|
TypographyService.ConfigureLoader(loader);
|
||||||
|
|
||||||
|
Task<bool> latinTask = TypographyService.LoadAndActivateAsync(LocaleKind.En);
|
||||||
|
Task<bool> japaneseTask = TypographyService.LoadAndActivateAsync(LocaleKind.Ja);
|
||||||
|
|
||||||
|
loader.Complete(TypographyLocaleMapping.GetProfileKey(LocaleKind.Ja), japanese);
|
||||||
|
yield return new WaitUntil(() => japaneseTask.IsCompleted);
|
||||||
|
loader.Complete(TypographyLocaleMapping.GetProfileKey(LocaleKind.En), latin);
|
||||||
|
yield return new WaitUntil(() => latinTask.IsCompleted);
|
||||||
|
|
||||||
|
Assert.That(TypographyService.CurrentProfile, Is.SameAs(japanese));
|
||||||
|
Assert.That(
|
||||||
|
TypographyService.CurrentProfileKey,
|
||||||
|
Is.EqualTo(TypographyLocaleMapping.GetProfileKey(LocaleKind.Ja)));
|
||||||
|
Assert.That(latinTask.Result, Is.False);
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator OverlappingLoads_ForSameProfileDoNotReleaseTheWinningHandle()
|
||||||
|
{
|
||||||
|
TypographyProfile latin = CreateProfile(TypographyProfileKind.Latin, 1f);
|
||||||
|
var loader = new DeferredLoader();
|
||||||
|
TypographyService.ConfigureLoader(loader);
|
||||||
|
|
||||||
|
Task<bool> firstTask = TypographyService.LoadAndActivateAsync(LocaleKind.En);
|
||||||
|
Task<bool> secondTask = TypographyService.LoadAndActivateAsync(LocaleKind.Es);
|
||||||
|
loader.Complete(TypographyLocaleMapping.GetProfileKey(LocaleKind.En), latin);
|
||||||
|
|
||||||
|
yield return new WaitUntil(() => firstTask.IsCompleted && secondTask.IsCompleted);
|
||||||
|
Assert.That(firstTask.Result, Is.False);
|
||||||
|
Assert.That(secondTask.Result, Is.True);
|
||||||
|
Assert.That(TypographyService.CurrentProfile, Is.SameAs(latin));
|
||||||
|
Assert.That(loader.ReleasedKeys, Is.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator MissingProfile_KeepsTheLastValidProfile()
|
||||||
|
{
|
||||||
|
TypographyProfile latin = CreateProfile(TypographyProfileKind.Latin, 1f);
|
||||||
|
var loader = new ImmediateLoader(new Dictionary<string, TypographyProfile>
|
||||||
|
{
|
||||||
|
[TypographyLocaleMapping.GetProfileKey(LocaleKind.En)] = latin
|
||||||
|
});
|
||||||
|
TypographyService.ConfigureLoader(loader);
|
||||||
|
|
||||||
|
Task<bool> validTask = TypographyService.LoadAndActivateAsync(LocaleKind.En);
|
||||||
|
yield return new WaitUntil(() => validTask.IsCompleted);
|
||||||
|
LogAssert.Expect(
|
||||||
|
LogType.Error,
|
||||||
|
"[TypographyService] 字体 Profile 不存在或加载失败:Typography/Profile/Japanese");
|
||||||
|
Task<bool> missingTask = TypographyService.LoadAndActivateAsync(LocaleKind.Ja);
|
||||||
|
yield return new WaitUntil(() => missingTask.IsCompleted);
|
||||||
|
|
||||||
|
Assert.That(validTask.Result, Is.True);
|
||||||
|
Assert.That(missingTask.Result, Is.False);
|
||||||
|
Assert.That(TypographyService.CurrentProfile, Is.SameAs(latin));
|
||||||
|
Assert.That(
|
||||||
|
TypographyService.CurrentProfileKey,
|
||||||
|
Is.EqualTo(TypographyLocaleMapping.GetProfileKey(LocaleKind.En)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator LocalizationKit_SupportedLocalesLoadRealAddressableProfilesAndReleasePreviousHandles()
|
||||||
|
{
|
||||||
|
Type localizationKit = Type.GetType(
|
||||||
|
"AibisDream.Framework.LocalizationKit, Assembly-CSharp",
|
||||||
|
throwOnError: true);
|
||||||
|
Type resourceSystem = Type.GetType(
|
||||||
|
"AibisDream.Framework.ResourceSystem, Assembly-CSharp",
|
||||||
|
throwOnError: true);
|
||||||
|
MethodInfo switchLanguage = localizationKit.GetMethod(
|
||||||
|
"SwitchLanguageAsync",
|
||||||
|
BindingFlags.Public | BindingFlags.Static);
|
||||||
|
PropertyInfo currentLanguageCode = localizationKit.GetProperty(
|
||||||
|
"CurrentLanguageCode",
|
||||||
|
BindingFlags.Public | BindingFlags.Static);
|
||||||
|
PropertyInfo persistentLoader = resourceSystem.GetProperty(
|
||||||
|
"PersistentLoader",
|
||||||
|
BindingFlags.Public | BindingFlags.Static);
|
||||||
|
Assert.That(switchLanguage, Is.Not.Null);
|
||||||
|
Assert.That(currentLanguageCode, Is.Not.Null);
|
||||||
|
Assert.That(persistentLoader, Is.Not.Null);
|
||||||
|
|
||||||
|
string originalLocale = (string)currentLanguageCode.GetValue(null);
|
||||||
|
var localeCases = new[]
|
||||||
|
{
|
||||||
|
(Code: "zh-Hans", Kind: LocaleKind.Cn),
|
||||||
|
(Code: "en", Kind: LocaleKind.En),
|
||||||
|
(Code: "ja-JP", Kind: LocaleKind.Ja),
|
||||||
|
(Code: "ru", Kind: LocaleKind.Ru),
|
||||||
|
(Code: "es", Kind: LocaleKind.Es),
|
||||||
|
(Code: "pt-BR", Kind: LocaleKind.PtBr)
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var localeCase in localeCases)
|
||||||
|
{
|
||||||
|
var switchTask = (Task<bool>)switchLanguage.Invoke(null, new object[] { localeCase.Code });
|
||||||
|
yield return new WaitUntil(() => switchTask.IsCompleted);
|
||||||
|
Assert.That(switchTask.IsFaulted, Is.False, localeCase.Code);
|
||||||
|
Assert.That(switchTask.Result, Is.True, localeCase.Code);
|
||||||
|
Assert.That(
|
||||||
|
TypographyService.CurrentProfileKey,
|
||||||
|
Is.EqualTo(TypographyLocaleMapping.GetProfileKey(localeCase.Kind)),
|
||||||
|
localeCase.Code);
|
||||||
|
|
||||||
|
object loaderInstance = persistentLoader.GetValue(null);
|
||||||
|
Assert.That(loaderInstance, Is.Not.Null, localeCase.Code);
|
||||||
|
int handleCount = (int)loaderInstance.GetType()
|
||||||
|
.GetProperty("TotalHandleCount", BindingFlags.Public | BindingFlags.Instance)
|
||||||
|
.GetValue(loaderInstance);
|
||||||
|
Assert.That(handleCount, Is.EqualTo(1), localeCase.Code);
|
||||||
|
}
|
||||||
|
|
||||||
|
var restoreTask = (Task<bool>)switchLanguage.Invoke(null, new object[] { originalLocale });
|
||||||
|
yield return new WaitUntil(() => restoreTask.IsCompleted);
|
||||||
|
Assert.That(restoreTask.Result, Is.True);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TypographyProfile CreateProfile(TypographyProfileKind kind, float multiplier)
|
||||||
|
{
|
||||||
|
var profile = ScriptableObject.CreateInstance<TypographyProfile>();
|
||||||
|
createdObjects.Add(profile);
|
||||||
|
profile.Configure(
|
||||||
|
kind,
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
new TypographyStyle(
|
||||||
|
TypographyRole.Body,
|
||||||
|
TMP_Settings.defaultFontAsset,
|
||||||
|
fontSizeMultiplier: multiplier)
|
||||||
|
});
|
||||||
|
return profile;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetPrivateField<T>(LocalizedTypography binding, string fieldName, T value)
|
||||||
|
{
|
||||||
|
FieldInfo field = typeof(LocalizedTypography).GetField(
|
||||||
|
fieldName,
|
||||||
|
BindingFlags.Instance | BindingFlags.NonPublic);
|
||||||
|
Assert.That(field, Is.Not.Null, fieldName);
|
||||||
|
field.SetValue(binding, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class ImmediateLoader : ITypographyProfileLoader
|
||||||
|
{
|
||||||
|
private readonly IReadOnlyDictionary<string, TypographyProfile> profiles;
|
||||||
|
|
||||||
|
public ImmediateLoader(IReadOnlyDictionary<string, TypographyProfile> profiles)
|
||||||
|
{
|
||||||
|
this.profiles = profiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<string> ReleasedKeys { get; } = new();
|
||||||
|
|
||||||
|
public Task<TypographyProfile> LoadAsync(string key)
|
||||||
|
{
|
||||||
|
profiles.TryGetValue(key, out TypographyProfile profile);
|
||||||
|
return Task.FromResult(profile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Release(string key)
|
||||||
|
{
|
||||||
|
ReleasedKeys.Add(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class DeferredLoader : ITypographyProfileLoader
|
||||||
|
{
|
||||||
|
private readonly Dictionary<string, TaskCompletionSource<TypographyProfile>> pending = new();
|
||||||
|
|
||||||
|
public List<string> ReleasedKeys { get; } = new();
|
||||||
|
|
||||||
|
public Task<TypographyProfile> LoadAsync(string key)
|
||||||
|
{
|
||||||
|
if (!pending.TryGetValue(key, out TaskCompletionSource<TypographyProfile> source))
|
||||||
|
{
|
||||||
|
source = new TaskCompletionSource<TypographyProfile>();
|
||||||
|
pending[key] = source;
|
||||||
|
}
|
||||||
|
return source.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Complete(string key, TypographyProfile profile)
|
||||||
|
{
|
||||||
|
pending[key].SetResult(profile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Release(string key)
|
||||||
|
{
|
||||||
|
ReleasedKeys.Add(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9a0486afd10903e4f81fc8560089f9f5
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
Reference in New Issue
Block a user