using UnityEngine;
|
using UnityEditor;
|
using System.Collections.Generic;
|
using System.Linq;
|
|
/// <summary>
|
/// 战斗预加载资源监控窗口
|
/// </summary>
|
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<BattleCacheManager.ResourceReferenceView> spineResourceViews = new List<BattleCacheManager.ResourceReferenceView>();
|
private List<BattleCacheManager.ResourceReferenceView> audioResourceViews = new List<BattleCacheManager.ResourceReferenceView>();
|
private HashSet<string> allBattleGuids = new HashSet<string>();
|
|
private GUIStyle headerStyle;
|
private GUIStyle evenRowStyle;
|
private GUIStyle oddRowStyle;
|
private GUIStyle loadedStyle;
|
private GUIStyle unloadedStyle;
|
|
[MenuItem("Tools/战斗/预加载资源监控")]
|
public static void ShowWindow()
|
{
|
var window = GetWindow<BattlePreloadResWatcher>("战斗资源监控");
|
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<BattleCacheManager.ResourceReferenceView> 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();
|
}
|
|
/// <summary>
|
/// 在编辑器中选中资源
|
/// </summary>
|
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<Object>(fullPath);
|
if (asset != null)
|
{
|
foundPath = fullPath;
|
break;
|
}
|
}
|
|
if (asset != null)
|
{
|
// 在 Project 窗口中选中并高亮
|
Selection.activeObject = asset;
|
EditorGUIUtility.PingObject(asset);
|
Debug.Log($"已选中资源: {foundPath}");
|
}
|
else
|
{
|
// 如果找不到资源,尝试使用 AssetDatabase.FindAssets 进行模糊搜索
|
string assetName = System.IO.Path.GetFileName(resourcePath);
|
string[] guids = AssetDatabase.FindAssets(assetName);
|
|
if (guids != null && guids.Length > 0)
|
{
|
// 找到匹配的资源,优先选择路径最匹配的
|
string bestMatch = null;
|
foreach (var guid in guids)
|
{
|
string assetPath = AssetDatabase.GUIDToAssetPath(guid);
|
if (assetPath.Contains(resourcePath))
|
{
|
bestMatch = assetPath;
|
break;
|
}
|
}
|
|
// 如果没有完全匹配,使用第一个结果
|
if (bestMatch == null)
|
{
|
bestMatch = AssetDatabase.GUIDToAssetPath(guids[0]);
|
}
|
|
asset = AssetDatabase.LoadAssetAtPath<Object>(bestMatch);
|
if (asset != null)
|
{
|
Selection.activeObject = asset;
|
EditorGUIUtility.PingObject(asset);
|
Debug.Log($"通过搜索找到资源: {bestMatch}\n(原始路径: {resourcePath})");
|
return;
|
}
|
}
|
|
Debug.LogWarning($"未找到资源: {resourcePath}\n尝试的基础路径: Assets/ResourcesOut/{resourcePath}[extensions]");
|
}
|
}
|
}
|