diff options
author | chai <chaifix@163.com> | 2018-07-08 12:41:58 +0800 |
---|---|---|
committer | chai <chaifix@163.com> | 2018-07-08 12:41:58 +0800 |
commit | 7bf4eb116498086274306fa0d3ebbe906f9833e6 (patch) | |
tree | 54344f56a55c3b1059ae14ec1af4184c0f03d03f |
*第一次提交
-rw-r--r-- | AssetBrowser.cs | 703 |
1 files changed, 703 insertions, 0 deletions
diff --git a/AssetBrowser.cs b/AssetBrowser.cs new file mode 100644 index 0000000..4be9386 --- /dev/null +++ b/AssetBrowser.cs @@ -0,0 +1,703 @@ +using UnityEditor; +using UnityEngine; +using System; +using System.IO; +using System.Collections; +using System.Collections.Generic; + +class AssetData +{ + // 枚举值为遍历的优先级,用于AssetData.Dependcies和AssetDataManager.Assets + public static readonly int AssetTypeCount = 9; + public enum AssetType + { + Scene = 0, + Prefab, + Mesh, + Material, + Asset, + Texture, + Shader, + Script, + CSV, + + Unknown = 255, + } + + public AssetData(FileSystemInfo f) + { + for (int i = 0; i < AssetTypeCount; ++i) + Dependencies[i] = new List<string>(); + AssetDataManager manager = AssetDataManager.Get(); + AssetPath = manager.FullPathToAssetPath(f.FullName); + GUID = AssetDatabase.AssetPathToGUID(AssetPath); + Name = f.Name; + ObjType = AssetDatabase.GetMainAssetTypeAtPath(AssetPath); + FileType = ExtensionToAssetType(f.Extension); + Extension = f.Extension; + string[] depsPath = AssetDatabase.GetDependencies(AssetPath, false); + for(int i = 0; i < depsPath.Length; ++i) + { + string path = depsPath[i]; + string guid = AssetDatabase.AssetPathToGUID(path); + if (guid == GUID) + continue; + string extension = manager.GetExtension(path); + AssetType type = ExtensionToAssetType(extension); + if (type == AssetType.Unknown) + continue; + Dependencies[(int)type].Add(guid); + ++RefCount; + List<string> data; + if (!AssetDataManager.AssetsReverse.TryGetValue(guid, out data)) + { + data = new List<string>(); + AssetDataManager.AssetsReverse.Add(guid, data); + } + data.Add(GUID); + } + FileInfo finfo = new FileInfo(f.FullName); + DiskSize = finfo.Length; + DiskSizeStr = EditorUtility.FormatBytes(DiskSize); + } + + #region 资源属性 + public string GUID; + public string Name; + public int RefCount = 0; + public int RevRefCount = 0; + public long DiskSize; + public string DiskSizeStr; + public string AssetPath; + public Type ObjType; + public string Extension; + public AssetType FileType; + public List<string>[] Dependencies = new List<string>[AssetTypeCount]; + #endregion + + private AssetType ExtensionToAssetType(string extension) + { + if (extension == ".unity") return AssetType.Scene; + else if (extension == ".prefab") return AssetType.Prefab; + else if (extension == ".mat") return AssetType.Material; + else if (extension == ".asset") return AssetType.Asset; + else if (extension == ".fbx" || extension == ".FBX") return AssetType.Mesh; + else if (extension == ".png" || extension == ".jpg" || extension == ".bmp") return AssetType.Texture; + else if (extension == ".shader") return AssetType.Shader; + else if (extension == ".cs") return AssetType.Script; + else if (extension == ".csv") return AssetType.CSV; + else return AssetType.Unknown; + } +} + +class AssetDataManager +{ + private static AssetDataManager manager; + + public static AssetDataManager Get() + { + if (manager == null) + manager = new AssetDataManager(); + return manager; + } + + public AssetDataManager() + { + for (int i = 0; i < AssetData.AssetTypeCount; ++i) + Assets[i] = new Dictionary<string, AssetData>(); + } + + #region Asset字典 + // 所有资源 + public static Dictionary<string, AssetData> AllAssets = new Dictionary<string, AssetData>(); + // 分类统计 + public static Dictionary<string, AssetData>[] Assets = new Dictionary<string, AssetData>[AssetData.AssetTypeCount]; + // 反向依赖 + public static Dictionary<string, List<string>> AssetsReverse = new Dictionary<string, List<string>>(); + #endregion + + #region 过滤 + public static List<string> ValidExtension = new List<string> + { + ".unity", // scene + ".prefab", // prefab + ".mat", // material + ".asset", // asset + ".fbx", ".FBX", // mesh + ".png", ".jpg", ".bmp", // texture + ".shader", // shader + ".cs", // script + ".csv" // CSV + }; + + public string GetExtension(string file) + { + if (file == null || file.Length == 0) + return null; + int len = file.Length; + int idx = file.LastIndexOf('.'); + return file.Substring(idx, len - idx); + } + + public bool IsValidFile(string file) + { + string extension = GetExtension(file); + foreach (var ext in ValidExtension) + { + if (ext == extension) + return true; + } + return false; + } + + public List<string> InvalidFolder = new List<string> + { + }; + + public bool IsValidFolder(string folderName) + { + foreach(var dir in InvalidFolder) + { + if(dir == folderName) + return false; + } + return true; + } + #endregion + + public string FullPathToAssetPath(string fullpath) + { + fullpath = fullpath.Replace('\\', '/'); + string path = "Assets" + fullpath.Replace(Application.dataPath, ""); + path = path.Replace('/', '\\'); + return path; + } + + public string AssetPathToFullPath(string assetpath) + { + string fullpath = Application.dataPath.Replace("/Assets", "") + "/" + assetpath; + return fullpath; + } + + public void LoadAssets(string fullpath) + { + DirectoryInfo directoryInfo = new DirectoryInfo(fullpath); + FileSystemInfo[] filesInfo = directoryInfo.GetFileSystemInfos(); + foreach(FileSystemInfo f in filesInfo) + { + if(f is DirectoryInfo && IsValidFolder(f.Name)) + { + LoadAssets(f.FullName); + } + else if(f.Extension != ".meta" && IsValidFile(f.Name)) + { + AssetData asset = new AssetData(f); + AllAssets.Add(asset.GUID, asset); + Assets[(int)asset.FileType].Add(asset.GUID, asset); + } + } + } + + public void ClearAssets() + { + AllAssets.Clear(); + foreach (var assets in Assets) + assets.Clear(); + AssetsReverse.Clear(); + } + +} + +class Drawer +{ + private Drawer() + { + SelectorButtonStyle = new GUIStyle(GUIStyle.none); + SelectorButtonStyle.normal.background = CreateTexture(1, 1, new Color32(62, 95, 150, 255)); + } + + private Texture2D CreateTexture(int w, int h, Color col) + { + Color[] pixels = new Color[w * h]; + for(int i = 0; i < w * h; ++i) + pixels[i] = col; + Texture2D texture = new Texture2D(w, h); + texture.SetPixels(pixels); + return texture; + } + + private static Drawer drawer = null; + + public static Drawer Get() + { + if (drawer == null) + drawer = new Drawer(); + return drawer; + } + + public void DrawButtonMid(Rect rect, string content) + { + GUI.Button(rect, content, EditorStyles.miniButtonMid); + } + + public void DrawButton(Rect rect, string content, out bool click) + { + click = GUI.Button(rect, content); + } + + public void DrawLabel(Rect rect, string content) + { + GUI.Label(rect, content); + } + + public void DrawTextfield(Rect rect, string content, ref string value) + { + value = GUI.TextField(rect, content); + } + + public void DrawCheckBox(Rect rect, string content, bool check, ref bool ischeck) + { + ischeck = GUI.Toggle(rect, check, content); + } + + public void DrawBackground(Rect rect, Color col) + { + Color old = GUI.backgroundColor; + GUI.backgroundColor = col; + GUI.Box(rect, ""); + GUI.backgroundColor = old; + } + + public void DrawFoldout(Rect rect, ref bool foldout) + { + foldout = EditorGUI.Foldout(rect, foldout, ""); + } + + public void DrawAssetIcon(Rect rect, AssetData asset) + { + GUIContent content = EditorGUIUtility.ObjectContent(null, asset.ObjType); + content.text = asset.Name; + GUI.Label(rect, content); + } + + public void DrawSelector(Rect rect, bool selected, out bool clicked) + { + SelectorButtonStyle.normal.background = CreateTexture(1, 1, new Color32(62, 95, 150, 255)); + clicked = GUI.Button(rect, "",selected ? SelectorButtonStyle : GUIStyle.none); + } + + #region 配置项 + public Color ColorHeavy = new Color(0.2f, 0.2f, 0.2f); + public Color ColorLight = new Color(0.3f, 0.3f, 0.3f); + public GUIStyle SelectorButtonStyle; + #endregion +} + +// 文件树 +class FileTree +{ + public class Node + { + public Node(string guid) + { + GUID = guid; + Foldout = false; + } + public bool Foldout; // 是否展开 + public string GUID; // GUID + public List<Node> Children = new List<Node>(); // 子节点 + } + + public List<Node> root = new List<Node>(); +} + +class AssetBrowser : EditorWindow +{ + private static AssetBrowser editor; + private static Drawer drawer; + + private FileTree assetsTree = new FileTree(); + + #region GUI 组件配置数值 + private Rect window = new Rect(); + private int contentWidth = 1600; + private int contentHeight = 1000; + private readonly int kLineHeight = 20; // 行高 + private Vector2 scrollPos = new Vector2(0, 0); + private int directoryOffsetY = 5; // 资源目录Y偏移度 + private int filtersOffsetY = 27; // 过滤器Y偏移度 + private int headerOffsetY = 50; // 标题Y偏移度 + private int assetsTreeOffsetY = 70; // 列表偏移度 + private int kTabWidth = 15; + #endregion + + #region 数据 + private string directory = ""; + private FileTree.Node selectedNode = null; + #endregion + + #region 过滤器 + private string filterFile = ""; + private bool filterScene = false; + private bool filterPrefab = false; + private bool filterMaterial = false; + private bool filterAsset = false; + private bool filterMesh = false; + private bool filterTexture = false; + private bool filterShader = false; + private bool filterScript = false; + private bool filterCSV = false; + #endregion + + [MenuItem("Window/Asset Browser")] + public static void Show() + { + editor = GetWindow<AssetBrowser>(); + editor.titleContent = new GUIContent("Asset Browser"); + drawer = Drawer.Get(); + } + + private enum Colum + { + Name, // 资源名 + Ref, // 引用数 + RevRef, // 被引用数 + Type, // 资源类型 + DiskSize, // 硬盘大小 + Path, // 资源路径 + } + + private Dictionary<Colum, string> ColumHeader = new Dictionary<Colum, string> + { + {Colum.Name, "资源引用关系"}, + {Colum.Ref, "引用数"}, + {Colum.RevRef, "被引用数"}, + {Colum.Type, "资源类型"}, + {Colum.DiskSize, "硬盘大小"}, + {Colum.Path, "资源路径"}, + }; + + private Dictionary<Colum, int> ColumWidth = new Dictionary<Colum, int> + { + {Colum.Name, 500}, + {Colum.Ref, 100}, + {Colum.RevRef, 100}, + {Colum.Type, 100}, + {Colum.DiskSize, 100}, + {Colum.Path, 500}, + }; + + private Dictionary<Colum, int> ColumOffsetX = new Dictionary<Colum, int>(); + + private void AdjustColumOffsetX() + { + if (ColumOffsetX == null) + return; + ColumOffsetX.Clear(); + ColumOffsetX.Add(Colum.Name, 0); + ColumOffsetX.Add(Colum.Ref, ColumOffsetX[Colum.Name] + ColumWidth[Colum.Name]); + ColumOffsetX.Add(Colum.RevRef, ColumOffsetX[Colum.Ref] + ColumWidth[Colum.Ref]); + ColumOffsetX.Add(Colum.Type, ColumOffsetX[Colum.RevRef] + ColumWidth[Colum.RevRef]); + ColumOffsetX.Add(Colum.DiskSize, ColumOffsetX[Colum.Type] + ColumWidth[Colum.Type]); + ColumOffsetX.Add(Colum.Path, ColumOffsetX[Colum.DiskSize] + ColumWidth[Colum.DiskSize]); + } + + private void AdjustWindow() + { + window = editor.position; + } + + public void OnGUI() + { + // 调试 + if (editor == null) + editor = GetWindow<AssetBrowser>(); + drawer = Drawer.Get(); + + AdjustColumOffsetX(); + AdjustWindow(); + + BeginScrollView(); + // 绘制工具栏 + OnToolset(); + // 绘制资源列表 + OnAsset(); + EndScrollView(); + } + + private void BeginScrollView() + { + scrollPos = GUI.BeginScrollView(new Rect(0, 0, window.width, window.height), scrollPos, new Rect(0, 0, contentWidth, contentHeight), true, true); + } + + private void EndScrollView() + { + GUI.EndScrollView(); + } + + private void OnToolset() + { + OnDirectory(); + OnFilters(); + } + + private void OnDirectory() + { + float offsetY = directoryOffsetY + scrollPos.y; + drawer.DrawLabel(new Rect(0, offsetY, 95, kLineHeight), "资源目录 Assets/"); + drawer.DrawTextfield(new Rect(95, offsetY, 250, kLineHeight), directory, ref directory); + bool load; + drawer.DrawButton(new Rect(350, offsetY, 50, kLineHeight), "载入", out load); + if(load) + { + ProcessAssets(); + ProcessTree(); + EditorUtility.DisplayDialog("载入完毕", "载入完毕", "确定"); + } + } + + private void OnFilters() + { + // 文件匹配 + drawer.DrawLabel(new Rect(0, filtersOffsetY + scrollPos.y, 100, kLineHeight), "匹配文件"); + drawer.DrawTextfield(new Rect(50, filtersOffsetY + scrollPos.y, 150, kLineHeight), filterFile, ref filterFile); + // 类型过滤 + float offsetX = 250; + float offsetY = filtersOffsetY + scrollPos.y; + int width = 70; + drawer.DrawCheckBox(new Rect(offsetX, offsetY, width, kLineHeight), "场景", filterScene, ref filterScene); + drawer.DrawCheckBox(new Rect(offsetX + width, offsetY, width, kLineHeight), "Prefab", filterPrefab, ref filterPrefab); + drawer.DrawCheckBox(new Rect(offsetX + width * 2, offsetY, width, kLineHeight), "材质", filterMaterial, ref filterMaterial); + drawer.DrawCheckBox(new Rect(offsetX + width * 3, offsetY, width, kLineHeight), "Asset", filterAsset, ref filterAsset); + drawer.DrawCheckBox(new Rect(offsetX + width * 4, offsetY, width, kLineHeight), "Mesh", filterMesh, ref filterMesh); + drawer.DrawCheckBox(new Rect(offsetX + width * 5, offsetY, width, kLineHeight), "贴图", filterTexture, ref filterTexture); + drawer.DrawCheckBox(new Rect(offsetX + width * 6, offsetY, width, kLineHeight), "Shader", filterShader, ref filterShader); + drawer.DrawCheckBox(new Rect(offsetX + width * 7, offsetY, width, kLineHeight), "脚本", filterScript, ref filterScript); + drawer.DrawCheckBox(new Rect(offsetX + width * 8, offsetY, width, kLineHeight), "CSV", filterCSV, ref filterCSV); + } + + private void OnAsset() + { + OnHeader(); + BeginFileTreeView(); + OnAssetsTree(); + EndFileTreeView(); + } + + private void BeginFileTreeView() + { + scrollPos = GUI.BeginScrollView( + new Rect(scrollPos.x, scrollPos.y + assetsTreeOffsetY, window.width, window.height - assetsTreeOffsetY), + scrollPos, + new Rect(0, 0, contentWidth, contentHeight - assetsTreeOffsetY), + false , false + ); + } + + private void EndFileTreeView() + { + GUI.EndScrollView(); + } + + private void OnHeader() + { + float scrollY = scrollPos.y; + drawer.DrawButtonMid(new Rect(ColumOffsetX[Colum.Name], headerOffsetY + scrollY, ColumWidth[Colum.Name], kLineHeight), ColumHeader[Colum.Name]); + drawer.DrawButtonMid(new Rect(ColumOffsetX[Colum.Ref], headerOffsetY + scrollY, ColumWidth[Colum.Ref], kLineHeight), ColumHeader[Colum.Ref]); + drawer.DrawButtonMid(new Rect(ColumOffsetX[Colum.RevRef], headerOffsetY + scrollY, ColumWidth[Colum.RevRef], kLineHeight), ColumHeader[Colum.RevRef]); + drawer.DrawButtonMid(new Rect(ColumOffsetX[Colum.Type], headerOffsetY + scrollY, ColumWidth[Colum.Type], kLineHeight), ColumHeader[Colum.Type]); + drawer.DrawButtonMid(new Rect(ColumOffsetX[Colum.DiskSize], headerOffsetY + scrollY, ColumWidth[Colum.DiskSize], kLineHeight), ColumHeader[Colum.DiskSize]); + drawer.DrawButtonMid(new Rect(ColumOffsetX[Colum.Path], headerOffsetY + scrollY, ColumWidth[Colum.Path], kLineHeight), ColumHeader[Colum.Path]); + } + + private void OnAssetsTree() + { + int count = 0; + foreach(FileTree.Node node in assetsTree.root) + { + DrawNode(node, 0, ref count); + } + } + + private bool Filter(FileTree.Node node, int hierachy) + { + if(hierachy == 0) + { + AssetData asset; + if (!AssetDataManager.AllAssets.TryGetValue(node.GUID, out asset)) + return false; + // 文件类型过滤 + if (asset.FileType == AssetData.AssetType.Scene && !filterScene) return false; + else if (asset.FileType == AssetData.AssetType.Prefab&& !filterPrefab) return false; + else if (asset.FileType == AssetData.AssetType.Material&& !filterMaterial) return false; + else if (asset.FileType == AssetData.AssetType.Asset&& !filterAsset) return false; + else if (asset.FileType == AssetData.AssetType.Mesh&& !filterMesh) return false; + else if (asset.FileType == AssetData.AssetType.CSV&& !filterCSV) return false; + else if (asset.FileType == AssetData.AssetType.Script&& !filterScript) return false; + else if (asset.FileType == AssetData.AssetType.Shader&& !filterShader) return false; + else if (asset.FileType == AssetData.AssetType.Texture&& !filterTexture) return false; + // 文件名 + if (asset.Name.IndexOf(filterFile) == -1) + return false; + } + + return true; + } + + // 按照深度绘制 + private void DrawNode(FileTree.Node node, int hierachy, ref int count) + { + if (!Filter(node, hierachy)) + return; + ++count; + contentHeight = count * kLineHeight + assetsTreeOffsetY; + DrawAssetInfo(node, hierachy, count - 1); + if (!node.Foldout || node.Children.Count == 0) + return; + foreach (FileTree.Node child in node.Children) + { + DrawNode(child, hierachy + 1, ref count); + } + } + + // 绘制单个 + private void DrawAssetInfo(FileTree.Node node, int hierachy, int index) + { + Drawer drawer = Drawer.Get(); + AssetData asset; + if (!AssetDataManager.AllAssets.TryGetValue(node.GUID, out asset)) + return; + int offsetY = index * kLineHeight; + // 绘制背景 + drawer.DrawBackground(new Rect(0, offsetY, contentWidth, kLineHeight), (index & 1) == 1 ? drawer.ColorHeavy : drawer.ColorLight); + // 绘制折叠按钮 + if (node.Children.Count > 0) + drawer.DrawFoldout(new Rect(hierachy * kTabWidth, offsetY, 10, kLineHeight), ref node.Foldout); + // 绘制选择按钮 + bool clicked; + drawer.DrawSelector(new Rect(hierachy * kTabWidth + 12, offsetY, contentWidth, kLineHeight), selectedNode == node, out clicked); + if (clicked) + { + if(selectedNode != node) + selectedNode = node; + else + { + AssetReverse.Show(node.GUID); + selectedNode = null; + } + } + // 绘制图标 + drawer.DrawAssetIcon(new Rect(hierachy * kTabWidth + 12, offsetY, ColumWidth[Colum.Name], kLineHeight), asset); + // 绘制引用数 + drawer.DrawLabel(new Rect(ColumOffsetX[Colum.Ref], offsetY, 100, kLineHeight), asset.RefCount.ToString()); + // 绘制反向引用数 + List<string> revRef; + int revRefCount = 0; + if (AssetDataManager.AssetsReverse.TryGetValue(node.GUID, out revRef)) + revRefCount = revRef.Count; + drawer.DrawLabel(new Rect(ColumOffsetX[Colum.RevRef], offsetY, 100, kLineHeight), revRefCount.ToString()); + // 绘制资源类型 + drawer.DrawLabel(new Rect(ColumOffsetX[Colum.Type], offsetY, 100, kLineHeight), asset.Extension); + // 绘制硬盘大小 + drawer.DrawLabel(new Rect(ColumOffsetX[Colum.DiskSize], offsetY, 100, kLineHeight), asset.DiskSizeStr); + // 绘制资源路径 + drawer.DrawLabel(new Rect(ColumOffsetX[Colum.Path], offsetY, 1000, kLineHeight), asset.AssetPath); + } + + private void ProcessAssets() + { + AssetDataManager assetManager = AssetDataManager.Get(); + assetManager.ClearAssets(); + assetManager.LoadAssets(assetManager.AssetPathToFullPath("Assets/" + directory)); + } + + private void ProcessTree() + { + AssetDataManager assetManager = AssetDataManager.Get(); + assetsTree.root.Clear(); + for(int i = 0; i < AssetData.AssetTypeCount; ++i) + { + Dictionary<string, AssetData> assets = AssetDataManager.Assets[i]; + foreach(var asset in assets) + { + AssetData data = asset.Value; + FileTree.Node node = ProcessNode(data); + assetsTree.root.Add(node); + } + } + } + + private FileTree.Node ProcessNode(AssetData data) + { + AssetDataManager assetManager = AssetDataManager.Get(); + FileTree.Node node = new FileTree.Node(data.GUID); + foreach(List<string> deps in data.Dependencies) + { + foreach(string guid in deps) + { + AssetData dep; + if(AssetDataManager.AllAssets.TryGetValue(guid, out dep)) + { + FileTree.Node child = ProcessNode(dep); + node.Children.Add(child); + } + } + } + return node; + } + +} + +class AssetReverse : EditorWindow +{ + private static AssetReverse editor; + private static Drawer drawer; + + private static string GUID; + private static AssetData asset; + private Vector2 scrollPos = new Vector2(0, 0); + + private int kLineHeight = 20; + private int contentHeight = 0; + + public static void Show(string guid) + { + editor = GetWindow<AssetReverse>(); + editor.titleContent = new GUIContent("Asset Reverse"); + GUID = guid; + if(AssetDataManager.AllAssets.TryGetValue(GUID, out asset)) + { + } + Drawer dawer = Drawer.Get(); + } + + public void OnGUI() + { + if (asset == null) + return; + drawer = Drawer.Get(); + drawer.DrawAssetIcon(new Rect(0, 0, 200, kLineHeight), asset); + scrollPos = GUI.BeginScrollView(new Rect(0, kLineHeight, editor.position.width, editor.position.height - kLineHeight), scrollPos, new Rect(0, 0, 1500, contentHeight)); + // 标题 + drawer.DrawButtonMid(new Rect(0, 0, 400, 20), "资源名"); + drawer.DrawButtonMid(new Rect(400, 0, 400, 20), "资源路径"); + // + List<string> revRef; + if(AssetDataManager.AssetsReverse.TryGetValue(GUID, out revRef)) + { + int count = 0; + foreach(string guid in revRef) + { + AssetData data; + if(AssetDataManager.AllAssets.TryGetValue(guid, out data)) + { + drawer.DrawAssetIcon(new Rect(0, kLineHeight + count * kLineHeight, 400, kLineHeight), data); + drawer.DrawLabel(new Rect(400, kLineHeight + count * kLineHeight, 400, kLineHeight), data.AssetPath); + ++count; + contentHeight = count * kLineHeight + kLineHeight; + } + } + } + GUI.EndScrollView(); + } +} |