using CaoCao.XAsset; using System.Collections.Generic; using System.IO; using System.Text; using UnityEditor; using UnityEditor.IMGUI.Controls; using UnityEngine; namespace CaoCao.Editor { public class ManifestsWindow : EditorWindow { private const int k_SearchHeight = 20; [SerializeField] private MultiColumnHeaderState multiColumnHeaderState; [SerializeField] private TreeViewState treeViewState; public bool reloadSelectedNow; [MenuItem("Window/CaoCao/XAsset/Manifests")] public static void OpenManifests() { EditorWindow.GetWindow(false, "Manifests"); } public ulong sizeInBytes; private readonly GUIContent typeModule = new GUIContent("模块"); private readonly List types = new List(); private readonly Dictionary> typeWithAssets = new Dictionary>(); private readonly GUIContent versionName = new GUIContent("模块"); private Manifest _manifest; private Versions _versions; private bool reloadAssets; private bool reloadNow = true; private SearchField searchField; private int selected; private AssetTreeView treeView; private int selectedType { get; set; } private void OnEnable() { reloadNow = true; } private void OnGUI() { if (reloadNow) { Reload(); reloadNow = false; reloadSelectedNow = true; } if (_versions.data.Count == 0) { EditorGUILayout.HelpBox("请先打包", MessageType.Info); return; } if (reloadSelectedNow) { selectedType = 0; sizeInBytes = 0; var version = _versions.data[selected]; typeWithAssets.Clear(); types.Clear(); var all = new List(); typeWithAssets.Add("All", all); types.Add("All"); _manifest = CaoCao.XAsset.Utility.LoadFromFile(Settings.GetDataPath(version.file)); foreach (var bundle in _manifest.bundles) sizeInBytes += bundle.size; foreach (var asset in _manifest.assets) { if (!File.Exists(asset.path)) { CaoCao.XAsset.Logger.W($"File not found: {asset}"); continue; } var type = AssetDatabase.GetMainAssetTypeAtPath(asset.path); if (!typeWithAssets.TryGetValue(type.Name, out var assets)) { assets = new List(); typeWithAssets.Add(type.Name, assets); types.Add(type.Name); } assets.Add(asset.path); all.Add(asset.path); } reloadSelectedNow = false; reloadAssets = true; } if (reloadAssets) { var set = new HashSet(); foreach (var asset in GetSelectedAssets()) { var bundle = _manifest.GetBundle(asset); set.Add(bundle); } selectedTypeSize = 0; foreach (var bundle in set) { selectedTypeSize += bundle.size; } if (treeView != null) { treeView.SetAssets(this); reloadAssets = false; } } if (_versions == null || _versions.data.Count == 0) { GUILayout.Label("没有加载到当前平台的打包数据,请在打包后再打开此界面"); return; } if (treeView == null) { if (treeViewState == null) treeViewState = new TreeViewState(); var headerState = AssetTreeView.CreateDefaultMultiColumnHeaderState(); // multiColumnTreeViewRect.width); if (MultiColumnHeaderState.CanOverwriteSerializedFields(multiColumnHeaderState, headerState)) MultiColumnHeaderState.OverwriteSerializedFields(multiColumnHeaderState, headerState); multiColumnHeaderState = headerState; treeView = new AssetTreeView(treeViewState, multiColumnHeaderState); treeView.Reload(); } if (searchField == null) { searchField = new SearchField(); searchField.downOrUpArrowKeyPressed += treeView.SetFocusAndEnsureSelectedItem; } var rect = new Rect(0, 0, position.width, position.height); DrawTree(rect); DrawToolbar(new Rect(rect.xMin, rect.yMin, rect.width, k_SearchHeight)); } private void Reload() { _versions = Settings.GetDefaultVersions(); } private void DrawManifest() { versionName.text = $"{_versions.data[selected].name}({CaoCao.XAsset.Utility.FormatBytes(sizeInBytes)})"; var rect = GUILayoutUtility.GetRect(versionName, EditorStyles.toolbarDropDown); if (EditorGUI.DropdownButton(rect, versionName, FocusType.Keyboard, EditorStyles.toolbarDropDown)) { var menu = new GenericMenu(); for (var index = 0; index < _versions.data.Count; index++) { var version = _versions.data[index]; menu.AddItem(new GUIContent(version.name), selected == index, data => { selected = (int) data; reloadSelectedNow = true; }, index); } menu.DropDown(rect); } } private ulong selectedTypeSize; private void DrawTypes() { typeModule.text = $"{types[selectedType]}({CaoCao.XAsset.Utility.FormatBytes(selectedTypeSize)})"; var rect = GUILayoutUtility.GetRect(typeModule, EditorStyles.toolbarDropDown); if (!EditorGUI.DropdownButton(rect, typeModule, FocusType.Keyboard, EditorStyles.toolbarDropDown)) return; var menu = new GenericMenu(); for (var index = 0; index < types.Count; index++) { var type = types[index]; menu.AddItem(new GUIContent(type), selectedType == index, data => { selectedType = (int) data; reloadAssets = true; }, index); } menu.DropDown(rect); } public List GetSelectedAssets() { return selectedType < types.Count ? typeWithAssets[types[selectedType]] : new List(); } private void DrawToolbar(Rect toolbarPos) { GUILayout.BeginArea(new Rect(0, 0, toolbarPos.width, k_SearchHeight * 2)); GUILayout.BeginHorizontal(EditorStyles.toolbar); { treeView.searchString = searchField.OnToolbarGUI(treeView.searchString); DrawManifest(); DrawTypes(); GUILayout.FlexibleSpace(); if (GUILayout.Button("Save", EditorStyles.toolbarButton)) SaveSelected(); } GUILayout.EndHorizontal(); GUILayout.EndArea(); } private void SaveSelected() { var path = EditorUtility.SaveFilePanel("Save File", "", $"DependenciesWith{_manifest.name}For{types[selectedType]}Assets", "txt"); if (string.IsNullOrEmpty(path)) return; ShowNotification(new GUIContent("Save Success!")); var assets = GetSelectedAssets(); assets.Sort((a, b) => GetBundlesSize(b, out _).CompareTo(GetBundlesSize(a, out _))); var sb = new StringBuilder(); foreach (var asset in assets) { var size = GetBundlesSize(asset, out var bundles); sb.AppendLine($"{asset}({CaoCao.XAsset.Utility.FormatBytes(size)})"); sb.AppendLine(" - Bundles:" + CaoCao.XAsset.Utility.FormatBytes(size)); bundles.Sort((a, b) => b.size.CompareTo(a.size)); foreach (var bundle in bundles) sb.AppendLine( $" - {bundle.file}({CaoCao.XAsset.Utility.FormatBytes(bundle.size)})"); sb.AppendLine(" - Dependencies:"); var dependencies = new List(); foreach (var dependency in AssetDatabase.GetDependencies(asset)) { if (asset == dependency || !_manifest.TryGetAsset(dependency, out _)) continue; dependencies.Add(dependency); } dependencies.Sort((a, b) => GetBundlesSize(b, out _).CompareTo(GetBundlesSize(a, out _))); foreach (var dependency in dependencies) sb.AppendLine( $" - {dependency}({CaoCao.XAsset.Utility.FormatBytes(GetBundlesSize(dependency, out _))})"); } File.WriteAllText(path, sb.ToString()); EditorUtility.OpenWithDefaultApp(path); } public ulong GetBundlesSize(string asset, out List bundles) { var manifest = _manifest; bundles = new List(); var bundlesSize = 0UL; var bundle = manifest.GetBundle(asset); bundlesSize += bundle.size; bundles.Add(bundle); var dependencies = manifest.GetDependencies(bundle); if (dependencies == null) return bundlesSize; foreach (var dependency in dependencies) { bundlesSize += dependency.size; bundles.Add(dependency); } return bundlesSize; } private void DrawTree(Rect rect) { const int toolbarHeight = k_SearchHeight + 4; var treeRect = new Rect( rect.xMin, rect.yMin + toolbarHeight, rect.width, rect.height - toolbarHeight); treeView.OnGUI(treeRect); } public Manifest GetManifest() { return _manifest; } } }