//------------------------------------------------------------------ //本文件中的工具提供在编辑器环境下,快速整理UI图片相关的资源 //------------------------------------------------------------------- using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; using UnityEngine.UI; using System.IO; using System; using System.Threading; using System.Text.RegularExpressions; public class WindowImageCheck { static string windowRootPath = Application.dataPath + "/ResourcesOut/UI/Window"; static string windowRootPath2 = Application.dataPath + "/ResourcesOut/UI/PriorityWindow"; static string prefabRootPath = Application.dataPath + "/ResourcesOut/UI/Prefab"; [MenuItem("程序/UI Assets/查找和移除无用的Image")] public static void CheckAndReplaceFontSwitch() { try { var allFiles = new List(); allFiles.AddRange(new DirectoryInfo(windowRootPath).GetFiles("*.prefab", SearchOption.TopDirectoryOnly)); allFiles.AddRange(new DirectoryInfo(windowRootPath2).GetFiles("*.prefab", SearchOption.TopDirectoryOnly)); allFiles.AddRange(new DirectoryInfo(prefabRootPath).GetFiles("*.prefab", SearchOption.TopDirectoryOnly)); var count = allFiles.Count; var index = 0; foreach (var file in allFiles) { var assetPath = file.FullName.Replace("\\", "/").Replace(Application.dataPath, "Assets"); var prefab = AssetDatabase.LoadAssetAtPath(assetPath); var images = prefab.GetComponentsInChildren(true); foreach (var image in images) { if (image != null) { if (!image.enabled || image.color.a == 0f) { path = string.Empty; Debug.Log(GetComponentPath(image.transform, ref path)); MonoBehaviour.DestroyImmediate(image, true); } } } index++; EditorUtility.SetDirty(prefab); EditorUtility.DisplayProgressBar("检测和移除Image", StringUtility.Contact("正在检测:", prefab.name), (float)index / count); } AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); EditorUtility.ClearProgressBar(); } catch (Exception ex) { Debug.Log(ex); } } static string path; private static string GetComponentPath(Transform _transform, ref string _path) { path = _transform.gameObject.name + path; if (_transform.parent != null) { path = "/" + path; return GetComponentPath(_transform.parent, ref _path); } else { return path; } } } public class RemoveUnUsedSpriteQuickly { static string uiroot1 = "Assets/ResourcesOut/UI/Window"; static string uiroot2 = "Assets/ResourcesOut/UI/PriorityWindow"; static string uiroot3 = "Assets/ResourcesOut/UI/Prefab"; static string uiroot4 = "Assets/ResourcesOut/UI/Treasure/Misc"; static string spriteroot = "Assets/ResourcesOut/UI/Sprite"; static List prefabTexts = new List(); static Dictionary processObjects = new Dictionary(); static List tasks = new List(); static int taskCount = 1; static int completedTaskCount = 0; static List configs; [MenuItem("程序/UI Assets/移除无用的Sprite")] public static void RemoveUnUsedSprites() { try { EditorApplication.update += OnUpdate; IconConfig.Init(true); configs = IconConfig.GetValues(); LoadPrefabTexts(); FindSprites(); taskCount = 0; completedTaskCount = 0; foreach (var task in tasks) { task.total = task.sprites.Count; taskCount += task.total; } Analyze(); } catch (Exception e) { EditorApplication.update -= OnUpdate; EditorUtility.ClearProgressBar(); } } static void LoadPrefabTexts() { var guids = new List(); guids.AddRange(AssetDatabase.FindAssets("t:prefab", new string[] { uiroot1 })); guids.AddRange(AssetDatabase.FindAssets("t:prefab", new string[] { uiroot2 })); guids.AddRange(AssetDatabase.FindAssets("t:prefab", new string[] { uiroot3 })); guids.AddRange(AssetDatabase.FindAssets("t:prefab", new string[] { uiroot4 })); var assetPaths = new List(); foreach (var item in guids) { assetPaths.Add(AssetDatabase.GUIDToAssetPath(item)); } var totalCount = assetPaths.Count; var count = 0; prefabTexts.Clear(); foreach (var path in assetPaths) { count++; EditorUtility.DisplayProgressBar("读取预置体文本", "正在读取预置体文本", count / (float)totalCount); prefabTexts.Add(File.ReadAllText(Application.dataPath + path.Substring(6, path.Length - 6))); } EditorUtility.ClearProgressBar(); } static void FindSprites() { processObjects.Clear(); tasks.Clear(); var sprites = new List(); var guids = new List(); var subFolders = AssetDatabase.GetSubFolders(spriteroot); foreach (var folder in subFolders) { guids.AddRange(AssetDatabase.FindAssets("t:sprite", new string[] { folder })); } foreach (var guid in guids) { sprites.Add(AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(guid))); } var count = 0; var task = new AnalyzeTask(); tasks.Add(task); foreach (var asset in sprites) { var path = AssetDatabase.GetAssetPath(asset); var guid = AssetDatabase.AssetPathToGUID(path); processObjects[guid] = asset; var folderName = Path.GetDirectoryName(path).Split('/').GetLast(); if (folderName == "Frame") { continue; } task.Add(new SpriteInfo() { name = asset.name, guid = guid, folder = folderName }); count++; if (count >= 500) { count = 0; task = new AnalyzeTask(); tasks.Add(task); } } } static void Analyze() { foreach (var task in tasks) { ThreadPool.QueueUserWorkItem(x => { for (int i = 0; i < task.sprites.Count; i++) { task.completed++; var info = task.sprites[i]; if (ContainByIconTable(info)) { continue; } if (RefrenceByPrefab(info)) { continue; } info.unUsed = true; task.sprites[i] = info; } task.done = true; }); } } static void ProcessUnUsedSprites() { var total = 0; foreach (var task in tasks) { foreach (var sprite in task.sprites) { if (sprite.unUsed) { Debug.LogFormat("找到一张无用的图片:文件夹->{0};图片名称->{1}", sprite.folder, sprite.name); total++; } } } var count = 0; foreach (var task in tasks) { foreach (var sprite in task.sprites) { if (sprite.unUsed) { EditorUtility.DisplayProgressBar("删除Sprite", string.Format("正在删除第{0}张图片,共{1}张", count + 1, total), (float)count / total); count++; AssetDatabase.DeleteAsset(AssetDatabase.GUIDToAssetPath(sprite.guid)); } } } EditorUtility.ClearProgressBar(); } static bool ContainByIconTable(SpriteInfo info) { return configs.FindIndex(x => { return x.folder.ToLower() == info.folder.ToLower() && x.sprite == info.name; }) != -1; } static bool RefrenceByPrefab(SpriteInfo info) { foreach (var content in prefabTexts) { if (Regex.IsMatch(content, info.guid)) { return true; } } return false; } static void OnUpdate() { var done = true; completedTaskCount = 0; foreach (var task in tasks) { completedTaskCount += task.completed; if (!task.done) { done = false; } } EditorUtility.DisplayProgressBar("分析无用Sprite", string.Format("正在分析第{0}张Sprite,共计{1}张", completedTaskCount + 1, taskCount), (float)completedTaskCount / taskCount); if (done) { EditorUtility.ClearProgressBar(); ProcessUnUsedSprites(); EditorApplication.update -= OnUpdate; } } struct SpriteInfo { public string guid; public string name; public string folder; public bool unUsed; } class AnalyzeTask { public int total; public int completed; public bool done; public List sprites; public AnalyzeTask() { sprites = new List(); } public void Add(SpriteInfo info) { sprites.Add(info); } } } public class FindUnUsedPrefab { static string uiroot1 = "Assets/ResourcesOut/UI/Window"; static string uiroot2 = "Assets/ResourcesOut/UI/PriorityWindow"; static string uiroot3 = "Assets/ResourcesOut/UI/Prefab"; [MenuItem("程序/UI Assets/找到可疑的UI Prefab对象")] public static void FindUnnormalObjects() { var guids = new List(); guids.AddRange(AssetDatabase.FindAssets("t:prefab", new string[] { uiroot1 })); guids.AddRange(AssetDatabase.FindAssets("t:prefab", new string[] { uiroot2 })); guids.AddRange(AssetDatabase.FindAssets("t:prefab", new string[] { uiroot3 })); var assets = new List(); foreach (var guid in guids) { var go = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(guid)); if (go != null) { assets.Add(go); } } foreach (var item in assets) { var so = new SerializedObject(item); var soProperties = so.FindProperty("m_Component"); var components = item.GetComponents(); int propertyIndex = 0; foreach (var c in components) { if (c == null) { Debug.LogFormat("资源{0}存在可疑错误,请检查", item.name); } ++propertyIndex; } } } } public class RemoveUnUsedIconKey { static string uiroot1 = "Assets/ResourcesOut/UI/Window"; static string uiroot2 = "Assets/ResourcesOut/UI/PriorityWindow"; static string uiroot3 = "Assets/ResourcesOut/UI/Prefab"; static string uiroot4 = "Assets/ResourcesOut/UI/Treasure/Misc"; static List ignoreKeyList = new List() { "Remark_","GemTypeMini_","AllianceBossRank_","GemTypeMini_","RealmSelectBottom_","XT_TJ_","Fb_", "MultipleExp_Icon_","MapNPC_Colour_","LocalMapTaskState_","EquipDefaultIcon_","OpenServerActivty_","ChatIcon_" }; static int taskCount = 1; static int completedTaskCount = 0; static Dictionary> iconKeyMap = new Dictionary>(); static List prefabTexts = new List(); static List iconKeyRefrences = new List(); static List csharpFileContents = new List(); static string generalContent = string.Empty; static List tasks = new List(); static List scriptableObjects = new List(); [MenuItem("程序/UI Assets/移除无用的IconKey")] public static void Remove() { tasks.Clear(); var iconKeys = GetOriginalIconKeys(); taskCount = iconKeys.Count; completedTaskCount = 0; var count = 0; var task = new AnalyzeTask(); tasks.Add(task); foreach (var key in iconKeys) { task.Add(new IconKeyInfo() { key = key, unused = false }); if (count >= 200) { count = 0; task = new AnalyzeTask(); tasks.Add(task); } count++; } iconKeyMap = GetIconKeyMap(); iconKeyRefrences = GetAllIconKeyRefrences(); csharpFileContents = GetAllCSharpFileContents(); generalContent = GetGeneralContent(); prefabTexts = GetPrefabTexts(); scriptableObjects = GetScriptableObjects(); try { EditorApplication.update += OnUpdate; Analyze(); } catch (Exception e) { EditorUtility.ClearProgressBar(); EditorApplication.update -= OnUpdate; Debug.Log(e); } } static void Analyze() { foreach (var task in tasks) { ThreadPool.QueueUserWorkItem(x => { for (int i = task.iconKeys.Count - 1; i >= 0; i--) { task.completed++; var info = task.iconKeys[i]; if (ContainByTables(info.key, ref iconKeyRefrences)) { // Debug.LogFormat("找到一个被其他配置配件引用的 icon key:{0}", info.key); continue; } if (ContainByGeneralConfig(info.key, ref generalContent)) { // Debug.LogFormat("找到一个写在功能配置表中的 icon key:{0}", info.key); continue; } if (ContainBySriptableObjects(info.key, ref scriptableObjects)) { // Debug.LogFormat("找到一个配置在ScriptObject文件中的 icon key:{0}", info.key); continue; } if (ContainByCSharpFile(info.key, ref csharpFileContents)) { // Debug.LogFormat("找到一个写在C#文件中的 icon key:{0}", info.key); continue; } if (ContainByPrefab(info.key, ref prefabTexts)) { //Debug.LogFormat("找到一个被prefab依赖的 icon key:{0}", info.key); continue; } info.unused = true; task.iconKeys[i] = info; } task.done = true; }); } } static void ProcessUnUsedIconKeys() { var lines = new List(File.ReadAllLines(Application.dataPath + "/ResourcesOut/Refdata/Config/Icon.txt")); var deleteLines = new List(); var unUsedIconKeys = new List(); foreach (var task in tasks) { foreach (var iconkey in task.iconKeys) { if (iconkey.unused) { unUsedIconKeys.Add(iconkey.key); } } } for (int i = lines.Count - 1; i >= 3; i--) { var contents = lines[i].Split('\t'); if (!contents.IsNullOrEmpty()) { if (unUsedIconKeys.Contains(contents[0])) { deleteLines.Add(lines[i]); lines.RemoveAt(i); } } } File.WriteAllLines(Application.dataPath + "/删除的Icon表配置.txt", deleteLines.ToArray()); File.WriteAllLines(Application.dataPath + "/ResourcesOut/Refdata/Config/Icon.txt", lines.ToArray()); } static Dictionary> GetIconKeyMap() { var lines = File.ReadAllLines(Application.dataPath + "/Editor/Config/TxtIconKeys.txt"); var map = new Dictionary>(); for (int i = 1; i < lines.Length; i++) { var line = lines[i]; var contents = new List(line.Split('\t')); if (!contents.IsNullOrEmpty() && contents.Count >= 2) { var fields = map[contents[0]] = new List(); fields.AddRange(contents.GetRange(1, contents.Count - 1)); } } return map; } static List GetOriginalIconKeys() { var lines = File.ReadAllLines(Application.dataPath + "/ResourcesOut/Refdata/Config/Icon.txt"); var iconKeys = new List(); for (int i = 3; i < lines.Length; i++) { var contents = lines[i].Split('\t'); if (!contents.IsNullOrEmpty()) { iconKeys.Add(contents[0]); } } return iconKeys; } static List GetAllIconKeyRefrences() { var files = FileExtersion.GetFileInfos(Application.dataPath + "/ResourcesOut/Refdata/Config", new string[] { "*.txt", "*.TXT" }); var count = 0; var columns = new List(); foreach (var file in files) { count++; EditorUtility.DisplayProgressBar("解析配置文件", "正在解析配置文件", (float)count / files.Count); var nameWithoutExtension = Path.GetFileNameWithoutExtension(file.FullName); if (!iconKeyMap.ContainsKey(nameWithoutExtension)) { continue; } var lines = File.ReadAllLines(file.FullName); var fields0 = new List(lines[0].Split('\t')); var fields1 = new List(lines[1].Split('\t')); var refrences = iconKeyMap[nameWithoutExtension]; foreach (var refrence in refrences) { var name = string.Empty; var index = fields0.IndexOf(refrence); if (index != -1) { name = fields0[index]; } else { index = fields1.IndexOf(refrence); if (index != -1) { name = fields1[index]; } } if (index == -1) { continue; } var column = new Column() { name = name, values = new List() }; columns.Add(column); for (int i = 1; i < lines.Length; i++) { var line = lines[i]; var contents = line.Split('\t'); column.values.Add(contents[index]); } } } EditorUtility.ClearProgressBar(); return columns; } static List GetAllCSharpFileContents() { var files = FileExtersion.GetFileInfos(Application.dataPath + "/Scripts", new string[] { "*.cs" }); var contents = new List(); var count = 0; foreach (var file in files) { count++; EditorUtility.DisplayProgressBar("读取代码文件", "正在读取代码文件", (float)count / files.Count); contents.Add(File.ReadAllText(file.FullName)); } EditorUtility.ClearProgressBar(); return contents; } static List GetPrefabTexts() { var guids = new List(); guids.AddRange(AssetDatabase.FindAssets("t:prefab", new string[] { uiroot1 })); guids.AddRange(AssetDatabase.FindAssets("t:prefab", new string[] { uiroot2 })); guids.AddRange(AssetDatabase.FindAssets("t:prefab", new string[] { uiroot3 })); guids.AddRange(AssetDatabase.FindAssets("t:prefab", new string[] { uiroot4 })); var assetPaths = new List(); foreach (var item in guids) { assetPaths.Add(AssetDatabase.GUIDToAssetPath(item)); } var count = 0; var prefabTexts = new List(); foreach (var path in assetPaths) { count++; EditorUtility.DisplayProgressBar("读取预置体文本", "正在读取预置体文本", (float)count / assetPaths.Count); prefabTexts.Add(File.ReadAllText(Application.dataPath + path.Substring(6, path.Length - 6))); } EditorUtility.ClearProgressBar(); return prefabTexts; } static string GetGeneralContent() { return File.ReadAllText(Application.dataPath + "/ResourcesOut/Refdata/Config/FuncConfig.txt"); } static List GetScriptableObjects() { var guids = new List(); guids.AddRange(AssetDatabase.FindAssets("t:ScriptableObject", new string[] { "Assets/ResourcesOut/BuiltIn/ScriptableObjects" })); var assetPaths = new List(); foreach (var item in guids) { assetPaths.Add(AssetDatabase.GUIDToAssetPath(item)); } var count = 0; var scriptableObjects = new List(); foreach (var path in assetPaths) { count++; EditorUtility.DisplayProgressBar("读取ScriptableObject", "正在读取ScriptableObject", (float)count / assetPaths.Count); scriptableObjects.Add(File.ReadAllText(Application.dataPath + path.Substring(6, path.Length - 6))); } EditorUtility.ClearProgressBar(); return scriptableObjects; } static bool ContainByTables(string key, ref List columns) { foreach (var column in columns) { foreach (var value in column.values) { if (Regex.IsMatch(value, key)) { return true; } } } return false; } static bool ContainByCSharpFile(string key, ref List csharpContents) { foreach (var ignore in ignoreKeyList) { if (Regex.IsMatch(key, ignore)) { return true; } } var pattern = string.Format(@"\.SetSprite.*{0}.*\);", key); foreach (var content in csharpContents) { if (Regex.IsMatch(content, pattern)) { return true; } } return false; } static bool ContainByGeneralConfig(string key, ref string content) { return Regex.IsMatch(content, key); } static bool ContainByPrefab(string key, ref List prefabTexts) { foreach (var text in prefabTexts) { if (Regex.IsMatch(text, key)) { return true; } } return false; } static bool ContainBySriptableObjects(string key, ref List scriptableObjects) { foreach (var text in scriptableObjects) { if (Regex.IsMatch(text, key)) { return true; } } return false; } static void OnUpdate() { var done = true; completedTaskCount = 0; foreach (var task in tasks) { completedTaskCount += task.completed; if (!task.done) { done = false; } } EditorUtility.DisplayProgressBar("分析无用IconKey", string.Format("正在分析第{0}个IconKey,共计{1}个", completedTaskCount + 1, taskCount), (float)completedTaskCount / taskCount); if (done) { EditorUtility.ClearProgressBar(); ProcessUnUsedIconKeys(); EditorApplication.update -= OnUpdate; } } struct Column { public string name; public List values; } struct IconKeyInfo { public string key; public bool unused; } class AnalyzeTask { public int total; public int completed; public bool done; public List iconKeys; public AnalyzeTask() { iconKeys = new List(); } public void Add(IconKeyInfo key) { iconKeys.Add(key); } } }