//------------------------------------------------------------------ //本文件中的工具提供在编辑器环境下,快速整理特效相关的资源 //------------------------------------------------------------------- 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 RemoveUnUsedEffect { static string uiroot1 = "Assets/ResourcesOut/UI/Window"; static string uiroot2 = "Assets/ResourcesOut/UI/PriorityWindow"; static string uiroot3 = "Assets/ResourcesOut/UI/Prefab"; static string mobroot = "Assets/ResourcesOut/UI/Prefab"; static string builtinroot = "Assets/ResourcesOut/BuiltIn/Prefabs"; static string effectroot = "Assets/ResourcesOut/Effect"; 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("程序/Effect Assets/移除无用的特效")] public static void RemoveUnUsedEffects() { try { EditorApplication.update += OnUpdate; EffectConfig.Init(true); configs = EffectConfig.GetValues(); LoadPrefabTexts(); FindEffects(); taskCount = 0; completedTaskCount = 0; foreach (var task in tasks) { task.total = task.effects.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[] { mobroot })); guids.AddRange(AssetDatabase.FindAssets("t:prefab", new string[] { builtinroot })); var assetPaths = new List(); foreach (var item in guids) { assetPaths.Add(AssetDatabase.GUIDToAssetPath(item)); } var count = 0; prefabTexts.Clear(); 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(); } static void FindEffects() { processObjects.Clear(); tasks.Clear(); var effects = new List(); var guids = new List(); var subFolders = AssetDatabase.GetSubFolders(effectroot); foreach (var folder in subFolders) { guids.AddRange(AssetDatabase.FindAssets("t:prefab", new string[] { folder })); } foreach (var guid in guids) { effects.Add(AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(guid))); } var count = 0; var task = new AnalyzeTask(); tasks.Add(task); foreach (var asset in effects) { var path = AssetDatabase.GetAssetPath(asset); var guid = AssetDatabase.AssetPathToGUID(path); processObjects[guid] = asset; var folderName = Path.GetDirectoryName(path).Split('/').GetLast(); task.Add(new EffectInfo() { name = asset.name, guid = guid, folder = folderName }); count++; EditorUtility.DisplayProgressBar("获取特效", "正在获取特效预置体", (float)count / effects.Count); if (count >= 20) { count = 0; task = new AnalyzeTask(); tasks.Add(task); } } EditorUtility.ClearProgressBar(); } static void Analyze() { foreach (var task in tasks) { ThreadPool.QueueUserWorkItem(x => { for (int i = 0; i < task.effects.Count; i++) { task.completed++; var info = task.effects[i]; if (ContainByIconTable(info)) { continue; } if (RefrenceByPrefab(info)) { continue; } info.unUsed = true; task.effects[i] = info; } task.done = true; }); } } static void ProcessUnUsedEffects() { var total = 0; foreach (var task in tasks) { foreach (var effect in task.effects) { if (effect.unUsed) { Debug.LogFormat("找到一个无用的Effect:文件夹->{0};特效名称->{1}", effect.folder, effect.name); total++; } } } var count = 0; foreach (var task in tasks) { foreach (var sprite in task.effects) { if (sprite.unUsed) { EditorUtility.DisplayProgressBar("删除Effect", string.Format("正在删除第{0}个特效,共{1}个", count + 1, total), (float)count / total); count++; AssetDatabase.DeleteAsset(AssetDatabase.GUIDToAssetPath(sprite.guid)); } } } EditorUtility.ClearProgressBar(); } static bool ContainByIconTable(EffectInfo info) { return configs.FindIndex(x => { return x.packageName.ToLower() == info.folder.ToLower() && x.fxName == info.name; }) != -1; } static bool RefrenceByPrefab(EffectInfo 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("分析无用Effect", string.Format("正在分析第{0}个Effect,共计{1}个", completedTaskCount + 1, taskCount), (float)completedTaskCount / taskCount); if (done) { EditorUtility.ClearProgressBar(); ProcessUnUsedEffects(); EditorApplication.update -= OnUpdate; } } struct EffectInfo { 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 effects; public AnalyzeTask() { effects = new List(); } public void Add(EffectInfo info) { effects.Add(info); } } } public class RemoveUnUsedEffectKey { static string uiroot1 = "Assets/ResourcesOut/UI/Window"; static string uiroot2 = "Assets/ResourcesOut/UI/PriorityWindow"; static string uiroot3 = "Assets/ResourcesOut/UI/Prefab"; static string soskillroot = "Assets/ResourcesOut/Refdata/ScriptableObject/SoSkill"; static string newbieguideroot = "Assets/ResourcesOut/Refdata/ScriptableObject/SoNewBieGuide"; static int taskCount = 1; static int completedTaskCount = 0; static Dictionary> iconKeyMap = new Dictionary>(); static List uieffects = new List(); static List iconKeyRefrences = new List(); static List csharpFileContents = new List(); static List newbieGuides = new List(); static List soSkills = new List(); static List tasks = new List(); static string ignorekeys = string.Empty; [MenuItem("程序/Effect Assets/移除无用的EffectKey")] public static void Remove() { tasks.Clear(); var effectKeys = GetOriginalEffectKeys(); taskCount = effectKeys.Count; completedTaskCount = 0; var count = 0; var task = new AnalyzeTask(); tasks.Add(task); foreach (var keyInfo in effectKeys) { task.Add(keyInfo); if (count >= 100) { count = 0; task = new AnalyzeTask(); tasks.Add(task); } count++; } iconKeyMap = GetEffectKeyMap(); iconKeyRefrences = GetAllEffectKeyRefrences(); csharpFileContents = GetAllCSharpFileContents(); uieffects = GetEffectComponents(); soSkills = GetSoSkills(); newbieGuides = GetNewbieGuides(); ignorekeys = GetIngoreKeys(); 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 = 0; i < task.effectKeys.Count; i++) { task.completed++; var info = task.effectKeys[i]; if (info.folder.ToLower() == "cj") { continue; } if (IsIgnoreKey(info.key, ref ignorekeys)) { Debug.LogFormat("找到一个不要删除的 effect key:{0}", info.key); continue; } if (ContainBySoSkills(info.key, ref soSkills)) { Debug.LogFormat("找到一个被soskill依赖的 effect key:{0}", info.key); continue; } if (ContainByNewbieGuides(info.key, ref newbieGuides)) { Debug.LogFormat("找到一个被新手引导依赖的 effect key:{0}", info.key); continue; } if (ContainByPrefab(info.key, ref uieffects)) { Debug.LogFormat("找到一个被prefab依赖的 effect key:{0}", info.key); continue; } if (ContainByTables(info.key, ref iconKeyRefrences)) { Debug.LogFormat("找到一个被其他配置配件引用的 effect key:{0}", info.key); continue; } if (ContainByCSharpFile(info.key, ref csharpFileContents)) { Debug.LogFormat("找到一个写在C#文件中的 effect key:{0}", info.key); continue; } info.unused = true; task.effectKeys[i] = info; } task.done = true; }); } } static void ProcessUnUsedEffectKeys() { var lines = new List(File.ReadAllLines(Application.dataPath + "/ResourcesOut/Refdata/Config/Effect.txt")); var deleteLines = new List(); var unUsedEffectKeys = new List(); foreach (var task in tasks) { foreach (var iconkey in task.effectKeys) { if (iconkey.unused) { unUsedEffectKeys.Add(iconkey.key); } } } for (int i = lines.Count - 1; i >= 3; i--) { var contents = lines[i].Split('\t'); if (!contents.IsNullOrEmpty()) { if (unUsedEffectKeys.Contains(contents[0])) { deleteLines.Add(lines[i]); lines.RemoveAt(i); } } } File.WriteAllLines(Application.dataPath + "/删除的Effect表配置.txt", deleteLines.ToArray()); File.WriteAllLines(Application.dataPath + "/ResourcesOut/Refdata/Config/Effect.txt", lines.ToArray()); } static Dictionary> GetEffectKeyMap() { var lines = File.ReadAllLines(Application.dataPath + "/Editor/Config/TxtEffectKey.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 GetOriginalEffectKeys() { var lines = File.ReadAllLines(Application.dataPath + "/ResourcesOut/Refdata/Config/Effect.txt"); var effectKeys = new List(); for (int i = 3; i < lines.Length; i++) { var contents = lines[i].Split('\t'); if (!contents.IsNullOrEmpty()) { effectKeys.Add(new EffectKeyInfo() { key = contents[0], folder = contents[1] }); } } return effectKeys; } static List GetAllEffectKeyRefrences() { var files = FileExtersion.GetFileInfos(Application.dataPath + "/ResourcesOut/Refdata/Config", new string[] { "*.txt", "*.TXT" }); var columns = new List(); foreach (var file in files) { 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]); } } } 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 GetEffectComponents() { 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 assetPaths = new List(); foreach (var item in guids) { assetPaths.Add(AssetDatabase.GUIDToAssetPath(item)); } var count = 0; var uieffects = new List(); foreach (var path in assetPaths) { count++; EditorUtility.DisplayProgressBar("收集UIEffect", "正在搜索Prefab上的UIEffect", (float)count / assetPaths.Count); var gameObject = AssetDatabase.LoadAssetAtPath(path); if (gameObject == null) { continue; } var components = gameObject.GetComponentsInChildren(true); if (!components.IsNullOrEmpty()) { uieffects.AddRange(components); } } EditorUtility.ClearProgressBar(); return uieffects; } static List GetSoSkills() { var guids = new List(); guids.AddRange(AssetDatabase.FindAssets("t:ScriptableObject", new string[] { soskillroot })); var assetPaths = new List(); foreach (var item in guids) { assetPaths.Add(AssetDatabase.GUIDToAssetPath(item)); } var soskills = new List(); foreach (var path in assetPaths) { var soskill = AssetDatabase.LoadAssetAtPath(path); if (soskill != null) { soskills.Add(soskill); } } return soskills; } static List GetNewbieGuides() { var guids = new List(); guids.AddRange(AssetDatabase.FindAssets("t:ScriptableObject", new string[] { newbieguideroot })); var assetPaths = new List(); foreach (var item in guids) { assetPaths.Add(AssetDatabase.GUIDToAssetPath(item)); } var newbieguides = new List(); foreach (var path in assetPaths) { var so = AssetDatabase.LoadAssetAtPath(path); if (newbieguides != null) { newbieguides.Add(so); } } return newbieguides; } static string GetIngoreKeys() { return File.ReadAllText(Application.dataPath + "/Editor/Config/DeleteEffectExcludeKey.txt"); } 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) { var pattern1 = string.Format(@"SFXPlayUtility.Instance.PlayBattleEffect.*{0}", key); var pattern2 = string.Format(@"SFXPlayUtility.Instance.PlayEffectAsync.*{0}", key); var pattern3 = string.Format(@"SFXPlayUtility.Instance.PlayWithEulerAngle.*{0}", key); var pattern4 = string.Format(@"SFXPlayUtility.Instance.Play.*{0}", key); foreach (var content in csharpContents) { if (Regex.IsMatch(content, pattern1)) { return true; } if (Regex.IsMatch(content, pattern2)) { return true; } if (Regex.IsMatch(content, pattern3)) { return true; } if (Regex.IsMatch(content, pattern4)) { return true; } } return false; } static bool ContainByPrefab(string key, ref List uieffects) { foreach (var uieffect in uieffects) { if (uieffect.effect.ToString() == key) { return true; } } return false; } static bool ContainBySoSkills(string key, ref List soSkills) { foreach (var soskill in soSkills) { foreach (var frameEvent in soskill.animationEventList) { if (frameEvent.frameEventType == E_FrameEventType.OnPlayEffect) { if (frameEvent.intParam.ToString() == key) { return true; } } } } return false; } static bool ContainByNewbieGuides(string key, ref List newbieGuides) { foreach (var newbieGuide in newbieGuides) { if (newbieGuide.effect2.ToString() == key) { return true; } } return false; } static bool IsIgnoreKey(string key, ref string ignores) { return Regex.IsMatch(ignores, key); } static void OnUpdate() { var done = true; completedTaskCount = 0; foreach (var task in tasks) { completedTaskCount += task.completed; if (!task.done) { done = false; } } EditorUtility.DisplayProgressBar("分析无用EffectKey", string.Format("正在分析第{0}个EffectKey,共计{1}个", completedTaskCount + 1, taskCount), (float)completedTaskCount / taskCount); if (done) { EditorUtility.ClearProgressBar(); ProcessUnUsedEffectKeys(); EditorApplication.update -= OnUpdate; } } struct Column { public string name; public List values; } struct EffectKeyInfo { public string key; public string folder; public bool unused; } class AnalyzeTask { public int total; public int completed; public bool done; public List effectKeys; public AnalyzeTask() { effectKeys = new List(); } public void Add(EffectKeyInfo key) { effectKeys.Add(key); } } }