TH1/Unity/Assets/Scripts/TH1_Logic/Editor/VisualSpriteVariantMapEditorWindow.cs
2026-06-10 11:58:18 +08:00

260 lines
10 KiB
C#

using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace Logic.Editor
{
public class VisualSpriteVariantMapEditorWindow : EditorWindow
{
private const string DefaultMapAssetPath = "Assets/BundleResources/DataAssets/VisualSpriteVariantMap.asset";
private const string GridAndResourceAssetPath = "Assets/BundleResources/Export/GridAndResourceDataAssets.asset";
private const string EyeComfortGroundFolder = "Assets/BundleResources/ArtResources/TH1Grounds/EyeComfort";
private const string EyeComfortMountainFolder = "Assets/BundleResources/ArtResources/TH1Mountains/EyeComfort";
private const string EyeComfortForestFolder = "Assets/BundleResources/ArtResources/TH1Forests/EyeComfort";
private const string GroundGroup = "GridObject/Ground";
private const string MountainGroup = "GridObject/Mountain";
private const string ForestGroup = "GridObject/Forest";
private VisualSpriteVariantMapAsset _mapAsset;
private GridAndResourceDataAssets _gridAsset;
private Vector2 _scroll;
[MenuItem("Tools/TH1/Visual Sprite Variant Map")]
private static void OpenWindow()
{
var window = GetWindow<VisualSpriteVariantMapEditorWindow>();
window.titleContent = new GUIContent("Sprite Variants");
window.minSize = new Vector2(720, 420);
window.LoadAssets();
}
[MenuItem("Tools/TH1/Visual Sprite Variant Map/Rebuild Map Feature EyeComfort")]
private static void RebuildGroundFromMenu()
{
var window = CreateInstance<VisualSpriteVariantMapEditorWindow>();
window.LoadAssets();
window.RebuildMapFeatureMappings();
DestroyImmediate(window);
}
private void OnEnable()
{
LoadAssets();
}
private void OnGUI()
{
EditorGUILayout.LabelField("Visual Sprite Variant Map", EditorStyles.boldLabel);
using (new EditorGUILayout.HorizontalScope())
{
_mapAsset = (VisualSpriteVariantMapAsset)EditorGUILayout.ObjectField("Map Asset", _mapAsset, typeof(VisualSpriteVariantMapAsset), false);
if (GUILayout.Button("Create/Load", GUILayout.Width(110)))
LoadOrCreateMapAsset();
}
_gridAsset = (GridAndResourceDataAssets)EditorGUILayout.ObjectField("Grid Asset", _gridAsset, typeof(GridAndResourceDataAssets), false);
using (new EditorGUILayout.HorizontalScope())
{
if (GUILayout.Button("Rebuild Map Feature EyeComfort Mappings", GUILayout.Height(28)))
RebuildMapFeatureMappings();
if (GUILayout.Button("Select Asset", GUILayout.Width(110), GUILayout.Height(28)) && _mapAsset != null)
{
Selection.activeObject = _mapAsset;
EditorGUIUtility.PingObject(_mapAsset);
}
}
EditorGUILayout.Space(8);
if (_mapAsset == null)
{
EditorGUILayout.HelpBox("Create or load VisualSpriteVariantMap.asset first.", MessageType.Info);
return;
}
int total = _mapAsset.Pairs?.Count ?? 0;
int mapped = _mapAsset.CountMappedPairs(VisualTheme.EyeComfort);
EditorGUILayout.LabelField($"Pairs: {total} EyeComfort mapped: {mapped}");
var serializedObject = new SerializedObject(_mapAsset);
var pairsProperty = serializedObject.FindProperty("Pairs");
_scroll = EditorGUILayout.BeginScrollView(_scroll);
EditorGUILayout.PropertyField(pairsProperty, true);
EditorGUILayout.EndScrollView();
serializedObject.ApplyModifiedProperties();
}
private void LoadAssets()
{
_mapAsset = AssetDatabase.LoadAssetAtPath<VisualSpriteVariantMapAsset>(DefaultMapAssetPath);
_gridAsset = AssetDatabase.LoadAssetAtPath<GridAndResourceDataAssets>(GridAndResourceAssetPath);
}
private void LoadOrCreateMapAsset()
{
_mapAsset = LoadOrCreateMapAsset(DefaultMapAssetPath);
}
private static VisualSpriteVariantMapAsset LoadOrCreateMapAsset(string path)
{
var asset = AssetDatabase.LoadAssetAtPath<VisualSpriteVariantMapAsset>(path);
if (asset != null)
return asset;
var folder = System.IO.Path.GetDirectoryName(path)?.Replace("\\", "/");
if (!string.IsNullOrEmpty(folder) && !AssetDatabase.IsValidFolder(folder))
CreateFolders(folder);
asset = CreateInstance<VisualSpriteVariantMapAsset>();
AssetDatabase.CreateAsset(asset, path);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
return asset;
}
private void RebuildMapFeatureMappings()
{
_mapAsset = LoadOrCreateMapAsset(DefaultMapAssetPath);
if (_gridAsset == null)
_gridAsset = AssetDatabase.LoadAssetAtPath<GridAndResourceDataAssets>(GridAndResourceAssetPath);
if (_gridAsset == null)
{
Debug.LogError($"[VisualSpriteVariantMap] Grid asset not found: {GridAndResourceAssetPath}");
return;
}
Undo.RecordObject(_mapAsset, "Rebuild Map Feature EyeComfort Mappings");
_mapAsset.Pairs.RemoveAll(pair => pair != null
&& (pair.Group == GroundGroup
|| pair.Group == MountainGroup
|| pair.Group == ForestGroup));
int groundMapped = AddMappings(GroundGroup, CollectGroundSprites(_gridAsset), EyeComfortGroundFolder);
int mountainMapped = AddMappings(MountainGroup, CollectMountainSprites(_gridAsset), EyeComfortMountainFolder);
int forestMapped = AddMappings(ForestGroup, CollectForestSprites(_gridAsset), EyeComfortForestFolder);
_mapAsset.RebuildCache();
EditorUtility.SetDirty(_mapAsset);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
Debug.Log($"[VisualSpriteVariantMap] Rebuilt map feature pairs. Ground={groundMapped}, Mountain={mountainMapped}, Forest={forestMapped} matched EyeComfort sprites.");
}
private int AddMappings(string group, List<Sprite> sourceSprites, string replacementFolder)
{
var replacementByName = LoadSpritesByName(replacementFolder);
int mapped = 0;
foreach (var source in sourceSprites)
{
replacementByName.TryGetValue(source.name, out var replacement);
if (replacement != null)
mapped++;
_mapAsset.Pairs.Add(new VisualSpriteVariantPair
{
Group = group,
Source = source,
EyeComfort = replacement
});
}
return mapped;
}
private static List<Sprite> CollectGroundSprites(GridAndResourceDataAssets gridAsset)
{
var result = new List<Sprite>();
var seen = new HashSet<Sprite>();
if (gridAsset?.TerrainInfoList == null)
return result;
foreach (var terrain in gridAsset.TerrainInfoList)
{
if (terrain == null || terrain.TerrainType != TerrainType.Land)
continue;
AddSprite(result, seen, terrain.Sprite);
if (terrain.SpriteList == null)
continue;
foreach (var spriteInfo in terrain.SpriteList)
AddSprite(result, seen, spriteInfo?.Sprite);
}
return result;
}
private static List<Sprite> CollectMountainSprites(GridAndResourceDataAssets gridAsset)
{
var result = new List<Sprite>();
var seen = new HashSet<Sprite>();
if (gridAsset?.MountainInfo?.SpriteList == null)
return result;
foreach (var spriteInfo in gridAsset.MountainInfo.SpriteList)
AddSprite(result, seen, spriteInfo?.Sprite);
return result;
}
private static List<Sprite> CollectForestSprites(GridAndResourceDataAssets gridAsset)
{
var result = new List<Sprite>();
var seen = new HashSet<Sprite>();
if (gridAsset?.VegetationInfo?.SpriteList == null)
return result;
foreach (var spriteInfo in gridAsset.VegetationInfo.SpriteList)
AddSprite(result, seen, spriteInfo?.Sprite);
return result;
}
private static void AddSprite(List<Sprite> result, HashSet<Sprite> seen, Sprite sprite)
{
if (sprite == null || seen.Contains(sprite))
return;
seen.Add(sprite);
result.Add(sprite);
}
private static Dictionary<string, Sprite> LoadSpritesByName(string folder)
{
var result = new Dictionary<string, Sprite>();
var guids = AssetDatabase.FindAssets("t:Sprite", new[] { folder });
foreach (var guid in guids)
{
var path = AssetDatabase.GUIDToAssetPath(guid);
var sprites = AssetDatabase.LoadAllAssetsAtPath(path);
foreach (var asset in sprites)
{
if (asset is Sprite sprite && !result.ContainsKey(sprite.name))
result[sprite.name] = sprite;
}
}
return result;
}
private static void CreateFolders(string folder)
{
var parts = folder.Split('/');
var current = parts[0];
for (int i = 1; i < parts.Length; i++)
{
var next = $"{current}/{parts[i]}";
if (!AssetDatabase.IsValidFolder(next))
AssetDatabase.CreateFolder(current, parts[i]);
current = next;
}
}
}
}