using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Threading.Tasks; using UnityEditor; using Object = UnityEngine.Object; namespace AssetDependencyGraph { public interface IDependencyAnalysis { void Analyze(string path, Dictionary result); public (AssetIdentify id, AssetNode node) GetOrCreateFolderNode(string path, Dictionary result) { AssetIdentify k = null; foreach (var item in result.Keys) { if (item.Path == path) { k = item; } } if (k == null) { k = new AssetIdentify() { Path = path, AssetType = "Folder", Guid = null, Md5 = null }; result[k] = new FolderNode() { Self = k, AssetType = "Folder", }; } return (k, result[k]); } public (AssetIdentify id, AssetNode node) GetOrCreateAssetNode(string path, Dictionary result) { AssetIdentify k = null; foreach (var item in result.Keys) { if (item.Path == path) { k = item; } } if (k == null) { k = new AssetIdentify() { Path = path, Guid = null, Md5 = Utils.Md5(path) }; if (path.EndsWith(".prefab") || path.EndsWith(".unity")) { result[k] = new PackageNode() { Self = k, }; } else { result[k] = new AssetNode() { Self = k, }; } } return (k, result[k]); } public bool Exclude(string path) => path.EndsWith(".meta"); public string GetTypeByExtension(string ext) { switch (ext.ToLowerInvariant()) { case ".a": case ".dll": case ".so": case ".exe": case ".dynlib": return "Executable"; case ".asmdef": case ".asmref": return "UnityAssembly"; case ".cs": case ".lua": case ".js": case ".ts": case ".java": case ".h": case ".cpp": case ".cxx": case ".mm": return "SourceFile"; case ".json": case ".json5": return "JsonFile"; case ".xml": return "XmlFile"; case ".dat": return "DatFile"; default: return null; } } } public class UnityDependencyAnalysis : IDependencyAnalysis { HashSet processed = new(); public void Analyze(string path, Dictionary result) { if (!processed.Add(path)) { return; } var dependencyAnalysis = (this as IDependencyAnalysis); var kv = dependencyAnalysis.GetOrCreateAssetNode(path, result); var relatePath = path.ToUnityRelatePath(); kv.id.Guid = AssetDatabase.AssetPathToGUID(relatePath); var selfNode = kv.node; if (dependencyAnalysis.GetTypeByExtension(Path.GetExtension(path)) is string assetType && assetType != null) { kv.id.AssetType = assetType; selfNode.AssetType = assetType; } else { if (relatePath.EndsWith("Rock_01_Prefab.prefab")) { Console.WriteLine(); } Object mainObject = AssetDatabase.LoadMainAssetAtPath(relatePath); if (mainObject != null) { var prefabType = PrefabUtility.GetPrefabAssetType(mainObject); if (prefabType != PrefabAssetType.NotAPrefab) { selfNode.AssetType = prefabType.ToString(); } else { selfNode.AssetType = mainObject.GetType().Name; } kv.id.AssetType = selfNode.AssetType; string[] dependencies = AssetDatabase.GetDependencies(relatePath, false); for (int i = 0; i < dependencies.Length; i++) { var dep = dependencies[i].ToUnityFullPath(); var depkv = dependencyAnalysis.GetOrCreateAssetNode(dep, result); depkv.node.Dependent.Add(selfNode.Self); selfNode.Dependencies.Add(depkv.id); Analyze(dep, result); } } else { UnityEngine.Debug.LogWarning("unknown type:" + path); } } } } public class FolderDependencyAnalysis : IDependencyAnalysis { public void Analyze(string path, Dictionary result) { var dependencyAnalysis = (this as IDependencyAnalysis); var k = dependencyAnalysis.GetOrCreateFolderNode(path, result); Utils.TraverseDirectory(path, (p) => { p = p.ToUniversalPath(); if (dependencyAnalysis.Exclude(p)) { return; } var selfNode = result[k.id]; if (Directory.Exists(p)) { var kv = dependencyAnalysis.GetOrCreateFolderNode(p, result); kv.node.Dependent.Add(selfNode.Self); selfNode.Dependencies.Add(kv.id); } else if (File.Exists(p)) { var kv = dependencyAnalysis.GetOrCreateAssetNode(p, result); kv.node.Dependent.Add(selfNode.Self); selfNode.Dependencies.Add(kv.id); } }, 1); } } internal class DependencyAnalyzer { private Dictionary, IDependencyAnalysis> dependencyAnalysisDic = new(); private Dictionary assetIdentify2AssetNodeDic = new(); private bool Exclude(string path) => path.EndsWith(".meta"); public DependencyAnalyzer() { dependencyAnalysisDic.Add(new Predicate(path => !Directory.Exists(path)), new UnityDependencyAnalysis()); dependencyAnalysisDic.Add(new Predicate(path => Directory.Exists(path)), new FolderDependencyAnalysis()); } private void Visivt(string path) { path = path.ToUniversalPath(); if (Exclude(path)) { return; } foreach (var item in dependencyAnalysisDic) { if (item.Key(path)) { item.Value.Analyze(path, assetIdentify2AssetNodeDic); } } } public void Analyze(string rootFolder) { Stopwatch sw = Stopwatch.StartNew(); sw.Start(); Utils.TraverseDirectory(rootFolder, Visivt, -1); sw.Stop(); UnityEngine.Debug.Log($"分析引用耗时:{sw.ElapsedMilliseconds / 1000f}s"); AssetDependencyGraphDB db = new AssetDependencyGraphDB("admin", "CCS20190109", "10.225.0.170"); sw.Restart(); db.Clean(); Parallel.ForEach(assetIdentify2AssetNodeDic, item => { db.UpdateOrInsert(item.Value); }); sw.Stop(); UnityEngine.Debug.Log($"更新数据库:{sw.ElapsedMilliseconds / 1000f}s"); } } }