using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.Linq;
///
/// 战斗预加载资源监控窗口
///
public class BattlePreloadResWatcher : EditorWindow
{
private Vector2 scrollPosition;
private bool showSpineResources = true;
private bool showAudioResources = true;
private string filterText = "";
private bool autoRefresh = true;
private double lastRefreshTime = 0;
private const double AUTO_REFRESH_INTERVAL = 1.0; // 1秒自动刷新
private List spineResourceViews = new List();
private List audioResourceViews = new List();
private HashSet allBattleGuids = new HashSet();
private GUIStyle headerStyle;
private GUIStyle evenRowStyle;
private GUIStyle oddRowStyle;
private GUIStyle loadedStyle;
private GUIStyle unloadedStyle;
[MenuItem("Tools/战斗/预加载资源监控")]
public static void ShowWindow()
{
var window = GetWindow("战斗资源监控");
window.minSize = new Vector2(800, 400);
window.Show();
}
private void OnEnable()
{
RefreshData();
}
private void OnGUI()
{
InitStyles();
// 顶部工具栏
DrawToolbar();
// 统计信息
DrawStatistics();
GUILayout.Space(5);
// 滚动区域
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
// Spine资源
if (showSpineResources)
{
DrawResourceSection("Spine 资源", spineResourceViews);
}
GUILayout.Space(10);
// Audio资源
if (showAudioResources)
{
DrawResourceSection("Audio 资源", audioResourceViews);
}
EditorGUILayout.EndScrollView();
// 自动刷新
if (autoRefresh && EditorApplication.timeSinceStartup - lastRefreshTime > AUTO_REFRESH_INTERVAL)
{
RefreshData();
lastRefreshTime = EditorApplication.timeSinceStartup;
Repaint();
}
}
private void InitStyles()
{
if (headerStyle == null)
{
headerStyle = new GUIStyle(EditorStyles.boldLabel)
{
fontSize = 14,
normal = { textColor = Color.white }
};
evenRowStyle = new GUIStyle(EditorStyles.label)
{
normal = { background = MakeTexture(2, 2, new Color(0.3f, 0.3f, 0.3f, 0.1f)) }
};
oddRowStyle = new GUIStyle(EditorStyles.label)
{
normal = { background = MakeTexture(2, 2, new Color(0.3f, 0.3f, 0.3f, 0.3f)) }
};
loadedStyle = new GUIStyle(EditorStyles.label)
{
normal = { textColor = Color.green }
};
unloadedStyle = new GUIStyle(EditorStyles.label)
{
normal = { textColor = Color.red }
};
}
}
private Texture2D MakeTexture(int width, int height, Color color)
{
Color[] pixels = new Color[width * height];
for (int i = 0; i < pixels.Length; i++)
{
pixels[i] = color;
}
Texture2D texture = new Texture2D(width, height);
texture.SetPixels(pixels);
texture.Apply();
return texture;
}
private void DrawToolbar()
{
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
if (GUILayout.Button("刷新数据", EditorStyles.toolbarButton, GUILayout.Width(80)))
{
RefreshData();
}
autoRefresh = GUILayout.Toggle(autoRefresh, "自动刷新", EditorStyles.toolbarButton, GUILayout.Width(80));
GUILayout.Space(10);
showSpineResources = GUILayout.Toggle(showSpineResources, "显示Spine", EditorStyles.toolbarButton, GUILayout.Width(80));
showAudioResources = GUILayout.Toggle(showAudioResources, "显示Audio", EditorStyles.toolbarButton, GUILayout.Width(80));
GUILayout.Space(10);
GUILayout.Label("筛选:", GUILayout.Width(40));
filterText = GUILayout.TextField(filterText, EditorStyles.toolbarTextField, GUILayout.Width(200));
GUILayout.FlexibleSpace();
if (GUILayout.Button("清空缓存", EditorStyles.toolbarButton, GUILayout.Width(80)))
{
if (EditorUtility.DisplayDialog("确认", "确定要清空所有战斗资源缓存吗?", "确定", "取消"))
{
BattleCacheManager.DebugAPI.ClearAllCache();
RefreshData();
}
}
EditorGUILayout.EndHorizontal();
}
private void DrawStatistics()
{
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
EditorGUILayout.LabelField("缓存统计", EditorStyles.boldLabel);
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField($"Spine 资源总数: {spineResourceViews.Count}", GUILayout.Width(200));
EditorGUILayout.LabelField($"Audio 资源总数: {audioResourceViews.Count}", GUILayout.Width(200));
EditorGUILayout.LabelField($"战场数量: {allBattleGuids.Count}", GUILayout.Width(200));
EditorGUILayout.EndHorizontal();
int spineLoaded = spineResourceViews.Count(v => v.IsLoaded);
int audioLoaded = audioResourceViews.Count(v => v.IsLoaded);
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField($"Spine 已加载: {spineLoaded}", GUILayout.Width(200));
EditorGUILayout.LabelField($"Audio 已加载: {audioLoaded}", GUILayout.Width(200));
EditorGUILayout.LabelField($"总引用计数: {spineResourceViews.Sum(v => v.TotalRefCount) + audioResourceViews.Sum(v => v.TotalRefCount)}", GUILayout.Width(200));
EditorGUILayout.EndHorizontal();
// 战场列表
if (allBattleGuids.Count > 0)
{
GUILayout.Space(5);
EditorGUILayout.LabelField("活跃战场:", EditorStyles.boldLabel);
EditorGUILayout.BeginHorizontal();
foreach (var guid in allBattleGuids)
{
string displayGuid = string.IsNullOrEmpty(guid) ? "[主战场]" : guid.Substring(0, Mathf.Min(8, guid.Length)) + "...";
EditorGUILayout.LabelField($"• {displayGuid}", GUILayout.Width(150));
}
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.EndVertical();
}
private void DrawResourceSection(string title, List resources)
{
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
EditorGUILayout.LabelField(title, headerStyle);
GUILayout.Space(5);
// 表头
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
EditorGUILayout.LabelField("资源路径", EditorStyles.toolbarButton, GUILayout.Width(600));
EditorGUILayout.LabelField("状态", EditorStyles.toolbarButton, GUILayout.Width(60));
EditorGUILayout.LabelField("引用计数", EditorStyles.toolbarButton, GUILayout.Width(80));
EditorGUILayout.LabelField("战场分布", EditorStyles.toolbarButton, GUILayout.ExpandWidth(true));
EditorGUILayout.EndHorizontal();
// 筛选资源
var filteredResources = resources;
if (!string.IsNullOrEmpty(filterText))
{
filteredResources = resources.Where(r => r.ResourcePath.ToLower().Contains(filterText.ToLower())).ToList();
}
// 创建居中样式
GUIStyle centeredStyle = new GUIStyle(GUI.skin.label);
centeredStyle.alignment = TextAnchor.MiddleCenter;
GUIStyle centeredLoadedStyle = new GUIStyle(loadedStyle);
centeredLoadedStyle.alignment = TextAnchor.MiddleCenter;
GUIStyle centeredUnloadedStyle = new GUIStyle(unloadedStyle);
centeredUnloadedStyle.alignment = TextAnchor.MiddleCenter;
// 资源路径样式(可点击)
GUIStyle pathStyle = new GUIStyle(GUI.skin.label);
pathStyle.wordWrap = false;
pathStyle.normal.textColor = new Color(0.4f, 0.7f, 1f); // 淡蓝色表示可点击
// 资源行
for (int i = 0; i < filteredResources.Count; i++)
{
var resource = filteredResources[i];
var rowStyle = (i % 2 == 0) ? evenRowStyle : oddRowStyle;
EditorGUILayout.BeginHorizontal(rowStyle);
// 资源路径(可点击,添加 Tooltip)
GUIContent pathContent = new GUIContent(resource.ResourcePath, "点击选中资源\n" + resource.ResourcePath);
Rect pathRect = GUILayoutUtility.GetRect(pathContent, pathStyle, GUILayout.Width(600));
if (GUI.Button(pathRect, pathContent, pathStyle))
{
// 点击时选中资源
SelectAssetInEditor(resource.ResourcePath);
}
// 鼠标悬停时显示手型光标
EditorGUIUtility.AddCursorRect(pathRect, MouseCursor.Link);
// 加载状态(居中)
var statusStyle = resource.IsLoaded ? centeredLoadedStyle : centeredUnloadedStyle;
EditorGUILayout.LabelField(resource.IsLoaded ? "已加载" : "未加载", statusStyle, GUILayout.Width(60));
// 引用计数(居中)
EditorGUILayout.LabelField(resource.TotalRefCount.ToString(), centeredStyle, GUILayout.Width(80));
// 战场分布
string battleInfo = string.Join(", ", resource.BattlefieldRefCounts.Select(kvp =>
{
string guidDisplay = string.IsNullOrEmpty(kvp.Key) ? "[主]" : kvp.Key.Substring(0, Mathf.Min(4, kvp.Key.Length));
return $"{guidDisplay}({kvp.Value})";
}));
EditorGUILayout.LabelField(battleInfo, GUILayout.ExpandWidth(true));
EditorGUILayout.EndHorizontal();
}
if (filteredResources.Count == 0)
{
EditorGUILayout.LabelField("没有资源数据", EditorStyles.centeredGreyMiniLabel);
}
EditorGUILayout.EndVertical();
}
private void RefreshData()
{
spineResourceViews.Clear();
audioResourceViews.Clear();
allBattleGuids.Clear();
// 获取Spine资源
var spineCache = BattleCacheManager.DebugAPI.GetSpineCache();
foreach (var kvp in spineCache)
{
spineResourceViews.Add(new BattleCacheManager.ResourceReferenceView(kvp.Key, kvp.Value));
}
// 获取Audio资源
var audioCache = BattleCacheManager.DebugAPI.GetAudioCache();
foreach (var kvp in audioCache)
{
audioResourceViews.Add(new BattleCacheManager.ResourceReferenceView(kvp.Key, kvp.Value));
}
// 获取所有战场GUID
allBattleGuids = BattleCacheManager.DebugAPI.GetAllBattleGuids();
// 排序
spineResourceViews = spineResourceViews.OrderBy(v => v.ResourcePath).ToList();
audioResourceViews = audioResourceViews.OrderBy(v => v.ResourcePath).ToList();
}
///
/// 在编辑器中选中资源
///
private void SelectAssetInEditor(string resourcePath)
{
// resourcePath 格式: directory/assetName (例如: Battle/Audio/bgm_001)
// 需要转换为: Assets/ResourcesOut/{directory}/{assetName}{extension}
// 尝试多种可能的扩展名
string[] possibleExtensions = new string[]
{
".asset", // SkeletonDataAsset
".prefab", // Prefabs
".png", // Textures
".jpg", // Textures
".wav", // Audio
".mp3", // Audio
".ogg", // Audio
".bytes", // Binary data
"", // 无扩展名
};
Object asset = null;
string foundPath = null;
// 尝试所有可能的路径
foreach (var ext in possibleExtensions)
{
string fullPath = $"Assets/ResourcesOut/{resourcePath}{ext}";
asset = AssetDatabase.LoadAssetAtPath