1. 程式人生 > >Unity 資源管理外掛

Unity 資源管理外掛

之所以寫這個外掛呢,就是為了方便整理專案中的資原始檔,我記得之前好像也用了這麼一個外掛,但是也沒去找,還是自己動手寫一個吧,需要什麼功能就看自己的需求。

在專案的過程中呢,已經寫了一個外掛來管理材質,但是跟隨模型匯入的圖片卻越來越多,也是自己跟美術溝通不到位,導致根本沒辦法保證圖片的命名規則或者是否有重複的圖片,再加上不斷的刪除或者更新模型,專案中一些無用的圖片也堆積了不少。我嘗試了自己手動整理,但是沒有命名規則的圖片和一些靠縮圖甚至無法分辨是不是一樣的圖片,這樣的工作,就相當無力了。

於是呢,就完成了這麼一個外掛:

1.點選Organize Resources/Editor Window開啟外掛,點選未使用資源按鈕,這裡就會羅列出Assets資料夾裡所有未被引用的資源,當然當前未被引用肯定不代表他完全沒用,還是得靠自己確認然後手動刪除


2.點選全部資源,他就會輸出所有的檔案,並可以通過按鈕輸出他的GUID、應用和被引用的其他Assets路徑,這裡主要理清一些資源的關聯關係


3.點選重複資源按鈕,就可以分類顯示當前重複有哪些檔案,這裡的檔案重複判斷後面有具體介紹,點選合併按鈕,就可以清除掉多餘的資源,如果其他本地資源引用了這個檔案,合併的檔案也會自動替換上去,不用擔心其他檔案引用會丟失


這裡New Material材質引用了fish 2圖片,然後我在合併介面隨便點一個fish後面的合併按鈕


然後,多餘的圖片已經被清除了,而本來引用fish 2的材質New Material,也變成了引用fish圖片,這裡就是我當前需要的主要功能


上面的呢,是大概介紹了一些外掛的功能,接下來就展示一下主要的功能程式碼:

1.獲取當前Object引用了其他哪些Object,這裡在unity是有介面的,返回的就是引用了其他的Object的路徑:

string[] _OtherPaths = AssetDatabase.GetDependencies(_PathValue);
2.獲取當前Object被其他那些Object引用了,說著感覺有點繞,不過功能也就是根據上面的反正寫一個查詢功能,獲取其他Obejc的引用路徑,再與當前的的Object匹配,這裡的主要判斷依據靠的是Object的GUID
#region 獲取其他引用Assets的路徑
    string[] GetUseAssetPaths(string _AssetPath)
    {
        List<string> _AssetPaths = new List<string>();
        //使用GUID作為判斷標準
        string _AssetGUID = AssetDatabase.AssetPathToGUID(_AssetPath);
        //遍歷所有Assets
        for (int i = 0; i < _AllAssetsPaths.Count; i++)
        {
            if (_AllAssetsPaths[i] == _AssetPath)
                continue;

            string[] _OtherPaths = AssetDatabase.GetDependencies(_AllAssetsPaths[i]);
            if (_OtherPaths.Length > 1)
            {
                for (int j = 0; j < _OtherPaths.Length; j++)
                {
                    string _OtherGUID = AssetDatabase.AssetPathToGUID(_OtherPaths[j]);
                    if (_AssetGUID == _OtherGUID)
                    {
                        _AssetPaths.Add(_AllAssetsPaths[i]);
                    }
                }
            }
        }
        return _AssetPaths.ToArray();
    }
    #endregion
3.獲取相同的檔案,開始使用正常的檔案MD5值的驗證,發現了一個問題,就是對於外部資源(從外面匯入的資源,比如Texture、fbx)的驗證是正常的,但是對於unity內部資源(在unity內部建立的檔案,比如prefab、material)就不對了,後來讀取內部資源的Text發現,他在內部有Object的名稱,而Unity裡的檔案又不能重名,於是檔案就不一樣了,後來我就直接將他有名稱的那行資料先刪掉再來獲取MD5值,才成功判斷相同檔案

獲取相同檔案

string[] GetSameFilePaths(string _PathValue)
    {
        List<string> _AssetPaths = new List<string>();

        string _AssetMD5 = GetFileMD5(_PathValue);
        //遍歷所有Assets
        for (int i = 0; i < _AllAssetsPaths.Count; i++)
        {
            if (_AllAssetsPaths[i] == _PathValue)
                continue;
                if (_AssetMD5 == GetFileMD5(_AllAssetsPaths[i]))
                    _AssetPaths.Add(_AllAssetsPaths[i]);

        }
        return _AssetPaths.ToArray();
    }
獲取檔案的MD5值
#region 獲取檔案的MD5值
    string GetFileMD5(string _PathValue)
    {
        //判斷是否為本地資源   因為本地檔案裡有檔名稱 但是在資源名稱又不能重複  於是需要去掉名稱 來檢測md5值
        Object _ObejctValue = AssetDatabase.LoadAssetAtPath<Object>(_PathValue);
        bool _isNative =AssetDatabase.IsNativeAsset(_ObejctValue);
        string _FileMD5 = "";
        string _TemPath = Application.dataPath.Replace("Assets", "");

        if (_isNative)
        {
            string _TempFileText = File.ReadAllText(_TemPath + _PathValue).Replace("m_Name: " + _ObejctValue.name,"");
        
            System.Security.Cryptography.MD5 md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
            //將字串轉換為位元組陣列  
            byte[] fromData = System.Text.Encoding.Unicode.GetBytes(_TempFileText);
            //計算位元組陣列的雜湊值  
            byte[] toData = md5.ComputeHash(fromData);
            _FileMD5 = "";
            for (int i = 0; i < toData.Length; i++)
            {
                _FileMD5 += toData[i].ToString("x2");
            }
        }
        else
        {
            try
            {

                FileStream fs = new FileStream(_TemPath + _PathValue, FileMode.Open);

                System.Security.Cryptography.MD5 md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
                byte[] retVal = md5.ComputeHash(fs);
                fs.Close();
                _FileMD5 = "";
                for (int i = 0; i < retVal.Length; i++)
                {
                    _FileMD5 += retVal[i].ToString("x2");
                }
            }
            catch (System.Exception ex)
            {
                Debug.Log(ex);
            }
        }
        return _FileMD5;
    }
    #endregion
4.合併時,當前檔案替換掉其他被清除的檔案,這裡也是比較簡單的,其他Object也是通過檔案的GUID來引用檔案,這裡主要將他的GUID替換掉就可以
 #region 合併
    void OnRepeatMerge(string _PathValue,List<string> _ListValue)
    {
        string _FixedGUID= AssetDatabase.AssetPathToGUID(_PathValue);
        string _AssetsPath = Application.dataPath.Replace("Assets", "");

        for (int i = 0; i < _ListValue.Count; i++)
        {
            if (_PathValue == _ListValue[i])
                continue;
            string[] _OtherPaths = GetUseAssetPaths(_ListValue[i]);
            
            bool _isOtherNative = true;
            string _OldGUI = AssetDatabase.AssetPathToGUID(_ListValue[i]);
            for (int j = 0; j < _OtherPaths.Length; j++)
            {
                Object _OtherUseAsset = AssetDatabase.LoadAssetAtPath<Object>(_OtherPaths[j]);
                if (AssetDatabase.IsNativeAsset(_OtherUseAsset))
                {
                    string _RealAllText = File.ReadAllText(_AssetsPath+ _OtherPaths[j]).Replace(_OldGUI,_FixedGUID);
                    File.WriteAllText(_AssetsPath + _OtherPaths[j], _RealAllText);
                }
                else
                    _isOtherNative = false;
            }
            //如果沒有外部資源引用他 就刪除
            if (_isOtherNative)
            {
                AssetDatabase.DeleteAsset(_ListValue[i]);
                _ListValue.RemoveAt(i);
                i--;
            }
        }
        AssetDatabase.Refresh();
        OnRepeatClick();
    }
    #endregion

5.最後總結,在相對較大的專案中,因為會遍歷所有的檔案,外掛的反應速度就不那麼盡如人意,想想也不會時時來整理資源,所以也暫時忽略掉了,然後,外掛的功能也會在後面慢慢完善