Files
aibis-dream/Assets/Editor/Typography/TerminalTypographyMigration.cs
T

230 lines
9.1 KiB
C#

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; }
}
}
}