|
@@ -3,16 +3,15 @@ using Spine.Unity;
|
|
|
using System.Collections.Generic;
|
|
using System.Collections.Generic;
|
|
|
using UnityEditor;
|
|
using UnityEditor;
|
|
|
using System.Linq;
|
|
using System.Linq;
|
|
|
-using System;
|
|
|
|
|
using System.IO;
|
|
using System.IO;
|
|
|
|
|
+using System;
|
|
|
using Spine;
|
|
using Spine;
|
|
|
|
|
|
|
|
[InitializeOnLoad]
|
|
[InitializeOnLoad]
|
|
|
public class SkeletonDataMonitor
|
|
public class SkeletonDataMonitor
|
|
|
{
|
|
{
|
|
|
private static Dictionary<string, CachedPrefabData> _prefabCache = new Dictionary<string, CachedPrefabData>();
|
|
private static Dictionary<string, CachedPrefabData> _prefabCache = new Dictionary<string, CachedPrefabData>();
|
|
|
- private static DateTime _lastCheckTime;
|
|
|
|
|
- private const double MIN_CHECK_INTERVAL = 2.0;
|
|
|
|
|
|
|
+ private static Dictionary<SkeletonDataAsset, List<string>> _skeletonToPrefabs = new Dictionary<SkeletonDataAsset, List<string>>();
|
|
|
|
|
|
|
|
private struct CachedPrefabData
|
|
private struct CachedPrefabData
|
|
|
{
|
|
{
|
|
@@ -22,138 +21,106 @@ public class SkeletonDataMonitor
|
|
|
|
|
|
|
|
static SkeletonDataMonitor()
|
|
static SkeletonDataMonitor()
|
|
|
{
|
|
{
|
|
|
|
|
+ // 只监听必要的事件
|
|
|
EditorApplication.hierarchyChanged += OnHierarchyChanged;
|
|
EditorApplication.hierarchyChanged += OnHierarchyChanged;
|
|
|
- EditorApplication.update += OnEditorUpdate;
|
|
|
|
|
Selection.selectionChanged += OnSelectionChanged;
|
|
Selection.selectionChanged += OnSelectionChanged;
|
|
|
- AssemblyReloadEvents.afterAssemblyReload += RefreshCache;
|
|
|
|
|
-
|
|
|
|
|
- // 新版Unity的Undo回调注册方式
|
|
|
|
|
- Undo.undoRedoPerformed += OnUndoRedo;
|
|
|
|
|
|
|
+ AssemblyReloadEvents.afterAssemblyReload += InitializeCache;
|
|
|
|
|
|
|
|
- RefreshCache();
|
|
|
|
|
|
|
+ InitializeCache();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private static void OnUndoRedo()
|
|
|
|
|
|
|
+ private static void InitializeCache()
|
|
|
{
|
|
{
|
|
|
- RefreshCache();
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ _prefabCache.Clear();
|
|
|
|
|
+ _skeletonToPrefabs.Clear();
|
|
|
|
|
|
|
|
- private static void OnHierarchyChanged()
|
|
|
|
|
- {
|
|
|
|
|
- if ((DateTime.Now - _lastCheckTime).TotalSeconds < MIN_CHECK_INTERVAL)
|
|
|
|
|
- return;
|
|
|
|
|
|
|
+ var allPrefabs = AssetDatabase.FindAssets("t:Prefab")
|
|
|
|
|
+ .Select(AssetDatabase.GUIDToAssetPath)
|
|
|
|
|
+ .Where(p => p.EndsWith(".prefab"));
|
|
|
|
|
|
|
|
- EditorApplication.delayCall += () =>
|
|
|
|
|
|
|
+ foreach (var path in allPrefabs)
|
|
|
{
|
|
{
|
|
|
- ProcessActiveSceneObjects();
|
|
|
|
|
- _lastCheckTime = DateTime.Now;
|
|
|
|
|
- };
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private static void OnEditorUpdate()
|
|
|
|
|
- {
|
|
|
|
|
- if ((DateTime.Now - _lastCheckTime).TotalSeconds < MIN_CHECK_INTERVAL)
|
|
|
|
|
- return;
|
|
|
|
|
-
|
|
|
|
|
- ProcessActiveSceneObjects();
|
|
|
|
|
- _lastCheckTime = DateTime.Now;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private static void OnSelectionChanged()
|
|
|
|
|
- {
|
|
|
|
|
- ProcessSelectedObjects();
|
|
|
|
|
|
|
+ var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(path);
|
|
|
|
|
+ var skeleton = prefab.GetComponentInChildren<SkeletonRenderer>();
|
|
|
|
|
+ if (skeleton != null && skeleton.skeletonDataAsset != null)
|
|
|
|
|
+ {
|
|
|
|
|
+ CachePrefab(path, skeleton.skeletonDataAsset);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private static void ProcessActiveSceneObjects()
|
|
|
|
|
|
|
+ private static void CachePrefab(string path, SkeletonDataAsset skeletonData)
|
|
|
{
|
|
{
|
|
|
- var skeletons = GameObject.FindObjectsOfType<SkeletonRenderer>(true);
|
|
|
|
|
- foreach (var skeleton in skeletons)
|
|
|
|
|
|
|
+ _prefabCache[path] = new CachedPrefabData
|
|
|
{
|
|
{
|
|
|
- ProcessSkeletonChange(skeleton);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ skeletonDataAsset = skeletonData,
|
|
|
|
|
+ lastModified = File.GetLastWriteTime(path)
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
- private static void ProcessSelectedObjects()
|
|
|
|
|
- {
|
|
|
|
|
- foreach (var obj in Selection.objects.OfType<GameObject>())
|
|
|
|
|
|
|
+ if (!_skeletonToPrefabs.ContainsKey(skeletonData))
|
|
|
{
|
|
{
|
|
|
- var skeleton = obj.GetComponentInChildren<SkeletonRenderer>();
|
|
|
|
|
- if (skeleton != null)
|
|
|
|
|
- {
|
|
|
|
|
- ProcessSkeletonChange(skeleton);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ _skeletonToPrefabs[skeletonData] = new List<string>();
|
|
|
}
|
|
}
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private static void ProcessSkeletonChange(SkeletonRenderer skeleton)
|
|
|
|
|
- {
|
|
|
|
|
- var prefabPath = PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(skeleton.gameObject);
|
|
|
|
|
- if (!string.IsNullOrEmpty(prefabPath))
|
|
|
|
|
|
|
+ if (!_skeletonToPrefabs[skeletonData].Contains(path))
|
|
|
{
|
|
{
|
|
|
- CheckAndProcessPrefab(prefabPath, skeleton.skeletonDataAsset);
|
|
|
|
|
|
|
+ _skeletonToPrefabs[skeletonData].Add(path);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private static void CheckAndProcessPrefab(string prefabPath, SkeletonDataAsset skeletonData)
|
|
|
|
|
|
|
+ private static void OnHierarchyChanged()
|
|
|
{
|
|
{
|
|
|
- if (!_prefabCache.TryGetValue(prefabPath, out var cache) ||
|
|
|
|
|
- cache.skeletonDataAsset != skeletonData ||
|
|
|
|
|
- cache.lastModified != File.GetLastWriteTime(prefabPath))
|
|
|
|
|
|
|
+ // 只检查新添加的对象
|
|
|
|
|
+ var newSkeletons = GameObject.FindObjectsOfType<SkeletonRenderer>(true)
|
|
|
|
|
+ .Where(s => PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(s.gameObject) != null);
|
|
|
|
|
+
|
|
|
|
|
+ foreach (var skeleton in newSkeletons)
|
|
|
{
|
|
{
|
|
|
- WriteData(prefabPath);
|
|
|
|
|
- _prefabCache[prefabPath] = new CachedPrefabData
|
|
|
|
|
|
|
+ if (skeleton.skeletonDataAsset != null)
|
|
|
{
|
|
{
|
|
|
- skeletonDataAsset = skeletonData,
|
|
|
|
|
- lastModified = File.GetLastWriteTime(prefabPath)
|
|
|
|
|
- };
|
|
|
|
|
|
|
+ var path = PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(skeleton.gameObject);
|
|
|
|
|
+ if (!_prefabCache.ContainsKey(path) ||
|
|
|
|
|
+ _prefabCache[path].skeletonDataAsset != skeleton.skeletonDataAsset)
|
|
|
|
|
+ {
|
|
|
|
|
+ ProcessSkeletonChange(path, skeleton.skeletonDataAsset);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private static void RefreshCache()
|
|
|
|
|
|
|
+ private static void OnSelectionChanged()
|
|
|
{
|
|
{
|
|
|
- _prefabCache.Clear();
|
|
|
|
|
- var allPrefabs = AssetDatabase.FindAssets("t:Prefab")
|
|
|
|
|
- .Select(AssetDatabase.GUIDToAssetPath)
|
|
|
|
|
- .Where(p => p.EndsWith(".prefab"));
|
|
|
|
|
-
|
|
|
|
|
- foreach (var path in allPrefabs)
|
|
|
|
|
|
|
+ // 只处理选中的SkeletonRenderer组件
|
|
|
|
|
+ if (Selection.activeGameObject != null)
|
|
|
{
|
|
{
|
|
|
- var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(path);
|
|
|
|
|
- var skeleton = prefab.GetComponentInChildren<SkeletonRenderer>();
|
|
|
|
|
|
|
+ var skeleton = Selection.activeGameObject.GetComponentInChildren<SkeletonRenderer>();
|
|
|
if (skeleton != null && skeleton.skeletonDataAsset != null)
|
|
if (skeleton != null && skeleton.skeletonDataAsset != null)
|
|
|
{
|
|
{
|
|
|
- _prefabCache[path] = new CachedPrefabData
|
|
|
|
|
|
|
+ var path = PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(skeleton.gameObject);
|
|
|
|
|
+ if (!string.IsNullOrEmpty(path) &&
|
|
|
|
|
+ (!_prefabCache.TryGetValue(path, out var cache) ||
|
|
|
|
|
+ cache.skeletonDataAsset != skeleton.skeletonDataAsset))
|
|
|
{
|
|
{
|
|
|
- skeletonDataAsset = skeleton.skeletonDataAsset,
|
|
|
|
|
- lastModified = File.GetLastWriteTime(path)
|
|
|
|
|
- };
|
|
|
|
|
|
|
+ ProcessSkeletonChange(path, skeleton.skeletonDataAsset);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- static SkeletonMecanim skeletonMecanim;
|
|
|
|
|
|
|
+ private static void ProcessSkeletonChange(string prefabPath, SkeletonDataAsset skeletonData)
|
|
|
|
|
+ {
|
|
|
|
|
+ WriteData(prefabPath);
|
|
|
|
|
+ CachePrefab(prefabPath, skeletonData);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
static Character cha;
|
|
static Character cha;
|
|
|
|
|
+ static SkeletonMecanim skeletonMecanim;
|
|
|
|
|
|
|
|
private static void WriteData(string path)
|
|
private static void WriteData(string path)
|
|
|
{
|
|
{
|
|
|
try
|
|
try
|
|
|
{
|
|
{
|
|
|
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(path);
|
|
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(path);
|
|
|
- if (prefab == null)
|
|
|
|
|
- {
|
|
|
|
|
- Debug.LogWarning($"无法加载Prefab: {path}");
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
GameObject prefabInstance = PrefabUtility.InstantiatePrefab(prefab) as GameObject;
|
|
GameObject prefabInstance = PrefabUtility.InstantiatePrefab(prefab) as GameObject;
|
|
|
- if (prefabInstance == null)
|
|
|
|
|
- {
|
|
|
|
|
- Debug.LogWarning($"实例化Prefab失败: {path}");
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 这里替换为您原来的WriteData实现
|
|
|
|
|
- Debug.Log($"正在处理Prefab: {path}");
|
|
|
|
|
|
|
|
|
|
cha = prefabInstance.GetComponent<Character>();
|
|
cha = prefabInstance.GetComponent<Character>();
|
|
|
ReadData(prefabInstance);
|
|
ReadData(prefabInstance);
|
|
@@ -251,23 +218,38 @@ public class SkeletonDataMonitor
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- [MenuItem("Tools/Spine/Force Refresh All")]
|
|
|
|
|
- private static void ForceRefreshAll()
|
|
|
|
|
|
|
+ // 监听SkeletonDataAsset文件变化
|
|
|
|
|
+ public class SkeletonDataPostprocessor : AssetPostprocessor
|
|
|
{
|
|
{
|
|
|
- RefreshCache();
|
|
|
|
|
- var allSkeletonData = _prefabCache.Values
|
|
|
|
|
- .Select(x => x.skeletonDataAsset)
|
|
|
|
|
- .Distinct();
|
|
|
|
|
-
|
|
|
|
|
- foreach (var data in allSkeletonData)
|
|
|
|
|
|
|
+ static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets,
|
|
|
|
|
+ string[] movedAssets, string[] movedFromAssetPaths)
|
|
|
{
|
|
{
|
|
|
- var users = _prefabCache
|
|
|
|
|
- .Where(kv => kv.Value.skeletonDataAsset == data)
|
|
|
|
|
- .Select(kv => kv.Key);
|
|
|
|
|
|
|
+ foreach (string path in importedAssets)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (path.EndsWith(".asset"))
|
|
|
|
|
+ {
|
|
|
|
|
+ var asset = AssetDatabase.LoadAssetAtPath<SkeletonDataAsset>(path);
|
|
|
|
|
+ if (asset != null && _skeletonToPrefabs.ContainsKey(asset))
|
|
|
|
|
+ {
|
|
|
|
|
+ foreach (var prefabPath in _skeletonToPrefabs[asset])
|
|
|
|
|
+ {
|
|
|
|
|
+ ProcessSkeletonChange(prefabPath, asset);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- foreach (var path in users)
|
|
|
|
|
|
|
+ [MenuItem("Tools/Spine/手动刷新变更")]
|
|
|
|
|
+ private static void ForceRefreshModified()
|
|
|
|
|
+ {
|
|
|
|
|
+ foreach (var kvp in _prefabCache.ToList())
|
|
|
|
|
+ {
|
|
|
|
|
+ var currentModTime = File.GetLastWriteTime(kvp.Key);
|
|
|
|
|
+ if (currentModTime != kvp.Value.lastModified)
|
|
|
{
|
|
{
|
|
|
- WriteData(path);
|
|
|
|
|
|
|
+ ProcessSkeletonChange(kvp.Key, kvp.Value.skeletonDataAsset);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|