Unity 材质无用的引用清理

整理资源的时候 发现材质属性面板上只有四张贴图 但是材质球确有五张贴图的依赖 想着写个工具处理下 没想到网上一搜已经有小哥写好了工具 直接拿来用了

原文链接

代码上做了简洁

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

/// <summary>
/// 如果材质使用了带有两张贴图的Shader 并且对贴图赋了值 
/// 再切换到带有一张贴图的Shader 此时就会有一张冗余贴图在材质中
/// 浪费内存 浪费加载带宽
/// </summary>
static class MaterialCleanerTools
{
    [UnityEditor.MenuItem("Assets/Tools/清理材质无用贴图")]
    public static void ClearMaterial()
    {
        UnityEngine.Object[] objects = Selection.GetFiltered(typeof(object), SelectionMode.Assets);
        if (objects.Length == 0)
        {
            Debug.LogWarning("请选择资源目录后再进行操作");
            return;
        }

        foreach (var obj in objects)
        {
            float index = 0;
            string assetPath = AssetDatabase.GetAssetPath(obj);
            string projectPath = Application.dataPath.Replace("Assets", "");
            string fullPath = EditorPathUtils.ToUnixPath(projectPath + assetPath);
            List<string> filePaths = EditorUtils.CollectFilesByEnd(fullPath, ".mat");
            foreach (string filePath in filePaths)
            {
                string fileAssetPath = filePath.Replace(Application.dataPath, "Assets");
                ClearMaterialAsset(fileAssetPath);
                index++;
                EditorUtility.DisplayProgressBar("清理材质无用的缓存属性", fileAssetPath, index / (float)filePaths.Count);
            }
        }

        EditorUtility.ClearProgressBar();
    }
    public static void ClearMaterialAsset(string path)
    {
        if (String.IsNullOrEmpty(path))
        {
            return;
        }

        Material m = AssetDatabase.LoadAssetAtPath<Material>(path);
        if (m == null)
            return;

        var deps = AssetDatabase.GetDependencies(new String[] { path });
        var deps_textures = deps.Where(s => IsTextureAsset(s)).ToList();
        var used_textures = new HashSet<String>();
        var shader = m.shader;
        var newMat = new Material(shader);
        var c = ShaderUtil.GetPropertyCount(shader);

        for (int i = 0; i < c; ++i)
        {
            var type = ShaderUtil.GetPropertyType(shader, i);
            var name = ShaderUtil.GetPropertyName(shader, i);
            var value = m.GetProperty(i);
            switch (type)
            {
                case ShaderUtil.ShaderPropertyType.Color:
                    {
                        newMat.SetColor(name, m.GetColor(name));
                    }
                    break;
                case ShaderUtil.ShaderPropertyType.Float:
                    {
                        newMat.SetFloat(name, m.GetFloat(name));
                    }
                    break;
                case ShaderUtil.ShaderPropertyType.Range:
                    {
                        newMat.SetFloat(name, (float)value);
                    }
                    break;
                case ShaderUtil.ShaderPropertyType.TexEnv:
                    {
                        newMat.SetTexture(name, (Texture)value);
                        newMat.SetTextureOffset(name, m.GetTextureOffset(name));
                        newMat.SetTextureScale(name, m.GetTextureScale(name));
                        var tpath = AssetDatabase.GetAssetPath((Texture)value);
                        if (!String.IsNullOrEmpty(tpath))
                        {
                            used_textures.Add(tpath);
                        }
                    }
                    break;
                case ShaderUtil.ShaderPropertyType.Vector:
                    {
                        newMat.SetVector(name, (Vector4)value);
                    }
                    break;
            }
        }
        bool rebuild = false;
        if (used_textures.Count != deps_textures.Count)
        {
            for (int i = 0; i < deps_textures.Count; ++i)
            {
                var _fn = deps_textures[i];
                if (!used_textures.Contains(_fn))
                {
                    rebuild = true;
                    UnityEngine.Debug.LogWarning(String.Format("unused texture: {0}", _fn));
                }
            }
        }
        if (!rebuild)
        {
            if (newMat != null)
            {
                UnityEngine.Object.DestroyImmediate(newMat);
            }
            return;
        }

        string basePath = Path.GetFullPath(path + "/../").Replace(Path.GetFullPath(Application.dataPath), "Assets");
        string fn = Path.GetFileNameWithoutExtension(path);
        string ext = Path.GetExtension(path);

        //SplitFullFilename(path, out fn, out ext, out basePath);
        var tempAssetPath = String.Format("{0}{1}_temp.{2}", basePath, fn, ext);
        var _test = AssetDatabase.LoadAllAssetsAtPath(tempAssetPath);

        if (_test != null)
        {
            AssetDatabase.DeleteAsset(tempAssetPath);
        }

        // create a new material to replace it latter
        AssetDatabase.CreateAsset(newMat, tempAssetPath);
        Resources.UnloadAsset(newMat);

        var tempAssetDataPath = String.Format("{0}{1}_datatemp.bytes", basePath, fn, ext);
        if (File.Exists(tempAssetPath))
        {
            // rename it to .bytes
            File.Copy(tempAssetPath, tempAssetDataPath, true);
            // delete temp material
            AssetDatabase.DeleteAsset(tempAssetPath);
            if (File.Exists(tempAssetDataPath))
            {
                // delete original material
                File.Delete(path);
                // replace original material with .bytes file
                File.Copy(tempAssetDataPath, path, true);
                // remove bytes file
                File.Delete(tempAssetDataPath);
                AssetDatabase.Refresh();
            }
        }
        
        return;
    }

    static object GetProperty(this Material material, int index)
    {
        var name = ShaderUtil.GetPropertyName(material.shader, index);
        var type = ShaderUtil.GetPropertyType(material.shader, index);
        switch (type)
        {
            case ShaderUtil.ShaderPropertyType.Color:
                return material.GetColor(name);
            case ShaderUtil.ShaderPropertyType.Vector:
                return material.GetVector(name);
            case ShaderUtil.ShaderPropertyType.Range:
            case ShaderUtil.ShaderPropertyType.Float:
                return material.GetFloat(name);
            case ShaderUtil.ShaderPropertyType.TexEnv:
                return material.GetTexture(name);
        }
        return null;
    }

    static bool IsTextureAsset(String assetPath)
    {
        var ext = Path.GetExtension(assetPath).ToLower();
        return ext == ".png" ||
            ext == ".tga" ||
            ext == ".jpg" ||
            ext == ".bmp" ||
            ext == ".psd" ||
            ext == ".dds" ||
            ext == ".exr";
    }
}
上一篇:Unity Color与十六进制颜色互相转换


下一篇:入职day2day3