using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.Serialization.Formatters.Binary; using UnityEditor; using UnityEngine; public class ReferenceFinderData { //缓存路径 private const string CACHE_PATH = "Library/ReferenceFinderCache"; private const string CACHE_VERSION = "V1"; //资源引用信息字典 public Dictionary assetDict = new Dictionary(); //收集资源引用信息并更新缓存 public void CollectDependenciesInfo() { try { ReadFromCache(); var allAssets = AssetDatabase.GetAllAssetPaths(); int totalCount = allAssets.Length; for (int i = 0; i < allAssets.Length; i++) { //每遍历100个Asset,更新一下进度条,同时对进度条的取消操作进行处理 if ((i % 100 == 0) && EditorUtility.DisplayCancelableProgressBar("Refresh", string.Format("Collecting {0} assets", i), (float)i / totalCount)) { EditorUtility.ClearProgressBar(); return; } if (File.Exists(allAssets[i])) ImportAsset(allAssets[i]); if (i % 2000 == 0) GC.Collect(); } //将信息写入缓存 EditorUtility.DisplayCancelableProgressBar("Refresh", "Write to cache", 1f); WriteToChache(); //生成引用数据 EditorUtility.DisplayCancelableProgressBar("Refresh", "Generating asset reference info", 1f); UpdateReferenceInfo(); EditorUtility.ClearProgressBar(); } catch(Exception e) { Debug.LogError(e); EditorUtility.ClearProgressBar(); } } //通过依赖信息更新引用信息 private void UpdateReferenceInfo() { foreach(var asset in assetDict) { foreach(var assetGuid in asset.Value.dependencies) { assetDict[assetGuid].references.Add(asset.Key); } } } //生成并加入引用信息 private void ImportAsset(string path) { if (!path.StartsWith("Assets/")) return; //通过path获取guid进行储存 string guid = AssetDatabase.AssetPathToGUID(path); //获取该资源的最后修改时间,用于之后的修改判断 Hash128 assetDependencyHash = AssetDatabase.GetAssetDependencyHash(path); //如果assetDict没包含该guid或包含了修改时间不一样则需要更新 if (!assetDict.ContainsKey(guid) || assetDict[guid].assetDependencyHash != assetDependencyHash.ToString()) { //将每个资源的直接依赖资源转化为guid进行储存 var guids = AssetDatabase.GetDependencies(path, false). Select(p => AssetDatabase.AssetPathToGUID(p)). ToList(); //生成asset依赖信息,被引用需要在所有的asset依赖信息生成完后才能生成 AssetDescription ad = new AssetDescription(); ad.name = Path.GetFileNameWithoutExtension(path); ad.path = path; ad.assetDependencyHash = assetDependencyHash.ToString(); ad.dependencies = guids; if (assetDict.ContainsKey(guid)) assetDict[guid] = ad; else assetDict.Add(guid, ad); } } //读取缓存信息 public bool ReadFromCache() { assetDict.Clear(); if (!File.Exists(CACHE_PATH)) { return false; } var serializedGuid = new List(); var serializedDependencyHash = new List(); var serializedDenpendencies = new List(); //反序列化数据 FileStream fs = File.OpenRead(CACHE_PATH); try { BinaryFormatter bf = new BinaryFormatter(); string cacheVersion = (string) bf.Deserialize(fs); if (cacheVersion != CACHE_VERSION) { return false; } EditorUtility.DisplayCancelableProgressBar("Import Cache", "Reading Cache", 0); serializedGuid = (List) bf.Deserialize(fs); serializedDependencyHash = (List) bf.Deserialize(fs); serializedDenpendencies = (List) bf.Deserialize(fs); EditorUtility.ClearProgressBar(); } catch { //兼容旧版本序列化格式 return false; } finally { fs.Close(); } for (int i = 0; i < serializedGuid.Count; ++i) { string path = AssetDatabase.GUIDToAssetPath(serializedGuid[i]); if (!string.IsNullOrEmpty(path)) { var ad = new AssetDescription(); ad.name = Path.GetFileNameWithoutExtension(path); ad.path = path; ad.assetDependencyHash = serializedDependencyHash[i]; assetDict.Add(serializedGuid[i], ad); } } for(int i = 0; i < serializedGuid.Count; ++i) { string guid = serializedGuid[i]; if (assetDict.ContainsKey(guid)) { var guids = serializedDenpendencies[i]. Select(index => serializedGuid[index]). Where(g => assetDict.ContainsKey(g)). ToList(); assetDict[guid].dependencies = guids; } } UpdateReferenceInfo(); return true; } //写入缓存 private void WriteToChache() { if (File.Exists(CACHE_PATH)) File.Delete(CACHE_PATH); var serializedGuid = new List(); var serializedDependencyHash = new List(); var serializedDenpendencies = new List(); //辅助映射字典 var guidIndex = new Dictionary(); //序列化 using (FileStream fs = File.OpenWrite(CACHE_PATH)) { foreach (var pair in assetDict) { guidIndex.Add(pair.Key, guidIndex.Count); serializedGuid.Add(pair.Key); serializedDependencyHash.Add(pair.Value.assetDependencyHash); } foreach(var guid in serializedGuid) { //使用 Where 子句过滤目录 int[] indexes = assetDict[guid].dependencies. Where(s => guidIndex.ContainsKey(s)). Select(s => guidIndex[s]).ToArray(); serializedDenpendencies.Add(indexes); } BinaryFormatter bf = new BinaryFormatter(); bf.Serialize(fs, CACHE_VERSION); bf.Serialize(fs, serializedGuid); bf.Serialize(fs, serializedDependencyHash); bf.Serialize(fs, serializedDenpendencies); } } //更新引用信息状态 public void UpdateAssetState(string guid) { AssetDescription ad; if (assetDict.TryGetValue(guid,out ad) && ad.state != AssetState.NODATA) { if (File.Exists(ad.path)) { //修改时间与记录的不同为修改过的资源 if (ad.assetDependencyHash != AssetDatabase.GetAssetDependencyHash(ad.path).ToString()) { ad.state = AssetState.CHANGED; } else { //默认为普通资源 ad.state = AssetState.NORMAL; } } //不存在为丢失 else { ad.state = AssetState.MISSING; } } //字典中没有该数据 else if(!assetDict.TryGetValue(guid, out ad)) { string path = AssetDatabase.GUIDToAssetPath(guid); ad = new AssetDescription(); ad.name = Path.GetFileNameWithoutExtension(path); ad.path = path; ad.state = AssetState.NODATA; assetDict.Add(guid, ad); } } //根据引用信息状态获取状态描述 public static string GetInfoByState(AssetState state) { if(state == AssetState.CHANGED) { return "Changed"; } else if (state == AssetState.MISSING) { return "Missing"; } else if(state == AssetState.NODATA) { return "No Data"; } return "Normal"; } public class AssetDescription { public string name = ""; public string path = ""; public string assetDependencyHash; public List dependencies = new List(); public List references = new List(); public AssetState state = AssetState.NORMAL; } public enum AssetState { NORMAL, CHANGED, MISSING, NODATA, } }