笔者介绍:姜雪伟,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 即可实现丢失脚本的查找,它可以分成多个部分查找,自己可以去测试一下。

Unity3D查找丢失材质和脚本工具的更多相关文章

  1. 4.3.6 对象的界定通过编写接口来访问带这类命名结构的表会出问题。如前所述,SQL Server的灵活性不应用作编写错误代码或创建问题对象的借口。 注意在使用Management Studio的脚本工具时,SQL Server会界定所有的对象。这不是因为这么做是必须的,也不是编写代码的最佳方式,而是因为在界定符中封装所有的对象,比编写脚本引擎来查找需要界定的对象更容易。

    如前所述,在创建对象时,最好避免使用内嵌的空格或保留字作为对象名,但设计人员可能并没有遵守这个最佳实践原则.例如,我当前使用的数据库中有一个审核表名为Transaction,但是Transaction ...

  2. [Unity3D] 5.0 图集合并扩展工具,用于解决UGUI与AssetBundle打包造成资源包过大的问题

    [Unity3D] 5.0 图集合并扩展工具,用于解决UGUI与AssetBundle打包造成资源包过大的问题 2017年07月05日 15:57:44 阅读数:1494 http://www.cpp ...

  3. java性能问题排查提效脚本工具

    在性能测试过程中,往往会出现各种各样的性能瓶颈.其中java常见瓶颈故障模型有cpu资源瓶颈:文件IO瓶颈:网络IO瓶颈:内存资源瓶颈:资源消耗不高程序本身执行慢等场景模型. 如何快速定位分析这些类型 ...

  4. unity3d 特殊文件夹和脚本编译顺序

    unity3d 特殊文件夹和脚本编译顺序 转自http://blog.csdn.net/u010019717/article/details/40474631 大多数情况下,您可以选择任何你喜欢的文件 ...

  5. ArcGIS使用Python脚本工具

    在Pyhton写的一些代码,用户交互不方便,用户体验比较差,不方便重用.在ArcGIS中可以将用写的Python代码导入到ToolBox中,这样用起来就比较方便了.这里用按要素裁剪栅格的Python来 ...

  6. ArcMap自定义脚本工具制作

    原文 ArcMap自定义脚本工具制作 在制图的前期,一般需要做一些数据的整理,如图层合并.裁剪等工作.虽然在ArcMap中也有提供对应的工具,但使用起来需要点技巧.如批量裁剪,虽然可以实现,但出来的结 ...

  7. 自动脚本工具新版 v2.0

    自动脚本工具 下载 下载工具后,解压,直接双击 "execute.bat" 文件后(前提已配置好 jdk 1.7 的环境),会生成文件夹 "output",该文 ...

  8. axf、elf文件转换成bin、hex脚本工具

    在嵌入式开发过程中常常遇到将axf或elf文件转换成bin的情况,大家都知道通过gnu toolchain中的objcopy和keil中的fromelf能做到.可是为了这么一个小事而记住复杂的选项以及 ...

  9. REDGATE又一好用的脚本工具ScriptsManager1.3

    原文:REDGATE又一好用的脚本工具ScriptsManager1.3 REDGATE又一好用的脚本工具ScriptsManager1.3 先说明一下:这个工具是免费的 下载地址:http://ww ...

随机推荐

  1. was控制台英文改成中文

    在安装was的时候没有选中简体中文包,所以导致安装后的was控制台显示中文,但是没关系,我们还是通过界面配置来修改,使得控制台从英文变为中文 1.vnc远程到服务器2./opt/IBM/Install ...

  2. BZOJ3236: [AHOI2013]作业

    BZOJ3236: [AHOI2013]作业 题目描述 传送门 行,我知道是Please contact lydsy2012@163.com! 传送门2 题目分析 这题两问还是非常,emmmm. 首先 ...

  3. 关于推荐库位 java前端与SQL语句后面的结合

    ----------------------------------------------------------------------------------- select a1.id,a1. ...

  4. Linux安装ipvsadm

    一.介绍 ipvs称之为IP虚拟服务器(IP Virtual Server,简写为IPVS).是运行在LVS下的提供负载平衡功能的一种技术 二.安装 1.下载 http://www.linuxvirt ...

  5. 实现Promise的first等各种变体

    本篇文章主要是想通过ES6中Promise提供的几个方法,来实现诸如first.last.none.any等各种变体方法! 在标准的ES6规范中,提供了Promise.all和Promise.race ...

  6. python脚本6_打印菱形

    #输入菱形最宽的行所在的行数,打印菱形 m = int(input(">>>")) for n in range(m): print(" "* ...

  7. 设计模式--状态模式C++实现

    1定义 当一个状态的内在状态改变时允许其行为改变,这个对象看起来像改变了其类 2类图 角色分析 State抽象状态角色,接口或者抽象类,负责状态定义,并且封装环境角色以实现状态切换 ConcreteS ...

  8. jmeter-02 JMeter 生成HTML性能报告

    Report Dashboard: JMeter3.0 后提供的扩展模块,支持从测试计划中获取图形和统计数据,生成HTML页面格式图形化报告. 快速入门演示 一.准备测试计划 mock_api .jm ...

  9. Truncate a string

    用瑞兹来截断对面的退路! 截断一个字符串! 如果字符串的长度比指定的参数num长,则把多余的部分用...来表示. 切记,插入到字符串尾部的三个点号也会计入字符串的长度. 但是,如果指定的参数num小于 ...

  10. 转:Hive SQL的编译过程

    Hive是基于Hadoop的一个数据仓库系统,在各大公司都有广泛的应用.美团数据仓库也是基于Hive搭建,每天执行近万次的Hive ETL计算流程,负责每天数百GB的数据存储和分析.Hive的稳定性和 ...