//------------------------------------------------------------------
|
//本文件中的工具提供在编辑器环境下,快速整理特效相关的资源
|
//-------------------------------------------------------------------
|
|
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<string> prefabTexts = new List<string>();
|
|
static Dictionary<string, GameObject> processObjects = new Dictionary<string, GameObject>();
|
static List<AnalyzeTask> tasks = new List<AnalyzeTask>();
|
|
static int taskCount = 1;
|
static int completedTaskCount = 0;
|
static List<EffectConfig> 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<string>();
|
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<string>();
|
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<GameObject>();
|
var guids = new List<string>();
|
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<GameObject>(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<EffectInfo> effects;
|
|
public AnalyzeTask()
|
{
|
effects = new List<EffectInfo>();
|
}
|
|
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<string, List<string>> iconKeyMap = new Dictionary<string, List<string>>();
|
|
static List<UIEffect> uieffects = new List<UIEffect>();
|
static List<Column> iconKeyRefrences = new List<Column>();
|
static List<string> csharpFileContents = new List<string>();
|
static List<NewBieGuideScriptableObject> newbieGuides = new List<NewBieGuideScriptableObject>();
|
static List<SoSkill> soSkills = new List<SoSkill>();
|
static List<AnalyzeTask> tasks = new List<AnalyzeTask>();
|
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<string>(File.ReadAllLines(Application.dataPath + "/ResourcesOut/Refdata/Config/Effect.txt"));
|
var deleteLines = new List<string>();
|
var unUsedEffectKeys = new List<string>();
|
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<string, List<string>> GetEffectKeyMap()
|
{
|
var lines = File.ReadAllLines(Application.dataPath + "/Editor/Config/TxtEffectKey.txt");
|
var map = new Dictionary<string, List<string>>();
|
|
for (int i = 1; i < lines.Length; i++)
|
{
|
var line = lines[i];
|
var contents = new List<string>(line.Split('\t'));
|
if (!contents.IsNullOrEmpty() && contents.Count >= 2)
|
{
|
var fields = map[contents[0]] = new List<string>();
|
fields.AddRange(contents.GetRange(1, contents.Count - 1));
|
}
|
}
|
|
return map;
|
}
|
|
static List<EffectKeyInfo> GetOriginalEffectKeys()
|
{
|
var lines = File.ReadAllLines(Application.dataPath + "/ResourcesOut/Refdata/Config/Effect.txt");
|
var effectKeys = new List<EffectKeyInfo>();
|
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<Column> GetAllEffectKeyRefrences()
|
{
|
var files = FileExtersion.GetFileInfos(Application.dataPath + "/ResourcesOut/Refdata/Config", new string[] { "*.txt", "*.TXT" });
|
|
var columns = new List<Column>();
|
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<string>(lines[0].Split('\t'));
|
var fields1 = new List<string>(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<string>()
|
};
|
|
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<string> GetAllCSharpFileContents()
|
{
|
var files = FileExtersion.GetFileInfos(Application.dataPath + "/Scripts", new string[] { "*.cs" });
|
var contents = new List<string>();
|
|
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<UIEffect> GetEffectComponents()
|
{
|
var guids = new List<string>();
|
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<string>();
|
foreach (var item in guids)
|
{
|
assetPaths.Add(AssetDatabase.GUIDToAssetPath(item));
|
}
|
|
var count = 0;
|
var uieffects = new List<UIEffect>();
|
foreach (var path in assetPaths)
|
{
|
count++;
|
EditorUtility.DisplayProgressBar("收集UIEffect", "正在搜索Prefab上的UIEffect", (float)count / assetPaths.Count);
|
var gameObject = AssetDatabase.LoadAssetAtPath<GameObject>(path);
|
if (gameObject == null)
|
{
|
continue;
|
}
|
|
var components = gameObject.GetComponentsInChildren<UIEffect>(true);
|
if (!components.IsNullOrEmpty())
|
{
|
uieffects.AddRange(components);
|
}
|
}
|
|
EditorUtility.ClearProgressBar();
|
return uieffects;
|
}
|
|
static List<SoSkill> GetSoSkills()
|
{
|
var guids = new List<string>();
|
guids.AddRange(AssetDatabase.FindAssets("t:ScriptableObject", new string[] { soskillroot }));
|
|
var assetPaths = new List<string>();
|
foreach (var item in guids)
|
{
|
assetPaths.Add(AssetDatabase.GUIDToAssetPath(item));
|
}
|
|
var soskills = new List<SoSkill>();
|
foreach (var path in assetPaths)
|
{
|
var soskill = AssetDatabase.LoadAssetAtPath<SoSkill>(path);
|
if (soskill != null)
|
{
|
soskills.Add(soskill);
|
}
|
}
|
|
return soskills;
|
}
|
|
static List<NewBieGuideScriptableObject> GetNewbieGuides()
|
{
|
var guids = new List<string>();
|
guids.AddRange(AssetDatabase.FindAssets("t:ScriptableObject", new string[] { newbieguideroot }));
|
|
var assetPaths = new List<string>();
|
foreach (var item in guids)
|
{
|
assetPaths.Add(AssetDatabase.GUIDToAssetPath(item));
|
}
|
|
var newbieguides = new List<NewBieGuideScriptableObject>();
|
foreach (var path in assetPaths)
|
{
|
var so = AssetDatabase.LoadAssetAtPath<NewBieGuideScriptableObject>(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<Column> 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<string> 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<UIEffect> uieffects)
|
{
|
foreach (var uieffect in uieffects)
|
{
|
if (uieffect.effect.ToString() == key)
|
{
|
return true;
|
}
|
}
|
|
return false;
|
}
|
|
static bool ContainBySoSkills(string key, ref List<SoSkill> 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<NewBieGuideScriptableObject> 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<string> 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<EffectKeyInfo> effectKeys;
|
|
public AnalyzeTask()
|
{
|
effectKeys = new List<EffectKeyInfo>();
|
}
|
|
public void Add(EffectKeyInfo key)
|
{
|
effectKeys.Add(key);
|
}
|
}
|
|
}
|