using UnityEngine; using System.Collections.Generic; using Spine.Unity; public class BattleCacheManager { /// /// 资源引用信息(战场 + 角色维度) /// private class ResourceReference { public BattleResCache.CachedResource CachedResource; // 记录哪些战场的哪些角色在使用这个资源 // Key: battleGuid, Value: ownerIds public Dictionary> BattlefieldOwners = new Dictionary>(); public int RefCount { get { int count = 0; foreach (var owners in BattlefieldOwners.Values) { count += owners.Count; } return count; } } /// /// 添加战场+角色的引用 /// public void AddBattlefieldOwner(string battleGuid, string ownerId) { if (!BattlefieldOwners.ContainsKey(battleGuid)) { BattlefieldOwners[battleGuid] = new HashSet(); } BattlefieldOwners[battleGuid].Add(ownerId); } /// /// 移除整个战场的所有引用 /// public void RemoveBattlefield(string battleGuid) { BattlefieldOwners.Remove(battleGuid); } } // ===== 全局统一资源池(不再区分红蓝队)===== private static Dictionary globalSpineCache = new Dictionary(); private static Dictionary globalAudioCache = new Dictionary(); /// /// 注册战场资源需求(红队+蓝队一起注册) /// public void RegisterBattlefieldResources(string battleGuid, List spineResources, List audioResources) { // 注册Spine资源 foreach (var res in spineResources) { if (string.IsNullOrEmpty(res.OwnerId)) continue; string key = res.GetKey(); if (!globalSpineCache.ContainsKey(key)) { globalSpineCache[key] = new ResourceReference { CachedResource = new BattleResCache.CachedResource(res, null, false) }; } globalSpineCache[key].AddBattlefieldOwner(battleGuid, res.OwnerId); } // 注册Audio资源 foreach (var res in audioResources) { if (string.IsNullOrEmpty(res.OwnerId)) continue; string key = res.GetKey(); if (!globalAudioCache.ContainsKey(key)) { globalAudioCache[key] = new ResourceReference { CachedResource = new BattleResCache.CachedResource(res, null, false) }; } globalAudioCache[key].AddBattlefieldOwner(battleGuid, res.OwnerId); } Debug.Log($"BattleCacheManager: Registered battlefield {battleGuid} - Spine: {spineResources.Count}, Audio: {audioResources.Count}"); } /// /// 注销战场(卸载该战场的所有资源引用) /// public void UnregisterBattlefield(string battleGuid) { // 处理Spine资源 var spineKeysToRemove = new List(); foreach (var kvp in globalSpineCache) { kvp.Value.RemoveBattlefield(battleGuid); if (kvp.Value.RefCount == 0) { spineKeysToRemove.Add(kvp.Key); } } foreach (var key in spineKeysToRemove) { var res = globalSpineCache[key]; if (res.CachedResource.Asset != null) { ResManager.Instance.UnloadAsset( res.CachedResource.Identifier.Directory, res.CachedResource.Identifier.AssetName ); } globalSpineCache.Remove(key); Debug.Log($"BattleCacheManager: Unloaded spine (refCount=0): {key}"); } // 处理Audio资源 var audioKeysToRemove = new List(); foreach (var kvp in globalAudioCache) { kvp.Value.RemoveBattlefield(battleGuid); if (kvp.Value.RefCount == 0) { audioKeysToRemove.Add(kvp.Key); } } foreach (var key in audioKeysToRemove) { var res = globalAudioCache[key]; if (res.CachedResource.Asset != null) { ResManager.Instance.UnloadAsset( res.CachedResource.Identifier.Directory, res.CachedResource.Identifier.AssetName ); } globalAudioCache.Remove(key); Debug.Log($"BattleCacheManager: Unloaded audio (refCount=0): {key}"); } Debug.Log($"BattleCacheManager: Unregistered battlefield {battleGuid}"); } /// /// 获取或加载Spine资源 /// public SkeletonDataAsset GetSpineResource(string directory, string assetName, string battleGuid = "") { string key = $"{directory}/{assetName}"; if (globalSpineCache.TryGetValue(key, out var refInfo)) { return refInfo.CachedResource.Asset as SkeletonDataAsset; } // 自动加载 Debug.LogWarning($"BattleCacheManager: Spine cache miss for {key}, loading on-demand..."); var asset = ResManager.Instance.LoadAsset(directory, assetName); if (asset != null) { var identifier = new BattleResCache.ResourceIdentifier { Directory = directory, AssetName = assetName, Type = BattleResCache.ResourceType.Spine }; globalSpineCache[key] = new ResourceReference { CachedResource = new BattleResCache.CachedResource(identifier, asset, false) }; } return asset; } /// /// 获取或加载Audio资源 /// public AudioClip GetAudioResource(string directory, string assetName, string battleGuid = "") { string key = $"{directory}/{assetName}"; if (globalAudioCache.TryGetValue(key, out var refInfo)) { return refInfo.CachedResource.Asset as AudioClip; } // 自动加载 Debug.LogWarning($"BattleCacheManager: Audio cache miss for {key}, loading on-demand..."); var asset = ResManager.Instance.LoadAsset(directory, assetName, false); if (asset != null) { var identifier = new BattleResCache.ResourceIdentifier { Directory = directory, AssetName = assetName, Type = BattleResCache.ResourceType.Audio }; globalAudioCache[key] = new ResourceReference { CachedResource = new BattleResCache.CachedResource(identifier, asset, false) }; } return asset; } /// /// 更新已加载资源的引用(由加载器调用) /// public void UpdateResourceReference(string key, BattleResCache.CachedResource resource, string battleGuid, string ownerId) { if (resource.Identifier.Type == BattleResCache.ResourceType.Spine) { if (globalSpineCache.ContainsKey(key)) { globalSpineCache[key].CachedResource = resource; } } else if (resource.Identifier.Type == BattleResCache.ResourceType.Audio) { if (globalAudioCache.ContainsKey(key)) { globalAudioCache[key].CachedResource = resource; } } } public string GetCacheStats(string battleGuid = "") { int spineTotal = globalSpineCache.Count; int audioTotal = globalAudioCache.Count; return $"Spine: {spineTotal}, Audio: {audioTotal}"; } // ===== 编辑器调试接口 ===== #if UNITY_EDITOR // 编辑器模式下的资源引用调试类 public class ResourceReferenceDebug { public BattleResCache.CachedResource CachedResource; public Dictionary> BattlefieldOwners; public int RefCount { get { int count = 0; foreach (var owners in BattlefieldOwners.Values) { count += owners.Count; } return count; } } // 私有构造函数,只能由 DebugAPI 调用 internal ResourceReferenceDebug() { } } public static class DebugAPI { public static Dictionary GetSpineCache() { var result = new Dictionary(); foreach (var kvp in globalSpineCache) { // 直接在这里赋值,不通过构造函数传递私有类 result[kvp.Key] = new ResourceReferenceDebug { CachedResource = kvp.Value.CachedResource, BattlefieldOwners = kvp.Value.BattlefieldOwners }; } return result; } public static Dictionary GetAudioCache() { var result = new Dictionary(); foreach (var kvp in globalAudioCache) { result[kvp.Key] = new ResourceReferenceDebug { CachedResource = kvp.Value.CachedResource, BattlefieldOwners = kvp.Value.BattlefieldOwners }; } return result; } public static int GetTotalSpineCount() => globalSpineCache.Count; public static int GetTotalAudioCount() => globalAudioCache.Count; public static HashSet GetAllBattleGuids() { var guids = new HashSet(); foreach (var refInfo in globalSpineCache.Values) { foreach (var guid in refInfo.BattlefieldOwners.Keys) { guids.Add(guid); } } foreach (var refInfo in globalAudioCache.Values) { foreach (var guid in refInfo.BattlefieldOwners.Keys) { guids.Add(guid); } } return guids; } public static void ClearAllCache() { globalSpineCache.Clear(); globalAudioCache.Clear(); Debug.Log("BattleCacheManager: All cache cleared (Editor only)"); } } // 资源引用视图类(供编辑器使用) public class ResourceReferenceView { public string ResourceKey; public string ResourcePath; public bool IsLoaded; public int TotalRefCount; public Dictionary BattlefieldRefCounts = new Dictionary(); public ResourceReferenceView(string key, ResourceReferenceDebug refInfo) { ResourceKey = key; ResourcePath = refInfo.CachedResource.Identifier.Directory + "/" + refInfo.CachedResource.Identifier.AssetName; IsLoaded = refInfo.CachedResource.Asset != null; TotalRefCount = refInfo.RefCount; foreach (var kvp in refInfo.BattlefieldOwners) { BattlefieldRefCounts[kvp.Key] = kvp.Value.Count; } } } #endif }