unity编辑器拓展一——删除项目工程里的未使用Shader

时间:2021-11-27 04:45:46

1.描述

  我们在做项目的时候,美术会放很多shader在项目中,有一部分是没有用到的,想删又怕删错,会导致一些材质球丢shader,显示紫色。

今天就来想办法把多余的shader删干净。

2.分析

  在写代码之前,先分析下思路。首先我们要分析shader会被哪些文件引用,我找到了下面的两种情况。

1.material 这种情况是最常规的,所有用到的材质球都会依赖自己的shader

2.scene 这种情况不多,但是也会有,一般是屏幕特效之类的东西,在相机上挂了一个脚本,然后脚本里面挂上shader,并没有产生材质球。

研究了一下发现,这种shader是依赖在scene文件中。

3.游戏的时候动态运用的shader,这种情况只要在c#文件中搜索,如果包含shader名字,说明shader是用到的。

3.思路

  根据上面的分析,我们先整理出一个框架,然后再开始写代码,这是一个好的习惯,在写代码之前先用中文把思路表达清楚,写起来

就不会乱了,扯远了哈,继续说框架。

1.指定目录,一般只要在项目的某个路径里执行就可以了 

1.查找出项目中所有的材质球,特点是文件名包含 “.mat”,将其文件路径加入到数组A 

2.查找出项目中所有的scene,特点是文件名包含“.unity”,将其文件路径加入到数组B

3.查找出项目中所有的shader,特点是文件名包含“.shader”,将其文件路径加入到数组C

4.查找出c#文件使用的shader,将其加到数组C里面

5.通过shaderGuid将引用在mat和scene的shader找出来,将其加入数组D

6.通过数组C与D ,得出没有用到的shader路径,将其加入到数组E

7.删除E

 这样我们就可以开始写代码了

4.代码

  里面的代码全部是加上详细注释的,应该不难看懂

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;
using System.Text.RegularExpressions;





public class DelectShader : EditorWindow
{

    private static List<string> matPahtList = new List<string>(); //所有的material路径数组
    private static List<string> scenePathList = new List<string>();//所有的scene路径数组
    private static List<string> allshaderPahtList = new List<string>();//所有shader路径数组
    private static List<string> useShaderPathList = new List<string>();//所有用到的shader路径数组
    private static List<string> noUseShaderList = new List<string>();//没用到shader路径
    private static List<string> allCsPathList = new List<string>(); //所有c#数组

    [MenuItem("Game-X/删除多余shader")]


    //菜单调用这个函数
    static void GetNotQuoteShaderPath()
    {
        instantPathList(); //实例化三个数组:1 所有的shader 2 在材质球中用到的shader 3 在scene中用到的shader
        addMat();  // 将 在材质球中用到的shader + 到用到的shader数组里
        addScene();// 将 在scene中用到的shader + 到用到的shader数组里
        addCs();   // 将 在 c#中用到的shader + 到用到的shader数组里
        noUseShader();//通过 “所有的shader” 和 “所有用到的shader” 得出没有用到的shader
        deletes();//删除没有用到的shader
    }




    //获取所有的shader 和 mat 和 scene的路径
    static void instantPathList()
    {   //Application.dataPath:Assets路径  这里相当于“Assets/resourcex” 项目里用到的shader跟mat只在这个文件夹里面
        string path = Application.dataPath+ "/resourcex";
        //Directory.GetFiles():得到所有这个路径里包含.shader的文件。 得到文件夹用Directory.GetDirectories();
        allshaderPahtList = new List<string>(Directory.GetFiles(path, "*.shader", SearchOption.AllDirectories));
        matPahtList = new List<string>(Directory.GetFiles(path, "*.mat", SearchOption.AllDirectories));
        scenePathList = new List<string>(Directory.GetFiles(path, "*.unity", SearchOption.AllDirectories));
        //因为c#文件不一定在resourcex下面,所以把路径设为整个Assets
        string pathCs = Application.dataPath;
        allCsPathList = new List<string>(Directory.GetFiles(pathCs, "*.cs", SearchOption.AllDirectories));
    }




    /// <summary>
    /// 通过shader的路径获取Shader的guid 在下面的 addMat() 与 addScene()中取shaderGUID时使用
    /// </summary>
    /// <param name="shaderPath"></param>
    /// <returns></returns>
    //通过shader的路径获取Shader的guid 在下面的 addMat() 与 addScene()中取shaderGUID时使用
    static string FindShaderGuid(string shaderPath)
    {   //得到shader guid的方法
        return AssetDatabase.AssetPathToGUID(shaderPath);
    }



    /// 将材质用到的shader路径加到useShaderPathList数组里 

    //检查shaderGuid是否有引用在mat文件里 这个方法会在下面的addMat()方法中调用
    static bool CheckGuidInMat(string shaderGuid)
    { 
        for (int j = 0; j < matPahtList.Count; j++)
        {
            string matFilePath = matPahtList[j];
           //搜索路径文件里是否包含 shaderguid,如果包含 则返回true
            if (Regex.IsMatch(File.ReadAllText(matFilePath), shaderGuid))//在文件里查找是否有shaderGuid          
                return true;                                                                                                         
        }
        return false;
    }
    //将材质用到的shader路径加到useShaderPathList数组里
    static void addMat()
    {
        //遍历整个shader数组,将mat用到shader加到useShaderPathList数组里(要是里面没包含这个shader才需要加)
        for (int i = 0; i < allshaderPahtList.Count; i++)
        {   //得到shader的guid
            string shaderGuid = FindShaderGuid(allshaderPahtList[i].Substring(allshaderPahtList[i].IndexOf("Assets")));
            //如果shaderGuid不为空的时候
            if (!string.IsNullOrEmpty(shaderGuid))
            {
                //上面的方法CheckGuidInMat()返回为true
                if (CheckGuidInMat(shaderGuid))
                {
                    //如果useShaderPathList数组里没包含这个shader路径,将其加进数组
                    if (!useShaderPathList.Contains(allshaderPahtList[i]))
                        useShaderPathList.Add(allshaderPahtList[i]);
                    Debug.Log("材质球中用到的shader有:" + allshaderPahtList[i]);
                }
            }
        }
    }








    /// <summary>
    /// 将scene用到的shader路径加到useShaderPathList数组里
    /// </summary>
    /// <param name="shaderGuid"></param>
    /// <returns></returns>
    //检查shaderGuid是否有引用在scene文件里 这个方法会在下面的addScene()方法中调用
    static bool CheckGuidInScene (string shaderGuid)
    {
      
        for (int i = 0; i < scenePathList.Count; i++)
        {
            string SceneFilePath = scenePathList[i];
            if (Regex.IsMatch(File.ReadAllText(SceneFilePath), shaderGuid))
            return true;
        }
        return false;
    }

    //将scene用到的shader路径加到useShaderPathList数组里
    static void addScene()
    {
        for (int i = 0; i < allshaderPahtList.Count; i++)
        {
            string shaderGuid = FindShaderGuid(allshaderPahtList[i].Substring(allshaderPahtList[i].IndexOf("Assets")));
            if ((!string.IsNullOrEmpty(shaderGuid)))
            {
                if (CheckGuidInScene(shaderGuid))
                {
                    if (!useShaderPathList.Contains(allshaderPahtList[i]))
                    {
                        useShaderPathList.Add(allshaderPahtList[i]);
                        Debug.Log("场景中用到的shader有:" + allshaderPahtList[i]);
                    }

                }


            }
        }
    }






    /// <summary>
    /// 将所有cs用到的shader加入到数组
    /// </summary>
    /// <param name="Name"></param>
    /// <returns></returns>
    //检查c#是否被shader引用
    //因为c#跟shader没有guid依赖关系,说以要根据shader名来查找
    static bool CheckNameCs (string Name)
    {   //遍历c#数组,如果他包含指定的shader名,返回true
        for (int i = 0; i < allCsPathList.Count; i++)
        {
            string csPath = allCsPathList[i];
            
            if (Regex.IsMatch(File.ReadAllText(csPath), Name))
                return true;
        }
        return false;
    }

    //将所有cs用到的shader加入到数组
    static void addCs()
    {
        for (int i = 0; i < allshaderPahtList.Count; i++)
        {   //得到shader数组里的shader名(不包含扩展名 ,即“.”后面的名字)
            string Name = Path.GetFileNameWithoutExtension(allshaderPahtList[i]);
            //给上面的方法输入shader名,如果返回为true
            if (CheckNameCs(Name))
            {   //如果useShaderPathList没包含这个shader,将其加到useShaderPathList数组里
                if (!useShaderPathList.Contains(allshaderPahtList[i]))
                {
                    useShaderPathList.Add(allshaderPahtList[i]);
                    Debug.Log("c#中用到的材质球有:" + allshaderPahtList[i]);

                }
            }
        }
    }








   


    //通过所有的shader路径  很已经用到的shader路径  得出没用到的shader路径
    static void noUseShader()
    {
        //遍历allshaderPahtList路径  如果 useShaderPathList没包含 将加到noUseShaderList路径里
        for (int i = 0; i < allshaderPahtList.Count; i++)
        {
            string shaderPath = allshaderPahtList[i];
            if (!useShaderPathList.Contains(allshaderPahtList[i]))
            {
                if (!noUseShaderList.Contains(allshaderPahtList[i]))
                {
                    noUseShaderList.Add(allshaderPahtList[i]);

                }
                
            }
        }
    }


    // 删除没有用到的shader
    static void deletes()
    {
        for (int i = 0; i < noUseShaderList.Count; i++)
        {
            Debug.Log("删除的shader有:" + noUseShaderList[i]);
            File.Delete(noUseShaderList[i]);
        }
    }


}