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

来源:互联网 发布:淘宝买精密管警察找我 编辑:程序博客网 时间:2024/05/18 14:46

笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,已出版书籍:《手把手教你架构3D游戏引擎》电子工业出版社和《Unity3D实战核心技术详解》电子工业出版社等。

CSDN视频网址:http://edu.csdn.net/lecturer/144

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

using UnityEngine;using UnityEditor;using System.IO;using System.Linq;using System.Text.RegularExpressions;using System.Collections.Generic;using System.Text;using System;public static class LinqHelper {    public static TSource Fold<TSource>(this IEnumerable<TSource> source, Func<TSource, TSource, TSource> func, TSource id)    {        TSource r = id;        foreach (var s in source)        {            r = func(r, s);        }        return r;    }    public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)    {        foreach (T element in source)            action(element);    }    public static IEnumerable<U> SelectI<U, T>(this IEnumerable<T> source, Func<T, int, U> action)    {        int i = 0;        foreach (var s in source)        {            yield return action(s, i);            i += 1;        }    }    public static TSource Reduce<TSource>(this IEnumerable<TSource> source, Func<TSource, TSource, TSource> func) where TSource : new()    {        return Fold<TSource>(source, func, new TSource());    }    public static void ForEachI<T>(this IEnumerable<T> source, Action<T, int> action)    {        int i = 0;        foreach (T element in source)        {            action(element, i);            i += 1;        }    }}public static class FindUnUnUsedUITexture{    static List<string> getUUIDsInFile(string path)    {        StreamReader file = new StreamReader(path);        List<string> uuids = new List<string>();        string line;        while ((line = file.ReadLine()) != null)        {            var reg = new Regex(@"([a-f0-9]{32})");            var m = reg.Match(line);            if (m.Success)            {                uuids.Add(m.Groups[0].Value);            }        }        file.Close();        return uuids;    }    // Use this for initialization    [MenuItem("Tools/UI冗余图片扫描")]    public static void Scan()    {        var uiPrefabRootDir = EditorUtility.OpenFolderPanel("选择UIPrefab目录",  "Assets","");        if (string.IsNullOrEmpty(uiPrefabRootDir))        {            return;        }        var uiPicRootDir = EditorUtility.OpenFolderPanel("选择UIPrefab目录", "Assets", "");        if (string.IsNullOrEmpty(uiPicRootDir))        {            return;        }                //find all meta and pic path        var uuidReg = new Regex(@"guid: ([a-f0-9]{32})");        var pngs = Directory.GetFiles(uiPicRootDir, "*.meta", SearchOption.AllDirectories)        .Select(p => "Assets/" + p.Replace('\\','/').Substring(Application.dataPath.Length+1))        .Where(p =>        {            return p.EndsWith(".png.meta") || p.EndsWith(".jpg.meta") || p.EndsWith(".tag.meta");        }).ToList();        var uuid2path = new Dictionary<string, string>();        pngs.ForEachI((png, i) =>        {            var matcher = uuidReg.Match(File.ReadAllText(png));            var uuid = matcher.Groups[1].Value;            if (uuid2path.ContainsKey(uuid))            {                Debug.LogError("uuid dup" + uuid + " \n" + png + "\n" + uuid2path[uuid]);            }            else            {                uuid2path.Add(uuid, png.Substring(0,png.Length-5));            }            EditorUtility.DisplayProgressBar("扫描图片中", png, (float)i / pngs.Count);        });        //find all prefab and search pic uuid        var prefabs = Directory.GetFiles(uiPrefabRootDir, "*.prefab", SearchOption.AllDirectories);        var anims = Directory.GetFiles("Assets/", "*.anim", SearchOption.AllDirectories).Where(p => !p.Replace('\\', '/').Contains("Characters/"));        var allFiles = prefabs.Concat(anims).ToList();        var alluuids = allFiles        .SelectI((f, i) => {            EditorUtility.DisplayProgressBar("获取引用关系", f, (float)i / allFiles.Count);            return getUUIDsInFile(f);        }).ToList().Aggregate((a, b) => a.Concat(b).ToList()).ToList();        EditorUtility.ClearProgressBar();        //rm used pic uuid        var uuidshashset = new HashSet<string>(alluuids);        var em = uuidshashset.GetEnumerator();        while(em.MoveNext())        {            var uuid = em.Current;            uuid2path.Remove(uuid);        }        StringBuilder sb = new StringBuilder();        sb.Append("UnUsedFiles: ");        sb.Append(uuid2path.Count);        sb.Append("\n");        uuid2path.ForEach(kv => sb.Append(kv.Value +"\n"));        File.WriteAllText("Assets/unusedpic.txt", sb.ToString());        EditorUtility.DisplayDialog("扫描成功", string.Format("共找到{0}个冗余图片\n请在Assets/unsedpic.txt查看结果",uuid2path.Count), "ok");    }}
将该脚本放置到Editor文件夹下面,然后在Tool菜单下点击UI冗余图片扫描就会弹出窗口将冗余的UI图片列出来,非常方便。

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

using System.Linq;using UnityEditor;using UnityEngine;using System.Collections.Generic;using System.Text.RegularExpressions;using System.IO;using System;public static class MissingScriptFinder {    private const string MENU_ROOT = "Tool/Missing References/";    public static string GetHierarchyName(Transform t)    {        if (t == null)            return "";        var pname =  GetHierarchyName(t.parent);        if (pname != "")        {            return pname + "/" + t.gameObject.name;        }        return t.gameObject.name;    }    public static TSource Fold<TSource>(this IEnumerable<TSource> source, Func<TSource, TSource, TSource> func,TSource id)    {        TSource r = id;        foreach(var s in source)        {            r = func(r,s);        }        return r;    }    public static void ForEachI<T>(this IEnumerable<T> source, Action<T,int> action)    {        int i = 0;        foreach (T element in source)        {            action(element,i);            i += 1;        }                }    public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)    {        foreach (T element in source)        {            action(element);        }                }    static HashSet<string> findAllScriptUUIDsInAssets()    {        var uuids = Directory.GetFiles("Assets/", "*.cs.meta", SearchOption.AllDirectories)            .Select(p =>            {                return File.ReadAllLines(p)[1].Substring(6);            }).ToList();        //find dll uuids        var dlluuids = Directory.GetFiles(EditorApplication.applicationContentsPath, "*.dll", SearchOption.AllDirectories)        .Select(p =>        {            return AssetDatabase.AssetPathToGUID(p.Replace('\\', '/'));        }).Where(s => s!= "").ToList();        return new HashSet<string>(uuids.Concat(dlluuids));    }    static Regex s_scriptUUIDReg = new Regex(@"m_Script: \{fileID: [0-9]+, guid: ([0-9a-f]{32}), type: 3\}");    static string getScriptUUID(string line)    {        var m = s_scriptUUIDReg.Match(line);        if (m.Success)        {            return m.Groups[1].Value;        }        if(line.Contains("m_Script: {fileID: 0}")) //missing script        {            return "0";        }        return null;    }    static Dictionary<string,HashSet<string>> findAllPrefabScriptRefInDir(string dir,Action<int> onBeginFinding,Action<int,string,int> onFinding, Action onEndFinding )    {        var allPrefabs = Directory.GetFiles(dir, "*.prefab", SearchOption.AllDirectories);        onBeginFinding(allPrefabs.Length);        Dictionary<string, HashSet<string>> r = new Dictionary<string, HashSet<string>>();                for (int i =0;i<allPrefabs.Length;++i)        {            onFinding(i, allPrefabs[i],allPrefabs.Length);            File.ReadAllLines(allPrefabs[i]).ForEach(line =>            {                string s = getScriptUUID(line);                if (s != null)                {                    HashSet<string> files = null;                    r.TryGetValue(s, out files);                    if (files == null)                    {                        files = new HashSet<string>();                        r.Add(s, files);                    }                    files.Add(allPrefabs[i]);                }            });        }        onEndFinding();        return r;    }    private static void FindMissionRefInGo(GameObject go)    {        var components = go.GetComponents<MonoBehaviour>();        foreach (var c in components)        {            // Missing components will be null, we can't find their type, etc.            if (!c)            {                var assetPath =  AssetDatabase.GetAssetPath(go);                if(assetPath != "" && assetPath != null)                {                    Debug.LogError("missing script: " + GetHierarchyName(go.transform) + "-->" + assetPath);                }                else                {                    Debug.LogError("missing script: " + GetHierarchyName(go.transform));                }                continue;            }        }        foreach(Transform t in go.transform)        {            FindMissionRefInGo(t.gameObject);        }    }    public static IEnumerable<GameObject> SceneRoots()    {        var prop = new HierarchyProperty(HierarchyType.GameObjects);        var expanded = new int[0];        while (prop.Next(expanded))        {            yield return prop.pptrValue as GameObject;        }    }    [MenuItem(MENU_ROOT + "search in scene")]    public static void FindMissingReferencesInCurrentScene()    {        var objs = SceneRoots();        int count = objs.Count();        objs.ForEachI((prefab, i) =>        {            EditorUtility.DisplayProgressBar("check missing prefabs", prefab.ToString(), (float)i / count);            FindMissionRefInGo(prefab);        });        EditorUtility.ClearProgressBar();    }    [MenuItem(MENU_ROOT + "search in all assets")]    public static void MissingSpritesInAssets()    {        var allScriptsIds = findAllScriptUUIDsInAssets();        var refScriptIds = findAllPrefabScriptRefInDir("Assets/",        (count) =>        {            EditorUtility.DisplayProgressBar("scanning","",0);        },        (idx,file,count) =>        {            EditorUtility.DisplayProgressBar("scanning", file, (float) idx/count);        },        () =>        {            EditorUtility.ClearProgressBar();        });        var missingScriptsFiles = refScriptIds        .Where(kv => !allScriptsIds.Contains(kv.Key))        .Select(kv => kv.Value)        .ToList()        .Fold((a,b)=>new HashSet<string>(a.Concat(b)),new HashSet<string>());        Debug.LogError("----------------------------------------->\nMissingFiles: "  + missingScriptsFiles.Count);        missingScriptsFiles.ForEachI((f, i) =>        {            EditorUtility.DisplayProgressBar("check missing prefabs", f, (float)i / missingScriptsFiles.Count);            var prefab = AssetDatabase.LoadAssetAtPath(f, typeof(GameObject)) as GameObject;            FindMissionRefInGo(prefab);        });        EditorUtility.ClearProgressBar();    }}
同样需要将该脚本放置到Editor文件夹下面,点击菜单栏中的Tool/Missing Reference 即可实现丢失脚本的查找,它可以分成多个部分查找,自己可以去测试一下。