diff --git a/Assets/YooAsset/Editor/BundleCollector/BundleCollectorSettingData.cs b/Assets/YooAsset/Editor/BundleCollector/BundleCollectorSettingData.cs
index 93e43ebf..2509b817 100644
--- a/Assets/YooAsset/Editor/BundleCollector/BundleCollectorSettingData.cs
+++ b/Assets/YooAsset/Editor/BundleCollector/BundleCollectorSettingData.cs
@@ -175,6 +175,17 @@ namespace YooAsset.Editor
}
}
+ ///
+ /// 工程中是否已存在收集器配置文件
+ ///
+ /// 存在返回 true
+ public static bool HasSettingAsset()
+ {
+ string typeName = nameof(BundleCollectorSetting);
+ var guids = AssetDatabase.FindAssets($"t:{typeName}");
+ return guids != null && guids.Length > 0;
+ }
+
///
/// 存储配置文件
///
diff --git a/Assets/YooAsset/Editor/BundleCollector/BundleCollectorWindow.cs b/Assets/YooAsset/Editor/BundleCollector/BundleCollectorWindow.cs
index 7ae54973..f21d654c 100644
--- a/Assets/YooAsset/Editor/BundleCollector/BundleCollectorWindow.cs
+++ b/Assets/YooAsset/Editor/BundleCollector/BundleCollectorWindow.cs
@@ -19,10 +19,30 @@ namespace YooAsset.Editor
///
[MenuItem("YooAsset/Bundle Collector", false, 101)]
public static void OpenWindow()
+ {
+ OpenWindowInternal();
+ }
+
+ ///
+ /// 打开资源收集器窗口并定位到指定的收集器
+ ///
+ /// 包裹名称
+ /// 分组名称
+ /// 收集路径
+ public static void OpenWindow(string packageName, string groupName, string collectPath)
+ {
+ BundleCollectorWindow window = OpenWindowInternal();
+ window.SetFocusCollector(packageName, groupName, collectPath);
+ window.RefreshWindow();
+ window.Focus();
+ }
+
+ private static BundleCollectorWindow OpenWindowInternal()
{
Type[] dockedTypes = EditorWindowDefine.GetDockedWindowTypes();
BundleCollectorWindow window = GetWindow("Bundle Collector", true, dockedTypes);
window.minSize = new Vector2(800, 600);
+ return window;
}
private const string PlaceholderClass = "search-placeholder";
@@ -77,6 +97,7 @@ namespace YooAsset.Editor
private int _highlightCollectorIndex = -1;
private int _lastModifyPackageIndex = 0;
private int _lastModifyGroupIndex = 0;
+ private bool _hasFocusCollector = false;
private bool _showGlobalSettings = false;
private bool _showPackageSettings = false;
@@ -471,7 +492,8 @@ namespace YooAsset.Editor
private void RefreshWindow()
{
_highlightAssetPath = null;
- _highlightCollectorIndex = -1;
+ if (_hasFocusCollector == false)
+ _highlightCollectorIndex = -1;
_groupContainer.visible = false;
_collectorContainer.visible = false;
@@ -508,6 +530,7 @@ namespace YooAsset.Editor
{
_highlightAssetPath = null;
_highlightCollectorIndex = -1;
+ _hasFocusCollector = false;
FillCollectorViewData();
string searchInput = GetSearchInput();
@@ -577,6 +600,46 @@ namespace YooAsset.Editor
return ruleDisplayName.ClassName;
}
+ // 焦点相关
+ private void SetFocusCollector(string packageName, string groupName, string collectPath)
+ {
+ var packages = BundleCollectorSettingData.Setting.Packages;
+ int packageIndex = packages.FindIndex(item => item.PackageName == packageName);
+ if (packageIndex < 0)
+ {
+ Debug.LogWarning($"Package not found: '{packageName}'.");
+ _highlightCollectorIndex = -1;
+ _hasFocusCollector = false;
+ return;
+ }
+
+ var package = packages[packageIndex];
+ int groupIndex = package.Groups.FindIndex(item => item.GroupName == groupName);
+ if (groupIndex < 0)
+ {
+ Debug.LogWarning($"Group not found: '{groupName}' in package '{packageName}'.");
+ _highlightCollectorIndex = -1;
+ _hasFocusCollector = false;
+ return;
+ }
+
+ var group = package.Groups[groupIndex];
+ int collectorIndex = group.Collectors.FindIndex(item => string.Equals(item.CollectPath, collectPath, StringComparison.OrdinalIgnoreCase));
+ if (collectorIndex < 0)
+ {
+ Debug.LogWarning($"Collector not found: '{collectPath}'.");
+ _highlightCollectorIndex = -1;
+ _hasFocusCollector = false;
+ return;
+ }
+
+ _highlightAssetPath = null;
+ _highlightCollectorIndex = collectorIndex;
+ _lastModifyPackageIndex = packageIndex;
+ _lastModifyGroupIndex = groupIndex;
+ _hasFocusCollector = true;
+ }
+
// 搜索栏相关
private void ShowSearchResult(string message, Color color)
{
@@ -898,6 +961,7 @@ namespace YooAsset.Editor
if (foldout != null)
foldout.value = true;
_highlightCollectorIndex = -1;
+ _hasFocusCollector = false;
}
}
private VisualElement MakeCollectorListViewItem()
diff --git a/Assets/YooAsset/Samples~/Extension Sample/Editor/CollectorInspector.meta b/Assets/YooAsset/Samples~/Extension Sample/Editor/CollectorInspector.meta
new file mode 100644
index 00000000..9c86ef57
--- /dev/null
+++ b/Assets/YooAsset/Samples~/Extension Sample/Editor/CollectorInspector.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: dd8a9e643275082438da0f2f5c4f7f68
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Samples~/Extension Sample/Editor/CollectorInspector/BundleCollectorGUIDraw.cs b/Assets/YooAsset/Samples~/Extension Sample/Editor/CollectorInspector/BundleCollectorGUIDraw.cs
new file mode 100644
index 00000000..cab1d758
--- /dev/null
+++ b/Assets/YooAsset/Samples~/Extension Sample/Editor/CollectorInspector/BundleCollectorGUIDraw.cs
@@ -0,0 +1,92 @@
+using UnityEditor;
+using UnityEngine;
+
+namespace YooAsset.Editor
+{
+ ///
+ /// 扩展的 IMGUI 绘制辅助方法集合
+ ///
+ internal static class BundleCollectorGUIDraw
+ {
+ ///
+ /// 绘制区块标题
+ ///
+ /// 标题文本
+ public static void DrawSectionTitle(string title)
+ {
+ EditorGUILayout.LabelField(title, BundleCollectorGUIStyle.TitleStyle);
+ EditorGUILayout.Space(2);
+ }
+
+ ///
+ /// 绘制一个带字段名的只读文本字段
+ ///
+ /// 字段名
+ /// 字段值
+ /// 是否置灰整行
+ public static void DrawLabelField(string label, string value, bool disabled = false)
+ {
+ using (new EditorGUI.DisabledScope(disabled))
+ {
+ using (new EditorGUILayout.HorizontalScope())
+ {
+ GUILayout.Label(label, BundleCollectorGUIStyle.FieldLabelStyle, GUILayout.Width(EditorGUIUtility.labelWidth));
+ EditorGUILayout.LabelField(string.IsNullOrEmpty(value) ? "-" : value);
+ }
+ }
+ }
+
+ ///
+ /// 绘制一个带字段名的延迟文本输入框
+ ///
+ /// 字段名
+ /// 当前文本值
+ /// 新的文本值
+ public static string DrawTextField(string label, string value)
+ {
+ using (new EditorGUILayout.HorizontalScope())
+ {
+ GUILayout.Label(label, BundleCollectorGUIStyle.FieldLabelStyle, GUILayout.Width(EditorGUIUtility.labelWidth));
+ return EditorGUILayout.DelayedTextField(value);
+ }
+ }
+
+ ///
+ /// 绘制一个带字段名的下拉框
+ ///
+ /// 字段名
+ /// 当前选项索引
+ /// 下拉选项
+ /// 新的选项索引
+ public static int DrawPopupField(string label, int index, string[] displayedOptions)
+ {
+ using (new EditorGUILayout.HorizontalScope())
+ {
+ GUILayout.Label(label, BundleCollectorGUIStyle.FieldLabelStyle, GUILayout.Width(EditorGUIUtility.labelWidth));
+ return EditorGUILayout.Popup(index, displayedOptions);
+ }
+ }
+
+ ///
+ /// 绘制规则选择行
+ ///
+ /// 字段标签
+ /// 显示名称列表
+ /// 当前选中索引
+ /// 新选中的索引
+ /// 规则索引发生变化返回 true
+ public static bool TryDrawRuleSelection(string label, string[] displayNames, int currentIndex, out int newIndex)
+ {
+ newIndex = -1;
+ if (displayNames == null || displayNames.Length == 0)
+ {
+ using (new EditorGUI.DisabledScope(true)) DrawPopupField(label, 0, new[] { "" });
+ return false;
+ }
+
+ currentIndex = Mathf.Clamp(currentIndex, 0, displayNames.Length - 1);
+ newIndex = DrawPopupField(label, currentIndex, displayNames);
+ return newIndex != currentIndex;
+ }
+ }
+}
diff --git a/Assets/YooAsset/Samples~/Extension Sample/Editor/CollectorInspector/BundleCollectorGUIDraw.cs.meta b/Assets/YooAsset/Samples~/Extension Sample/Editor/CollectorInspector/BundleCollectorGUIDraw.cs.meta
new file mode 100644
index 00000000..ee644678
--- /dev/null
+++ b/Assets/YooAsset/Samples~/Extension Sample/Editor/CollectorInspector/BundleCollectorGUIDraw.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: fb2a2401e7c68a64496c3194543aff6b
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Samples~/Extension Sample/Editor/CollectorInspector/BundleCollectorGUIStyle.cs b/Assets/YooAsset/Samples~/Extension Sample/Editor/CollectorInspector/BundleCollectorGUIStyle.cs
new file mode 100644
index 00000000..3ec471a9
--- /dev/null
+++ b/Assets/YooAsset/Samples~/Extension Sample/Editor/CollectorInspector/BundleCollectorGUIStyle.cs
@@ -0,0 +1,71 @@
+using UnityEditor;
+using UnityEngine;
+
+namespace YooAsset.Editor
+{
+ ///
+ /// 扩展的 IMGUI 样式集合
+ ///
+ internal static class BundleCollectorGUIStyle
+ {
+ // 注意:GUIStyle 依赖 GUI 皮肤,不能在静态构造里创建,需延迟到绘制时初始化。
+ private static GUIStyle _titleStyle;
+ private static GUIStyle _fieldLabelStyle;
+ private static GUIStyle _sectionStyle;
+
+ ///
+ /// 区块标题样式:保持正文字号,仅通过加粗和间距与内容区分。
+ ///
+ public static GUIStyle TitleStyle
+ {
+ get
+ {
+ if (_titleStyle == null)
+ {
+ _titleStyle = new GUIStyle(EditorStyles.boldLabel)
+ {
+ fontStyle = FontStyle.Bold,
+ margin = new RectOffset(0, 0, 2, 4),
+ };
+ }
+ return _titleStyle;
+ }
+ }
+
+ ///
+ /// 字段名样式:保持正文字号。
+ ///
+ public static GUIStyle FieldLabelStyle
+ {
+ get
+ {
+ if (_fieldLabelStyle == null)
+ {
+ _fieldLabelStyle = new GUIStyle(EditorStyles.label)
+ {
+ fontStyle = FontStyle.Normal,
+ };
+ }
+ return _fieldLabelStyle;
+ }
+ }
+
+ ///
+ /// 分组卡片样式:基于 helpBox 增加内边距,让内容不贴边。
+ ///
+ public static GUIStyle SectionStyle
+ {
+ get
+ {
+ if (_sectionStyle == null)
+ {
+ _sectionStyle = new GUIStyle(EditorStyles.helpBox)
+ {
+ padding = new RectOffset(6, 6, 5, 6),
+ };
+ }
+ return _sectionStyle;
+ }
+ }
+ }
+}
diff --git a/Assets/YooAsset/Samples~/Extension Sample/Editor/CollectorInspector/BundleCollectorGUIStyle.cs.meta b/Assets/YooAsset/Samples~/Extension Sample/Editor/CollectorInspector/BundleCollectorGUIStyle.cs.meta
new file mode 100644
index 00000000..f56756ea
--- /dev/null
+++ b/Assets/YooAsset/Samples~/Extension Sample/Editor/CollectorInspector/BundleCollectorGUIStyle.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 8f37650836ac1ca4ab53da11208bace4
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Samples~/Extension Sample/Editor/CollectorInspector/BundleCollectorInspector.cs b/Assets/YooAsset/Samples~/Extension Sample/Editor/CollectorInspector/BundleCollectorInspector.cs
new file mode 100644
index 00000000..ae2fafc5
--- /dev/null
+++ b/Assets/YooAsset/Samples~/Extension Sample/Editor/CollectorInspector/BundleCollectorInspector.cs
@@ -0,0 +1,456 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEditor;
+using UnityEngine;
+
+namespace YooAsset.Editor
+{
+ ///
+ /// 资源收集器的 Inspector 扩展
+ ///
+ [InitializeOnLoad]
+ internal static class BundleCollectorInspector
+ {
+ private struct CollectorContext
+ {
+ public BundleCollectorPackage Package;
+ public BundleCollectorGroup Group;
+ public BundleCollector Collector;
+ }
+
+ private static readonly string[] CollectorTypeNames =
+ {
+ nameof(ECollectorType.MainAssetCollector),
+ nameof(ECollectorType.StaticAssetCollector),
+ nameof(ECollectorType.DependAssetCollector),
+ };
+
+ private static int _createPackageIndex;
+ private static int _createGroupIndex;
+
+ static BundleCollectorInspector()
+ {
+ UnityEditor.Editor.finishedDefaultHeaderGUI -= OnPostHeaderGUI;
+ UnityEditor.Editor.finishedDefaultHeaderGUI += OnPostHeaderGUI;
+ }
+
+ ///
+ /// Inspector 默认头部绘制完成后的回调
+ ///
+ /// 当前正在绘制的 Inspector 编辑器实例
+ private static void OnPostHeaderGUI(UnityEditor.Editor editor)
+ {
+ // 注意:多目标选择的时候不绘制
+ if (editor.targets != null && editor.targets.Length > 1)
+ return;
+
+ UnityEngine.Object target = editor.target;
+ if (target == null)
+ return;
+
+ // 检测选择的路径是否合法
+ string assetPath = AssetDatabase.GetAssetPath(target);
+ if (string.IsNullOrEmpty(assetPath))
+ return;
+ if (assetPath == "Assets")
+ return;
+ if (assetPath.StartsWith("Assets/", StringComparison.OrdinalIgnoreCase) == false)
+ return;
+ if (AssetDatabase.IsValidFolder(assetPath) == false)
+ return;
+
+ // 检测配置文件是否存在
+ if (BundleCollectorSettingData.HasSettingAsset() == false)
+ {
+ EditorGUILayout.Space();
+ EditorGUILayout.LabelField("YooAsset", "BundleCollectorSetting.asset not found.");
+ return;
+ }
+
+ // 根据当前文件夹是否已经配置,动态切换展示模式。
+ EditorGUILayout.Space();
+ using (new EditorGUILayout.VerticalScope(BundleCollectorGUIStyle.SectionStyle))
+ {
+ bool isFindCollector = TryGetCollector(assetPath, out CollectorContext collectorContext);
+ if (isFindCollector)
+ {
+ DrawOpenCollectorHeader(collectorContext);
+ DrawTargetCollectorContent(collectorContext);
+ }
+ else
+ {
+ DrawCreateCollectorHeader(assetPath);
+ DrawCreateCollectorSelector();
+ }
+ }
+ }
+ private static void DrawCreateCollectorHeader(string assetPath)
+ {
+ using (new EditorGUILayout.HorizontalScope())
+ {
+ EditorGUILayout.LabelField("YooAsset", EditorStyles.boldLabel, GUILayout.Width(95));
+ GUILayout.FlexibleSpace();
+
+ bool hasCreateTarget = TryGetCreateCollectorTarget(out var package, out var group);
+ using (new EditorGUI.DisabledScope(hasCreateTarget == false))
+ {
+ if (GUILayout.Button("Create Collector", GUILayout.Width(120)))
+ {
+ TryAddCollector(assetPath, package, group, out _);
+ }
+ }
+ }
+ }
+ private static void DrawCreateCollectorSelector()
+ {
+ using (new EditorGUILayout.VerticalScope(BundleCollectorGUIStyle.SectionStyle))
+ {
+ BundleCollectorGUIDraw.DrawSectionTitle("Package & Group");
+
+ var setting = BundleCollectorSettingData.Setting;
+ if (setting == null || setting.Packages.Count == 0)
+ {
+ EditorGUILayout.HelpBox("Please create a Package in the Bundle Collector window first.", MessageType.Info);
+ return;
+ }
+
+ ClampCreateTargetIndices();
+ string[] packageNames = setting.Packages.Select(item => item.PackageName).ToArray();
+ int newPackageIndex = BundleCollectorGUIDraw.DrawPopupField("Package", _createPackageIndex, packageNames);
+ if (newPackageIndex != _createPackageIndex)
+ {
+ _createPackageIndex = newPackageIndex;
+ _createGroupIndex = 0;
+ }
+
+ var package = setting.Packages[_createPackageIndex];
+ if (package.Groups.Count == 0)
+ {
+ EditorGUILayout.HelpBox("Please create a Group in the Bundle Collector window first.", MessageType.Info);
+ return;
+ }
+
+ string[] groupNames = package.Groups.Select(item => item.GroupName).ToArray();
+ _createGroupIndex = BundleCollectorGUIDraw.DrawPopupField("Group", _createGroupIndex, groupNames);
+ }
+ }
+ private static void DrawOpenCollectorHeader(CollectorContext collectorContext)
+ {
+ using (new EditorGUILayout.HorizontalScope())
+ {
+ EditorGUILayout.LabelField("YooAsset", EditorStyles.boldLabel, GUILayout.Width(95));
+ GUILayout.FlexibleSpace();
+
+ if (GUILayout.Button("Open Collector", GUILayout.Width(120)))
+ {
+ BundleCollectorWindow.OpenWindow(collectorContext.Package.PackageName, collectorContext.Group.GroupName, collectorContext.Collector.CollectPath);
+ }
+ }
+ }
+ private static void DrawTargetCollectorContent(CollectorContext collectorContext)
+ {
+ var package = collectorContext.Package;
+ var group = collectorContext.Group;
+ var collector = collectorContext.Collector;
+ var setting = BundleCollectorSettingData.Setting;
+ bool showAlias = setting.ShowEditorAlias;
+
+ float oldLabelWidth = EditorGUIUtility.labelWidth;
+ EditorGUIUtility.labelWidth = 100;
+
+ try
+ {
+ // 区块:包裹与分组
+ using (new EditorGUILayout.VerticalScope(BundleCollectorGUIStyle.SectionStyle))
+ {
+ BundleCollectorGUIDraw.DrawSectionTitle("Package & Group");
+
+ // 包裹
+ var packageNames = setting.Packages.Select(p => p.PackageName).ToArray();
+ int packageIndex = Mathf.Max(0, Array.IndexOf(packageNames, package.PackageName));
+ int newPackageIndex = BundleCollectorGUIDraw.DrawPopupField("Package", packageIndex, packageNames);
+ if (newPackageIndex != packageIndex && MoveCollectorToPackage(collector, group, setting.Packages[newPackageIndex]))
+ return;
+
+ // 分组
+ var groupNames = package.Groups.Select(g => g.GroupName).ToArray();
+ int groupIndex = Mathf.Max(0, Array.IndexOf(groupNames, group.GroupName));
+ int newGroupIndex = BundleCollectorGUIDraw.DrawPopupField("Group", groupIndex, groupNames);
+ if (newGroupIndex != groupIndex)
+ {
+ MoveCollectorToGroup(collector, group, package.Groups[newGroupIndex]);
+ return;
+ }
+ }
+
+ // 区块:收集器配置
+ using (new EditorGUILayout.VerticalScope(BundleCollectorGUIStyle.SectionStyle))
+ {
+ BundleCollectorGUIDraw.DrawSectionTitle("Collector Settings");
+ BundleCollectorGUIDraw.DrawLabelField("Collect Path", collector.CollectPath);
+
+ // 收集器类型
+ int typeIndex = Mathf.Max(0, Array.IndexOf(CollectorTypeNames, collector.CollectorType.ToString()));
+ int newTypeIndex = BundleCollectorGUIDraw.DrawPopupField("Collector Type", typeIndex, CollectorTypeNames);
+ if (newTypeIndex != typeIndex)
+ {
+ RecordUndo("YooAsset.Inspector Modify CollectorType");
+ collector.CollectorType = EditorStringUtility.ParseEnum(CollectorTypeNames[newTypeIndex]);
+ CommitModify(group, collector);
+ }
+
+ // 地址规则(仅对主资源收集器生效)
+ bool isMainCollector = collector.CollectorType == ECollectorType.MainAssetCollector;
+ using (new EditorGUI.DisabledScope(isMainCollector == false))
+ {
+ var addressRules = BundleCollectorSettingData.GetAddressRuleNames();
+ if (BundleCollectorGUIDraw.TryDrawRuleSelection("Address Rule", GetRuleDisplayNames(addressRules, showAlias), GetRuleIndex(addressRules, collector.AddressRuleName), out int newAddressRuleIndex))
+ {
+ RecordUndo("YooAsset.Inspector Modify AddressRule");
+ collector.AddressRuleName = addressRules[newAddressRuleIndex].ClassName;
+ CommitModify(group, collector);
+ }
+ }
+
+ // 打包规则
+ var packRules = BundleCollectorSettingData.GetBundlePackRuleNames();
+ if (BundleCollectorGUIDraw.TryDrawRuleSelection("Pack Rule", GetRuleDisplayNames(packRules, showAlias), GetRuleIndex(packRules, collector.PackRuleName), out int newPackRuleIndex))
+ {
+ RecordUndo("YooAsset.Inspector Modify PackRule");
+ collector.PackRuleName = packRules[newPackRuleIndex].ClassName;
+ CommitModify(group, collector);
+ }
+
+ // 过滤规则
+ var filterRules = BundleCollectorSettingData.GetAssetFilterRuleNames();
+ if (BundleCollectorGUIDraw.TryDrawRuleSelection("Filter Rule", GetRuleDisplayNames(filterRules, showAlias), GetRuleIndex(filterRules, collector.FilterRuleName), out int newFilterRuleIndex))
+ {
+ RecordUndo("YooAsset.Inspector Modify FilterRule");
+ collector.FilterRuleName = filterRules[newFilterRuleIndex].ClassName;
+ CommitModify(group, collector);
+ }
+
+ // 用户数据(延迟提交,避免逐键写盘)
+ string newUserData = BundleCollectorGUIDraw.DrawTextField("UserData", collector.UserData);
+ if (newUserData != collector.UserData)
+ {
+ RecordUndo("YooAsset.Inspector Modify UserData");
+ collector.UserData = newUserData;
+ CommitModify(group, collector);
+ }
+
+ // 资源标签(仅对主资源收集器生效)
+ using (new EditorGUI.DisabledScope(isMainCollector == false))
+ {
+ string newTags = BundleCollectorGUIDraw.DrawTextField("Tags", collector.AssetTags);
+ if (newTags != collector.AssetTags)
+ {
+ RecordUndo("YooAsset.Inspector Modify AssetTags");
+ collector.AssetTags = newTags;
+ CommitModify(group, collector);
+ }
+ }
+ }
+ }
+ finally
+ {
+ EditorGUIUtility.labelWidth = oldLabelWidth;
+ }
+ }
+
+ ///
+ /// 获取规则列表用于下拉框展示的名称数组
+ ///
+ private static string[] GetRuleDisplayNames(List rules, bool showAlias)
+ {
+ if (rules == null || rules.Count == 0)
+ return Array.Empty();
+
+ return rules.Select(rule => showAlias ? rule.DisplayName : rule.ClassName).ToArray();
+ }
+
+ ///
+ /// 根据规则类名查找规则在列表中的索引
+ ///
+ private static int GetRuleIndex(List rules, string className)
+ {
+ if (rules == null || rules.Count == 0)
+ return -1;
+
+ return Mathf.Max(0, rules.FindIndex(rule => rule.ClassName == className));
+ }
+
+ #region 数据查找与编辑
+ ///
+ /// 获取当前创建收集器所选择的目标包裹和分组
+ ///
+ private static bool TryGetCreateCollectorTarget(out BundleCollectorPackage package, out BundleCollectorGroup group)
+ {
+ package = null;
+ group = null;
+
+ var setting = BundleCollectorSettingData.Setting;
+ if (setting == null || setting.Packages.Count == 0)
+ return false;
+
+ ClampCreateTargetIndices();
+ package = setting.Packages[_createPackageIndex];
+ if (package.Groups.Count == 0)
+ return false;
+
+ group = package.Groups[_createGroupIndex];
+ return true;
+ }
+
+ ///
+ /// 将创建目标的包裹/分组索引收敛到当前配置的合法范围
+ ///
+ private static void ClampCreateTargetIndices()
+ {
+ var setting = BundleCollectorSettingData.Setting;
+ if (setting == null || setting.Packages.Count == 0)
+ {
+ _createPackageIndex = 0;
+ _createGroupIndex = 0;
+ return;
+ }
+
+ _createPackageIndex = Mathf.Clamp(_createPackageIndex, 0, setting.Packages.Count - 1);
+ var package = setting.Packages[_createPackageIndex];
+ if (package.Groups.Count == 0)
+ {
+ _createGroupIndex = 0;
+ return;
+ }
+
+ _createGroupIndex = Mathf.Clamp(_createGroupIndex, 0, package.Groups.Count - 1);
+ }
+
+ ///
+ /// 按文件夹路径精确获取已配置的收集器信息
+ ///
+ private static bool TryGetCollector(string folderPath, out CollectorContext result)
+ {
+ result = default;
+
+ var setting = BundleCollectorSettingData.Setting;
+ if (setting == null)
+ return false;
+
+ foreach (var package in setting.Packages)
+ {
+ foreach (var group in package.Groups)
+ {
+ foreach (var collector in group.Collectors)
+ {
+ if (string.Equals(collector.CollectPath, folderPath, StringComparison.OrdinalIgnoreCase))
+ {
+ result = new CollectorContext
+ {
+ Package = package,
+ Group = group,
+ Collector = collector,
+ };
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ ///
+ /// 为指定文件夹在目标包裹分组下创建收集器
+ ///
+ private static bool TryAddCollector(string folderPath, BundleCollectorPackage package, BundleCollectorGroup group, out CollectorContext result)
+ {
+ result = default;
+ if (package == null || group == null)
+ return false;
+
+ RecordUndo("YooAsset.Inspector Add Collector");
+ var collector = new BundleCollector
+ {
+ CollectPath = folderPath,
+ CollectorGUID = AssetDatabase.AssetPathToGUID(folderPath),
+ };
+ BundleCollectorSettingData.CreateCollector(group, collector);
+ BundleCollectorSettingData.SaveFile();
+
+ result = new CollectorContext
+ {
+ Package = package,
+ Group = group,
+ Collector = collector,
+ };
+ return true;
+ }
+
+ ///
+ /// 将收集器移动到目标包裹的第一个分组
+ ///
+ /// 要移动的收集器实例
+ /// 原分组
+ /// 目标包裹
+ /// 目标包裹无分组或无需移动时返回 false
+ private static bool MoveCollectorToPackage(BundleCollector collector, BundleCollectorGroup fromGroup, BundleCollectorPackage toPackage)
+ {
+ if (toPackage.Groups.Count == 0)
+ {
+ Debug.LogWarning($"Package '{toPackage.PackageName}' has no group. Please create a group first.");
+ return false;
+ }
+
+ var toGroup = toPackage.Groups[0];
+ if (toGroup == fromGroup)
+ return false;
+
+ RecordUndo("YooAsset.Inspector Move Collector Package");
+ fromGroup.Collectors.Remove(collector);
+ toGroup.Collectors.Add(collector);
+ BundleCollectorSettingData.ModifyCollector(toGroup, collector);
+ BundleCollectorSettingData.SaveFile();
+ return true;
+ }
+
+ ///
+ /// 将收集器在同一包裹内移动到目标分组
+ ///
+ /// 要移动的收集器实例
+ /// 原分组
+ /// 目标分组
+ private static void MoveCollectorToGroup(BundleCollector collector, BundleCollectorGroup fromGroup, BundleCollectorGroup toGroup)
+ {
+ if (toGroup == fromGroup)
+ return;
+
+ RecordUndo("YooAsset.Inspector Move Collector Group");
+ fromGroup.Collectors.Remove(collector);
+ toGroup.Collectors.Add(collector);
+ BundleCollectorSettingData.ModifyCollector(toGroup, collector);
+ BundleCollectorSettingData.SaveFile();
+ }
+
+ ///
+ /// 标记收集器已修改并持久化配置文件
+ ///
+ /// 收集器所属分组
+ /// 被修改的收集器
+ private static void CommitModify(BundleCollectorGroup group, BundleCollector collector)
+ {
+ BundleCollectorSettingData.ModifyCollector(group, collector);
+ BundleCollectorSettingData.SaveFile();
+ }
+
+ ///
+ /// 在修改配置前登记 Undo,使操作可撤销。
+ ///
+ /// Undo 操作名称
+ private static void RecordUndo(string name)
+ {
+ Undo.RecordObject(BundleCollectorSettingData.Setting, name);
+ }
+ #endregion
+ }
+}
diff --git a/Assets/YooAsset/Samples~/Extension Sample/Editor/CollectorInspector/BundleCollectorInspector.cs.meta b/Assets/YooAsset/Samples~/Extension Sample/Editor/CollectorInspector/BundleCollectorInspector.cs.meta
new file mode 100644
index 00000000..68313a97
--- /dev/null
+++ b/Assets/YooAsset/Samples~/Extension Sample/Editor/CollectorInspector/BundleCollectorInspector.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9f06fac18179b404988b68a675c421af
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant: