Unity3D查找丢失材质和脚本工具

发表于2017-09-21
评论0 2.5k浏览

在游戏开发中,UI是经常需要变动的,一款游戏从开发到结束,UI至少更换好多版,这就给替换UI的人增加了很多负担,认为的因素很难将旧的UI彻底删除掉,这样就会出现很多冗余的资源,在项目后期要将这些冗余的资源清除掉,如果单靠人工操作难免会出现各种错误,其实我们完全可以通过工具将它们删除掉。下面把删除冗余的工具代码给大家展示如下:copy

  1. using UnityEngine;  
  2. using UnityEditor;  
  3.   
  4. using System.IO;  
  5. using System.Linq;  
  6. using System.Text.RegularExpressions;  
  7. using System.Collections.Generic;  
  8. using System.Text;  
  9. using System;  
  10.   
  11. public static class LinqHelper {  
  12.     public static TSource Fold<TSource>(this IEnumerable<TSource> source, Func<TSource, TSource, TSource> func, TSource id)  
  13.     {  
  14.         TSource r = id;  
  15.         foreach (var s in source)  
  16.         {  
  17.             r = func(r, s);  
  18.         }  
  19.         return r;  
  20.     }  
  21.     public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)  
  22.     {  
  23.         foreach (T element in source)  
  24.             action(element);  
  25.     }  
  26.     public static IEnumerable<U> SelectI<U, T>(this IEnumerable<T> source, Func<T, int, U> action)  
  27.     {  
  28.         int i = 0;  
  29.         foreach (var s in source)  
  30.         {  
  31.             yield return action(s, i);  
  32.             i  = 1;  
  33.         }  
  34.     }  
  35.     public static TSource Reduce<TSource>(this IEnumerable<TSource> source, Func<TSource, TSource, TSource> func) where TSource : new()  
  36.     {  
  37.         return Fold<TSource>(source, func, new TSource());  
  38.     }  
  39.     public static void ForEachI<T>(this IEnumerable<T> source, Action<T, int> action)  
  40.     {  
  41.         int i = 0;  
  42.         foreach (T element in source)  
  43.         {  
  44.             action(element, i);  
  45.             i  = 1;  
  46.         }  
  47.   
  48.     }  
  49. }  
  50. public static class FindUnUnUsedUITexture  
  51. {  
  52.   
  53.   
  54.     static List<string> getUUIDsInFile(string path)  
  55.     {  
  56.         StreamReader file = new StreamReader(path);  
  57.         List<string> uuids = new List<string>();  
  58.         string line;  
  59.         while ((line = file.ReadLine()) != null)  
  60.         {  
  61.             var reg = new Regex(@"([a-f0-9]{32})");  
  62.             var m = reg.Match(line);  
  63.             if (m.Success)  
  64.             {  
  65.                 uuids.Add(m.Groups[0].Value);  
  66.             }  
  67.         }  
  68.         file.Close();  
  69.         return uuids;  
  70.     }  
  71.     // Use this for initialization  
  72.     [MenuItem("Tools/UI冗余图片扫描")]  
  73.     public static void Scan()  
  74.     {  
  75.   
  76.         var uiPrefabRootDir = EditorUtility.OpenFolderPanel("选择UIPrefab目录",  "Assets","");  
  77.         if (string.IsNullOrEmpty(uiPrefabRootDir))  
  78.         {  
  79.             return;  
  80.         }  
  81.   
  82.         var uiPicRootDir = EditorUtility.OpenFolderPanel("选择UIPrefab目录""Assets""");  
  83.         if (string.IsNullOrEmpty(uiPicRootDir))  
  84.         {  
  85.             return;  
  86.         }  
  87.           
  88.         //find all meta and pic path  
  89.         var uuidReg = new Regex(@"guid: ([a-f0-9]{32})");  
  90.         var pngs = Directory.GetFiles(uiPicRootDir, "*.meta", SearchOption.AllDirectories)  
  91.         .Select(p => "Assets/"   p.Replace('\\','/').Substring(Application.dataPath.Length 1))  
  92.         .Where(p =>  
  93.         {  
  94.             return p.EndsWith(".png.meta") || p.EndsWith(".jpg.meta") || p.EndsWith(".tag.meta");  
  95.         }).ToList();  
  96.         var uuid2path = new Dictionary<stringstring>();  
  97.         pngs.ForEachI((png, i) =>  
  98.         {  
  99.             var matcher = uuidReg.Match(File.ReadAllText(png));  
  100.             var uuid = matcher.Groups[1].Value;  
  101.             if (uuid2path.ContainsKey(uuid))  
  102.             {  
  103.                 Debug.LogError("uuid dup"   uuid   " \n"   png   "\n"   uuid2path[uuid]);  
  104.             }  
  105.             else  
  106.             {  
  107.                 uuid2path.Add(uuid, png.Substring(0,png.Length-5));  
  108.             }  
  109.             EditorUtility.DisplayProgressBar("扫描图片中", png, (float)i / pngs.Count);  
  110.   
  111.         });  
  112.   
  113.         //find all prefab and search pic uuid  
  114.         var prefabs = Directory.GetFiles(uiPrefabRootDir, "*.prefab", SearchOption.AllDirectories);  
  115.         var anims = Directory.GetFiles("Assets/""*.anim", SearchOption.AllDirectories).Where(p => !p.Replace('\\', '/').Contains("Characters/"));  
  116.         var allFiles = prefabs.Concat(anims).ToList();  
  117.         var alluuids = allFiles  
  118.         .SelectI((f, i) => {  
  119.             EditorUtility.DisplayProgressBar("获取引用关系", f, (float)i / allFiles.Count);  
  120.             return getUUIDsInFile(f);  
  121.         }).ToList().Aggregate((a, b) => a.Concat(b).ToList()).ToList();  
  122.         EditorUtility.ClearProgressBar();  
  123.         //rm used pic uuid  
  124.         var uuidshashset = new HashSet<string>(alluuids);  
  125.         var em = uuidshashset.GetEnumerator();  
  126.         while(em.MoveNext())  
  127.         {  
  128.             var uuid = em.Current;  
  129.             uuid2path.Remove(uuid);  
  130.         }  
  131.   
  132.         StringBuilder sb = new StringBuilder();  
  133.         sb.Append("UnUsedFiles: ");  
  134.         sb.Append(uuid2path.Count);  
  135.         sb.Append("\n");  
  136.         uuid2path.ForEach(kv => sb.Append(kv.Value  "\n"));  
  137.   
  138.         File.WriteAllText("Assets/unusedpic.txt", sb.ToString());  
  139.         EditorUtility.DisplayDialog("扫描成功"string.Format("共找到{0}个冗余图片\n请在Assets/unsedpic.txt查看结果",uuid2path.Count), "ok");  
  140.     }  
  141. }  
将该脚本放置到Editor文件夹下面,然后在Tool菜单下点击UI冗余图片扫描就会弹出窗口将冗余的UI图片列出来,非常方便。

另外,在游戏中经常出现脚本丢失情况,自己查找非常麻烦,这个也可以通过工具去查找,代码如下所示:

  1. using System.Linq;  
  2. using UnityEditor;  
  3. using UnityEngine;  
  4. using System.Collections.Generic;  
  5. using System.Text.RegularExpressions;  
  6. using System.IO;  
  7. using System;  
  8.   
  9. public static class MissingScriptFinder   
  10. {  
  11.     private const string MENU_ROOT = "Tool/Missing References/";  
  12.     public static string GetHierarchyName(Transform t)  
  13.     {  
  14.         if (t == null)  
  15.             return "";  
  16.   
  17.         var pname =  GetHierarchyName(t.parent);  
  18.         if (pname != "")  
  19.         {  
  20.             return pname   "/"   t.gameObject.name;  
  21.         }  
  22.         return t.gameObject.name;  
  23.     }  
  24.   
  25.     public static TSource Fold<TSource>(this IEnumerable<TSource> source, Func<TSource, TSource, TSource> func,TSource id)  
  26.     {  
  27.         TSource r = id;  
  28.         foreach(var s in source)  
  29.         {  
  30.             r = func(r,s);  
  31.         }  
  32.         return r;  
  33.     }  
  34.     public static void ForEachI<T>(this IEnumerable<T> source, Action<T,int> action)  
  35.     {  
  36.         int i = 0;  
  37.         foreach (T element in source)  
  38.         {  
  39.             action(element,i);  
  40.             i  = 1;  
  41.         }  
  42.               
  43.     }  
  44.     public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)  
  45.     {  
  46.         foreach (T element in source)  
  47.         {  
  48.             action(element);  
  49.         }  
  50.               
  51.     }  
  52.   
  53.     static HashSet<string> findAllScriptUUIDsInAssets()  
  54.     {  
  55.         var uuids = Directory.GetFiles("Assets/""*.cs.meta", SearchOption.AllDirectories)  
  56.             .Select(p =>  
  57.             {  
  58.                 return File.ReadAllLines(p)[1].Substring(6);  
  59.             }).ToList();  
  60.         //find dll uuids  
  61.         var dlluuids = Directory.GetFiles(EditorApplication.applicationContentsPath, "*.dll", SearchOption.AllDirectories)  
  62.         .Select(p =>  
  63.         {  
  64.             return AssetDatabase.AssetPathToGUID(p.Replace('\\', '/'));  
  65.         }).Where(s => s!= "").ToList();  
  66.         return new HashSet<string>(uuids.Concat(dlluuids));  
  67.     }  
  68.     static Regex s_scriptUUIDReg = new Regex(@"m_Script: \{fileID: [0-9] , guid: ([0-9a-f]{32}), type: 3\}");  
  69.     static string getScriptUUID(string line)  
  70.     {  
  71.         var m = s_scriptUUIDReg.Match(line);  
  72.         if (m.Success)  
  73.         {  
  74.             return m.Groups[1].Value;  
  75.         }  
  76.         if(line.Contains("m_Script: {fileID: 0}")) //missing script  
  77.         {  
  78.             return "0";  
  79.         }  
  80.         return null;  
  81.     }  
  82.     static Dictionary<string,HashSet<string>> findAllPrefabScriptRefInDir(string dir,Action<int> onBeginFinding,Action<int,string,int> onFinding, Action onEndFinding )  
  83.     {  
  84.         var allPrefabs = Directory.GetFiles(dir, "*.prefab", SearchOption.AllDirectories);  
  85.         onBeginFinding(allPrefabs.Length);  
  86.         Dictionary<string, HashSet<string>> r = new Dictionary<string, HashSet<string>>();  
  87.           
  88.         for (int i =0;i<allPrefabs.Length; i)  
  89.         {  
  90.             onFinding(i, allPrefabs[i],allPrefabs.Length);  
  91.             File.ReadAllLines(allPrefabs[i]).ForEach(line =>  
  92.             {  
  93.                 string s = getScriptUUID(line);  
  94.                 if (s != null)  
  95.                 {  
  96.                     HashSet<string> files = null;  
  97.                     r.TryGetValue(s, out files);  
  98.                     if (files == null)  
  99.                     {  
  100.                         files = new HashSet<string>();  
  101.                         r.Add(s, files);  
  102.                     }  
  103.                     files.Add(allPrefabs[i]);  
  104.                 }  
  105.             });  
  106.         }  
  107.         onEndFinding();  
  108.         return r;  
  109.     }  
  110.   
  111.   
  112.     private static void FindMissionRefInGo(GameObject go)  
  113.     {  
  114.         var components = go.GetComponents<MonoBehaviour>();  
  115.         foreach (var c in components)  
  116.         {  
  117.             // Missing components will be null, we can't find their type, etc.  
  118.             if (!c)  
  119.             {  
  120.                 var assetPath =  AssetDatabase.GetAssetPath(go);  
  121.                 if(assetPath != "" && assetPath != null)  
  122.                 {  
  123.                     Debug.LogError("missing script: "   GetHierarchyName(go.transform)   "-->"   assetPath);  
  124.                 }  
  125.                 else  
  126.                 {  
  127.                     Debug.LogError("missing script: "   GetHierarchyName(go.transform));  
  128.                 }  
  129.                 continue;  
  130.             }  
  131.         }  
  132.         foreach(Transform t in go.transform)  
  133.         {  
  134.             FindMissionRefInGo(t.gameObject);  
  135.         }  
  136.     }  
  137.     public static IEnumerable<GameObject> SceneRoots()  
  138.     {  
  139.         var prop = new HierarchyProperty(HierarchyType.GameObjects);  
  140.         var expanded = new int[0];  
  141.         while (prop.Next(expanded))  
  142.         {  
  143.             yield return prop.pptrValue as GameObject;  
  144.         }  
  145.     }  
  146.     [MenuItem(MENU_ROOT   "search in scene")]  
  147.     public static void FindMissingReferencesInCurrentScene()  
  148.     {  
  149.         var objs = SceneRoots();  
  150.         int count = objs.Count();  
  151.         objs.ForEachI((prefab, i) =>  
  152.         {  
  153.             EditorUtility.DisplayProgressBar("check missing prefabs", prefab.ToString(), (float)i / count);  
  154.             FindMissionRefInGo(prefab);  
  155.         });  
  156.         EditorUtility.ClearProgressBar();  
  157.     }  
  158.   
  159.     [MenuItem(MENU_ROOT   "search in all assets")]  
  160.     public static void MissingSpritesInAssets()  
  161.     {  
  162.         var allScriptsIds = findAllScriptUUIDsInAssets();  
  163.         var refScriptIds = findAllPrefabScriptRefInDir("Assets/",  
  164.         (count) =>  
  165.         {  
  166.             EditorUtility.DisplayProgressBar("scanning","",0);  
  167.         },  
  168.         (idx,file,count) =>  
  169.         {  
  170.             EditorUtility.DisplayProgressBar("scanning", file, (float) idx/count);  
  171.         },  
  172.         () =>  
  173.         {  
  174.             EditorUtility.ClearProgressBar();  
  175.         });  
  176.         var missingScriptsFiles = refScriptIds  
  177.         .Where(kv => !allScriptsIds.Contains(kv.Key))  
  178.         .Select(kv => kv.Value)  
  179.         .ToList()  
  180.         .Fold((a,b)=>new HashSet<string>(a.Concat(b)),new HashSet<string>());  
  181.         Debug.LogError("----------------------------------------->\nMissingFiles: "    missingScriptsFiles.Count);  
  182.         missingScriptsFiles.ForEachI((f, i) =>  
  183.         {  
  184.             EditorUtility.DisplayProgressBar("check missing prefabs", f, (float)i / missingScriptsFiles.Count);  
  185.             var prefab = AssetDatabase.LoadAssetAtPath(f, typeof(GameObject)) as GameObject;  
  186.             FindMissionRefInGo(prefab);  
  187.         });  
  188.         EditorUtility.ClearProgressBar();  
  189.     }  
  190.   
  191. }  
同样需要将该脚本放置到Editor文件夹下面,点击菜单栏中的Tool/Missing Reference 即可实现丢失脚本的查找,它可以分成多个部分查找,自己可以去测试一下。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引