feat: Enhance Addressable tools with improved scanning and filtering features
- Updated AddressReferenceGraphWindow to scan for AddressKeys in the _Game directory and added a warning for missing directories. - Enhanced AddressableBatchTool with new filters for asset types (Prefab, Scene, ScriptableObject, Texture, Audio) and improved UI layout for better usability. - Introduced automatic application of grouping and labeling rules during registration in AddressableBatchTool. - Added functionality to quickly scan the _Game folder and improved address building logic. - Updated AddressableRuleSyncWindow to include handling for custom labels and improved reporting of issues. - Enhanced AddressableRules with a whitelist for known labels and refined grouping and labeling logic based on asset prefixes.
This commit is contained in:
@@ -200,11 +200,15 @@ namespace BaseGames.Editor
|
||||
foreach (var kv in keyDict)
|
||||
kv.Value.ExistsInAddressables = registeredAddressValues.Contains(kv.Value.Value);
|
||||
|
||||
// 3. 扫描 .cs 文件引用
|
||||
var csFiles = Directory.GetFiles(
|
||||
Path.Combine(Application.dataPath, "Scripts"),
|
||||
"*.cs",
|
||||
SearchOption.AllDirectories);
|
||||
// 3. 扫描 _Game/ 下所有 .cs 文件中对 AddressKeys 的引用
|
||||
string gameDir = Path.Combine(Application.dataPath, "_Game");
|
||||
if (!Directory.Exists(gameDir))
|
||||
{
|
||||
Debug.LogWarning($"[AddressReferenceGraph] 未找到游戏目录:{gameDir}");
|
||||
foreach (var kv in keyDict) _entries.Add(kv.Value);
|
||||
return;
|
||||
}
|
||||
var csFiles = Directory.GetFiles(gameDir, "*.cs", SearchOption.AllDirectories);
|
||||
|
||||
foreach (var file in csFiles)
|
||||
{
|
||||
|
||||
@@ -60,6 +60,11 @@ namespace BaseGames.Editor
|
||||
// Tab ③
|
||||
private List<SelectionEntry> _selectionEntries;
|
||||
private Vector2 _selectionScrollPos;
|
||||
private bool _selFilterPrefab = true;
|
||||
private bool _selFilterScene = true;
|
||||
private bool _selFilterSO = true;
|
||||
private bool _selFilterTexture;
|
||||
private bool _selFilterAudio;
|
||||
|
||||
// 共用
|
||||
private int _targetGroupIndex;
|
||||
@@ -67,6 +72,7 @@ namespace BaseGames.Editor
|
||||
private string _newGroupName = "New Group";
|
||||
private string _newLabel = "";
|
||||
private bool _overwriteAddress;
|
||||
private bool _applyRulesOnRegister = true;
|
||||
|
||||
// ── 样式(惰性初始化)────────────────────────────────────────────────
|
||||
private GUIStyle _headerStyle;
|
||||
@@ -81,7 +87,7 @@ namespace BaseGames.Editor
|
||||
public static void OpenWindow()
|
||||
{
|
||||
var win = GetWindow<AddressableBatchTool>(Title);
|
||||
win.minSize = new Vector2(600, 460);
|
||||
win.minSize = new Vector2(920, 520);
|
||||
win.Show();
|
||||
}
|
||||
|
||||
@@ -144,9 +150,11 @@ namespace BaseGames.Editor
|
||||
// 列表表头
|
||||
using (new EditorGUILayout.HorizontalScope(EditorStyles.toolbar))
|
||||
{
|
||||
EditorGUILayout.LabelField("常量名", _boldStyle, GUILayout.Width(200));
|
||||
EditorGUILayout.LabelField("地址 Key", _boldStyle, GUILayout.Width(180));
|
||||
EditorGUILayout.LabelField("状态", _boldStyle, GUILayout.Width(100));
|
||||
EditorGUILayout.LabelField("常量名", _boldStyle, GUILayout.Width(180));
|
||||
EditorGUILayout.LabelField("地址 Key", _boldStyle, GUILayout.Width(160));
|
||||
EditorGUILayout.LabelField("期望分组", _boldStyle, GUILayout.Width(110));
|
||||
EditorGUILayout.LabelField("期望标签", _boldStyle, GUILayout.Width(140));
|
||||
EditorGUILayout.LabelField("状态", _boldStyle, GUILayout.Width(80));
|
||||
EditorGUILayout.LabelField("匹配资产", _boldStyle);
|
||||
}
|
||||
|
||||
@@ -160,24 +168,30 @@ namespace BaseGames.Editor
|
||||
{
|
||||
using (new EditorGUILayout.HorizontalScope(GUILayout.Height(20)))
|
||||
{
|
||||
EditorGUILayout.LabelField(entry.FieldName, GUILayout.Width(200));
|
||||
EditorGUILayout.LabelField(entry.AddressKey, GUILayout.Width(180));
|
||||
EditorGUILayout.LabelField(entry.FieldName, GUILayout.Width(180));
|
||||
EditorGUILayout.LabelField(entry.AddressKey, GUILayout.Width(160));
|
||||
EditorGUILayout.LabelField(
|
||||
AddressableRules.GetExpectedGroup(entry.AddressKey) ?? "Default",
|
||||
GUILayout.Width(110));
|
||||
EditorGUILayout.LabelField(
|
||||
FormatLabels(AddressableRules.GetExpectedLabels(entry.AddressKey)),
|
||||
GUILayout.Width(140));
|
||||
|
||||
if (entry.IsRegistered)
|
||||
{
|
||||
EditorGUILayout.LabelField("✅ 已注册", _okStyle, GUILayout.Width(100));
|
||||
EditorGUILayout.LabelField("✅ 已注册", _okStyle, GUILayout.Width(80));
|
||||
EditorGUILayout.LabelField(entry.ExistingAssetPath ?? "—");
|
||||
}
|
||||
else if (entry.FoundAssetPath != null)
|
||||
{
|
||||
EditorGUILayout.LabelField("⚠ 未注册", _warnStyle, GUILayout.Width(100));
|
||||
EditorGUILayout.LabelField("⚠ 未注册", _warnStyle, GUILayout.Width(80));
|
||||
EditorGUILayout.LabelField(entry.FoundAssetPath, GUILayout.ExpandWidth(true));
|
||||
if (GUILayout.Button("注册", GUILayout.Width(50)))
|
||||
RegisterKeyEntry(entry);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.LabelField("❌ 未找到", _warnStyle, GUILayout.Width(100));
|
||||
EditorGUILayout.LabelField("❌ 未找到", _warnStyle, GUILayout.Width(80));
|
||||
entry.ManualAsset = (UnityEngine.Object)EditorGUILayout.ObjectField(
|
||||
entry.ManualAsset, typeof(UnityEngine.Object), false);
|
||||
if (entry.ManualAsset != null)
|
||||
@@ -247,6 +261,8 @@ namespace BaseGames.Editor
|
||||
|
||||
using (new EditorGUILayout.HorizontalScope())
|
||||
{
|
||||
if (GUILayout.Button("⚡ 全量扫描 _Game/", GUILayout.Width(150)))
|
||||
QuickScanGameFolder();
|
||||
GUILayout.FlexibleSpace();
|
||||
GUI.enabled = !string.IsNullOrEmpty(_folderPath);
|
||||
if (GUILayout.Button("扫描文件夹", GUILayout.Width(100)))
|
||||
@@ -266,9 +282,14 @@ namespace BaseGames.Editor
|
||||
EditorGUILayout.Space(4);
|
||||
using (new EditorGUILayout.HorizontalScope(EditorStyles.toolbar))
|
||||
{
|
||||
EditorGUILayout.LabelField("资产路径", _boldStyle, GUILayout.ExpandWidth(true));
|
||||
EditorGUILayout.LabelField("预计地址", _boldStyle, GUILayout.Width(200));
|
||||
EditorGUILayout.LabelField("状态", _boldStyle, GUILayout.Width(80));
|
||||
EditorGUILayout.LabelField("资产路径", _boldStyle, GUILayout.Width(180));
|
||||
EditorGUILayout.LabelField("地址", _boldStyle, GUILayout.Width(150));
|
||||
if (_applyRulesOnRegister)
|
||||
{
|
||||
EditorGUILayout.LabelField("分组(规则)", _boldStyle, GUILayout.Width(120));
|
||||
EditorGUILayout.LabelField("标签(规则)", _boldStyle, GUILayout.Width(150));
|
||||
}
|
||||
EditorGUILayout.LabelField("状态", _boldStyle, GUILayout.Width(70));
|
||||
}
|
||||
|
||||
_folderScrollPos = EditorGUILayout.BeginScrollView(_folderScrollPos, GUILayout.ExpandHeight(true));
|
||||
@@ -276,11 +297,16 @@ namespace BaseGames.Editor
|
||||
{
|
||||
using (new EditorGUILayout.HorizontalScope(GUILayout.Height(18)))
|
||||
{
|
||||
EditorGUILayout.LabelField(entry.AssetPath, GUILayout.ExpandWidth(true));
|
||||
entry.Address = EditorGUILayout.TextField(entry.Address, GUILayout.Width(200));
|
||||
EditorGUILayout.LabelField(entry.AssetPath, GUILayout.Width(180));
|
||||
entry.Address = EditorGUILayout.TextField(entry.Address, GUILayout.Width(150));
|
||||
if (_applyRulesOnRegister)
|
||||
{
|
||||
EditorGUILayout.LabelField(entry.PredictedGroup ?? "Default", GUILayout.Width(120));
|
||||
EditorGUILayout.LabelField(entry.PredictedLabels ?? "—", GUILayout.Width(150));
|
||||
}
|
||||
var label = entry.AlreadyRegistered ? "✅ 已有" : "待注册";
|
||||
var style = entry.AlreadyRegistered ? _okStyle : EditorStyles.miniLabel;
|
||||
EditorGUILayout.LabelField(label, style, GUILayout.Width(80));
|
||||
EditorGUILayout.LabelField(label, style, GUILayout.Width(70));
|
||||
}
|
||||
}
|
||||
EditorGUILayout.EndScrollView();
|
||||
@@ -296,6 +322,18 @@ namespace BaseGames.Editor
|
||||
EditorGUILayout.LabelField("在 Project 窗口中选中资产或文件夹,然后点击「读取选中项」。", EditorStyles.wordWrappedMiniLabel);
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
// 资产类型筛选(与 Tab ② 一致,防止误注册不该 Addressable 的文件类型)
|
||||
EditorGUILayout.LabelField("资产类型筛选", EditorStyles.boldLabel);
|
||||
using (new EditorGUILayout.HorizontalScope())
|
||||
{
|
||||
_selFilterPrefab = GUILayout.Toggle(_selFilterPrefab, "Prefab", GUILayout.Width(70));
|
||||
_selFilterScene = GUILayout.Toggle(_selFilterScene, "Scene", GUILayout.Width(70));
|
||||
_selFilterSO = GUILayout.Toggle(_selFilterSO, "SO/Asset", GUILayout.Width(80));
|
||||
_selFilterTexture = GUILayout.Toggle(_selFilterTexture, "Texture", GUILayout.Width(70));
|
||||
_selFilterAudio = GUILayout.Toggle(_selFilterAudio, "Audio", GUILayout.Width(70));
|
||||
}
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
using (new EditorGUILayout.HorizontalScope())
|
||||
{
|
||||
GUILayout.FlexibleSpace();
|
||||
@@ -316,9 +354,14 @@ namespace BaseGames.Editor
|
||||
EditorGUILayout.Space(4);
|
||||
using (new EditorGUILayout.HorizontalScope(EditorStyles.toolbar))
|
||||
{
|
||||
EditorGUILayout.LabelField("资产路径", _boldStyle, GUILayout.ExpandWidth(true));
|
||||
EditorGUILayout.LabelField("注册地址", _boldStyle, GUILayout.Width(200));
|
||||
EditorGUILayout.LabelField("状态", _boldStyle, GUILayout.Width(80));
|
||||
EditorGUILayout.LabelField("资产路径", _boldStyle, GUILayout.Width(180));
|
||||
EditorGUILayout.LabelField("注册地址", _boldStyle, GUILayout.Width(150));
|
||||
if (_applyRulesOnRegister)
|
||||
{
|
||||
EditorGUILayout.LabelField("分组(规则)", _boldStyle, GUILayout.Width(120));
|
||||
EditorGUILayout.LabelField("标签(规则)", _boldStyle, GUILayout.Width(150));
|
||||
}
|
||||
EditorGUILayout.LabelField("状态", _boldStyle, GUILayout.Width(70));
|
||||
}
|
||||
|
||||
_selectionScrollPos = EditorGUILayout.BeginScrollView(_selectionScrollPos, GUILayout.ExpandHeight(true));
|
||||
@@ -326,11 +369,16 @@ namespace BaseGames.Editor
|
||||
{
|
||||
using (new EditorGUILayout.HorizontalScope(GUILayout.Height(18)))
|
||||
{
|
||||
EditorGUILayout.LabelField(entry.AssetPath, GUILayout.ExpandWidth(true));
|
||||
entry.Address = EditorGUILayout.TextField(entry.Address, GUILayout.Width(200));
|
||||
EditorGUILayout.LabelField(entry.AssetPath, GUILayout.Width(180));
|
||||
entry.Address = EditorGUILayout.TextField(entry.Address, GUILayout.Width(150));
|
||||
if (_applyRulesOnRegister)
|
||||
{
|
||||
EditorGUILayout.LabelField(entry.PredictedGroup ?? "Default", GUILayout.Width(120));
|
||||
EditorGUILayout.LabelField(entry.PredictedLabels ?? "—", GUILayout.Width(150));
|
||||
}
|
||||
var label = entry.AlreadyRegistered ? "✅ 已有" : "待注册";
|
||||
var style = entry.AlreadyRegistered ? _okStyle : EditorStyles.miniLabel;
|
||||
EditorGUILayout.LabelField(label, style, GUILayout.Width(80));
|
||||
EditorGUILayout.LabelField(label, style, GUILayout.Width(70));
|
||||
}
|
||||
}
|
||||
EditorGUILayout.EndScrollView();
|
||||
@@ -347,22 +395,32 @@ namespace BaseGames.Editor
|
||||
using (new EditorGUILayout.HorizontalScope())
|
||||
{
|
||||
EditorGUILayout.LabelField("目标分组", GUILayout.Width(70));
|
||||
if (_groupNames != null && _groupNames.Length > 0)
|
||||
// 自动规则模式下,目标分组由规则决定,手动选择无效
|
||||
GUI.enabled = !_applyRulesOnRegister;
|
||||
if (_applyRulesOnRegister)
|
||||
{
|
||||
EditorGUILayout.LabelField("(由 AddressableRules 自动决定)",
|
||||
EditorStyles.miniLabel, GUILayout.Width(200));
|
||||
}
|
||||
else if (_groupNames != null && _groupNames.Length > 0)
|
||||
{
|
||||
_targetGroupIndex = EditorGUILayout.Popup(_targetGroupIndex,
|
||||
_groupNames, GUILayout.Width(200));
|
||||
}
|
||||
GUI.enabled = true;
|
||||
|
||||
EditorGUILayout.Space(8);
|
||||
EditorGUILayout.LabelField("标签", GUILayout.Width(30));
|
||||
EditorGUILayout.LabelField("附加标签", GUILayout.Width(52));
|
||||
_newLabel = EditorGUILayout.TextField(_newLabel, GUILayout.Width(120));
|
||||
GUILayout.Label("(留空则不添加标签)", EditorStyles.miniLabel);
|
||||
GUILayout.Label("(可在规则标签基础上追加)", EditorStyles.miniLabel);
|
||||
GUILayout.FlexibleSpace();
|
||||
}
|
||||
|
||||
using (new EditorGUILayout.HorizontalScope())
|
||||
{
|
||||
_overwriteAddress = GUILayout.Toggle(_overwriteAddress, "已注册的资产也覆盖地址");
|
||||
GUILayout.Space(16);
|
||||
_applyRulesOnRegister = GUILayout.Toggle(_applyRulesOnRegister, "自动应用分组/标签规则");
|
||||
GUILayout.FlexibleSpace();
|
||||
if (GUILayout.Button("新建分组…", GUILayout.Width(100)))
|
||||
ShowCreateGroupDialog();
|
||||
@@ -479,23 +537,45 @@ namespace BaseGames.Editor
|
||||
|
||||
string absFolder = Path.GetFullPath(_folderPath);
|
||||
|
||||
// 收集所有文件
|
||||
var allFiles = new List<string>();
|
||||
foreach (string filter in filters)
|
||||
allFiles.AddRange(Directory.GetFiles(absFolder, filter, option));
|
||||
|
||||
try
|
||||
{
|
||||
foreach (string absPath in Directory.GetFiles(absFolder, filter, option))
|
||||
for (int i = 0; i < allFiles.Count; i++)
|
||||
{
|
||||
string absPath = allFiles[i];
|
||||
|
||||
if (i % 20 == 0)
|
||||
EditorUtility.DisplayProgressBar("扫描文件夹",
|
||||
Path.GetFileName(absPath),
|
||||
(float)i / allFiles.Count);
|
||||
|
||||
string relPath = "Assets" + absPath.Substring(Application.dataPath.Length).Replace('\\', '/');
|
||||
if (!IsAddressableAssetPath(relPath)) continue;
|
||||
if (ShouldExclude(relPath)) continue;
|
||||
|
||||
string guid = AssetDatabase.AssetPathToGUID(relPath);
|
||||
if (string.IsNullOrEmpty(guid)) continue;
|
||||
|
||||
string addr = BuildAddress(relPath);
|
||||
_folderEntries.Add(new FolderEntry
|
||||
{
|
||||
AssetPath = relPath,
|
||||
Guid = guid,
|
||||
Address = BuildAddress(relPath),
|
||||
Address = addr,
|
||||
AlreadyRegistered = registeredGuids.Contains(guid),
|
||||
PredictedGroup = AddressableRules.GetExpectedGroup(addr),
|
||||
PredictedLabels = FormatLabels(AddressableRules.GetExpectedLabels(addr)),
|
||||
});
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
}
|
||||
|
||||
// 去重(多个 filter 可能匹配同一文件)
|
||||
_folderEntries = _folderEntries
|
||||
@@ -504,6 +584,34 @@ namespace BaseGames.Editor
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回 true 表示该文件应从 Addressable 注册中排除。
|
||||
/// 规范来源:AddressablesLabelSpec §5.2(禁止注册项)。
|
||||
/// </summary>
|
||||
private static bool ShouldExclude(string relPath)
|
||||
{
|
||||
string lowerPath = relPath.Replace('\\', '/').ToLowerInvariant();
|
||||
string fileName = Path.GetFileNameWithoutExtension(relPath);
|
||||
|
||||
// 测试场景(放在 Scenes/Testings/ 下)
|
||||
if (lowerPath.Contains("/scenes/testings/")) return true;
|
||||
|
||||
// 事件频道 ScriptableObject(EVT_ 前缀)
|
||||
if (fileName.StartsWith("EVT_", StringComparison.Ordinal)) return true;
|
||||
|
||||
// Sprite Atlas — 随依赖它的 Prefab 隐式打包,不单独注册
|
||||
if (Path.GetExtension(relPath) == ".spriteatlas") return true;
|
||||
|
||||
// Material — 随 Prefab 依赖关系打包,不单独注册
|
||||
if (Path.GetExtension(relPath) == ".mat") return true;
|
||||
|
||||
// HitBox / HurtBox 等碰撞盒子 Prefab(子 Prefab,不独立寻址)
|
||||
if (fileName.StartsWith("HitBox_", StringComparison.OrdinalIgnoreCase)) return true;
|
||||
if (fileName.StartsWith("HurtBox_", StringComparison.OrdinalIgnoreCase)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void RegisterAllFolderEntries()
|
||||
{
|
||||
int count = 0;
|
||||
@@ -554,12 +662,29 @@ namespace BaseGames.Editor
|
||||
|
||||
private void AddSelectionEntry(string guid, string path, HashSet<string> registeredGuids)
|
||||
{
|
||||
if (!IsAddressableAssetPath(path)) return;
|
||||
if (ShouldExclude(path)) return;
|
||||
|
||||
// 资产类型筛选(与 Tab ③ 筛选 Toggle 联动)
|
||||
string ext = Path.GetExtension(path).ToLowerInvariant();
|
||||
bool isMatch = (_selFilterPrefab && ext == ".prefab")
|
||||
|| (_selFilterScene && ext == ".unity")
|
||||
|| (_selFilterSO && ext == ".asset")
|
||||
|| (_selFilterTexture && (ext == ".png" || ext == ".jpg" || ext == ".tga"))
|
||||
|| (_selFilterAudio && (ext == ".mp3" || ext == ".wav" || ext == ".ogg"));
|
||||
// 若没有任何类型 Toggle 被勾选,则接受所有类型(兜底行为,避免全部筛空)
|
||||
bool anyToggled = _selFilterPrefab || _selFilterScene || _selFilterSO || _selFilterTexture || _selFilterAudio;
|
||||
if (anyToggled && !isMatch) return;
|
||||
|
||||
string addr = BuildAddress(path);
|
||||
_selectionEntries.Add(new SelectionEntry
|
||||
{
|
||||
AssetPath = path,
|
||||
Guid = guid,
|
||||
Address = BuildAddress(path),
|
||||
Address = addr,
|
||||
AlreadyRegistered = registeredGuids.Contains(guid),
|
||||
PredictedGroup = AddressableRules.GetExpectedGroup(addr),
|
||||
PredictedLabels = FormatLabels(AddressableRules.GetExpectedLabels(addr)),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -584,8 +709,24 @@ namespace BaseGames.Editor
|
||||
if (string.IsNullOrEmpty(guid)) return;
|
||||
|
||||
var settings = AddressableAssetSettingsDefaultObject.Settings;
|
||||
var group = groupNameOverride != null
|
||||
? GetOrCreateGroup(settings, groupNameOverride)
|
||||
|
||||
// 重复地址检查:同一 address 已注册到不同 GUID 时提示确认
|
||||
var existingByAddress = FindEntryByAddress(settings, address);
|
||||
if (existingByAddress != null && existingByAddress.guid != guid)
|
||||
{
|
||||
bool proceed = EditorUtility.DisplayDialog(
|
||||
"⚠ 地址已存在",
|
||||
$"地址 \"{address}\" 已注册到:\n{existingByAddress.AssetPath}\n\n" +
|
||||
$"继续会将该地址重新指向当前资产(GUID: {guid})。是否继续?",
|
||||
"继续", "取消");
|
||||
if (!proceed) return;
|
||||
}
|
||||
|
||||
// Determine target group: explicit override → rules → manual selection
|
||||
string effectiveGroup = groupNameOverride
|
||||
?? (_applyRulesOnRegister ? AddressableRules.GetExpectedGroup(address) : null);
|
||||
var group = effectiveGroup != null
|
||||
? GetOrCreateGroup(settings, effectiveGroup)
|
||||
: GetTargetGroup(settings);
|
||||
if (group == null) return;
|
||||
|
||||
@@ -601,6 +742,28 @@ namespace BaseGames.Editor
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(_newLabel))
|
||||
entry.SetLabel(_newLabel.Trim(), true, true);
|
||||
|
||||
// Apply rules-based labels
|
||||
if (_applyRulesOnRegister)
|
||||
{
|
||||
foreach (var lbl in AddressableRules.GetExpectedLabels(address))
|
||||
{
|
||||
EnsureLabelExists(settings, lbl);
|
||||
entry.SetLabel(lbl, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static AddressableAssetEntry FindEntryByAddress(AddressableAssetSettings settings, string address)
|
||||
{
|
||||
if (settings == null) return null;
|
||||
foreach (var group in settings.groups)
|
||||
{
|
||||
if (group == null) continue;
|
||||
foreach (var e in group.entries)
|
||||
if (e != null && e.address == address) return e;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private AddressableAssetGroup GetOrCreateGroup(AddressableAssetSettings settings, string groupName)
|
||||
@@ -636,8 +799,36 @@ namespace BaseGames.Editor
|
||||
AddressableAssetSettings.ModificationEvent.EntryModified, null, true);
|
||||
}
|
||||
|
||||
private static void EnsureLabelExists(AddressableAssetSettings settings, string label)
|
||||
{
|
||||
if (!settings.GetLabels().Contains(label))
|
||||
{
|
||||
settings.AddLabel(label, true);
|
||||
Debug.Log($"[AddressableBatch] 已创建标签:{label}");
|
||||
}
|
||||
}
|
||||
|
||||
private static string FormatLabels(string[] labels)
|
||||
=> labels.Length > 0 ? string.Join(", ", labels) : "—";
|
||||
|
||||
// ══ 创建分组 ══════════════════════════════════════════════════════════
|
||||
|
||||
private void QuickScanGameFolder()
|
||||
{
|
||||
_folderPath = "Assets/_Game";
|
||||
_folderAsset = AssetDatabase.LoadAssetAtPath<DefaultAsset>(_folderPath);
|
||||
_includeSubfolders = true;
|
||||
_filterPrefab = true;
|
||||
_filterScene = true;
|
||||
_filterSO = true;
|
||||
_filterAudio = true;
|
||||
_filterTexture = false;
|
||||
_addressFormat = AddressFormat.FileName;
|
||||
_applyRulesOnRegister = true;
|
||||
_tab = 1;
|
||||
ScanFolder();
|
||||
}
|
||||
|
||||
private void ShowCreateGroupDialog()
|
||||
{
|
||||
_newGroupName = EditorInputDialog.Show("新建 Addressable 分组", "请输入分组名称:", _newGroupName);
|
||||
@@ -685,13 +876,27 @@ namespace BaseGames.Editor
|
||||
private string BuildAddress(string assetPath)
|
||||
{
|
||||
string fileName = Path.GetFileNameWithoutExtension(assetPath);
|
||||
string relativePath = MakeRelativePath(assetPath, _folderPath);
|
||||
|
||||
return _addressFormat switch
|
||||
{
|
||||
AddressFormat.FileName => fileName,
|
||||
AddressFormat.FullAssetPath => assetPath,
|
||||
AddressFormat.RelativeToFolder => MakeRelativePath(assetPath, _folderPath),
|
||||
AddressFormat.PrefixPlusFileName => _addressPrefix + fileName,
|
||||
AddressFormat.PrefixPlusRelativePath=> _addressPrefix + MakeRelativePath(assetPath, _folderPath),
|
||||
AddressFormat.RelativeToFolder => relativePath,
|
||||
// 前缀拼接:前缀以 '/' 结尾时直接连接(如 "Config/" + "FootstepCatalog")
|
||||
// 前缀不以 '/' 结尾时用 '_' 连接(如 "Room_Forest" + "_01")
|
||||
AddressFormat.PrefixPlusFileName =>
|
||||
string.IsNullOrEmpty(_addressPrefix)
|
||||
? fileName
|
||||
: (_addressPrefix.EndsWith("/")
|
||||
? _addressPrefix + fileName
|
||||
: _addressPrefix + "_" + fileName),
|
||||
AddressFormat.PrefixPlusRelativePath =>
|
||||
string.IsNullOrEmpty(_addressPrefix)
|
||||
? relativePath
|
||||
: (_addressPrefix.EndsWith("/")
|
||||
? _addressPrefix + relativePath
|
||||
: _addressPrefix + "_" + relativePath),
|
||||
_ => fileName,
|
||||
};
|
||||
}
|
||||
@@ -716,6 +921,29 @@ namespace BaseGames.Editor
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断路径是否为可寻址资产(排除脚本、程序集定义、Shader、Sprite Atlas、Material 等文件)。
|
||||
/// </summary>
|
||||
private static bool IsAddressableAssetPath(string path)
|
||||
{
|
||||
string ext = Path.GetExtension(path);
|
||||
if (string.IsNullOrEmpty(ext)) return false;
|
||||
// 排除代码 / 元数据类文件
|
||||
return ext != ".cs"
|
||||
&& ext != ".asmdef"
|
||||
&& ext != ".asmref"
|
||||
&& ext != ".shader"
|
||||
&& ext != ".hlsl"
|
||||
&& ext != ".cginc"
|
||||
&& ext != ".glsl"
|
||||
&& ext != ".json"
|
||||
&& ext != ".xml"
|
||||
&& ext != ".txt"
|
||||
&& ext != ".md"
|
||||
&& ext != ".spriteatlas" // 随依赖它的 Prefab 隐式打包
|
||||
&& ext != ".mat"; // Material 随 Prefab 依赖打包
|
||||
}
|
||||
|
||||
/// <summary>从 AddressKey(如 "ENM_GruntWarrior")派生搜索名("GruntWarrior")。</summary>
|
||||
private static string DeriveName(string key)
|
||||
{
|
||||
@@ -740,27 +968,6 @@ namespace BaseGames.Editor
|
||||
return string.Equals(name, searchName, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断路径是否为可寻址资产(排除脚本、程序集定义、Shader 等代码文件)。
|
||||
/// </summary>
|
||||
private static bool IsAddressableAssetPath(string path)
|
||||
{
|
||||
string ext = Path.GetExtension(path);
|
||||
if (string.IsNullOrEmpty(ext)) return false;
|
||||
// 排除代码 / 元数据类文件
|
||||
return ext != ".cs"
|
||||
&& ext != ".asmdef"
|
||||
&& ext != ".asmref"
|
||||
&& ext != ".shader"
|
||||
&& ext != ".hlsl"
|
||||
&& ext != ".cginc"
|
||||
&& ext != ".glsl"
|
||||
&& ext != ".json"
|
||||
&& ext != ".xml"
|
||||
&& ext != ".txt"
|
||||
&& ext != ".md";
|
||||
}
|
||||
|
||||
private void InitStyles()
|
||||
{
|
||||
if (_stylesInitialized) return;
|
||||
@@ -790,6 +997,8 @@ namespace BaseGames.Editor
|
||||
public string Guid;
|
||||
public string Address;
|
||||
public bool AlreadyRegistered;
|
||||
public string PredictedGroup;
|
||||
public string PredictedLabels;
|
||||
}
|
||||
|
||||
private class SelectionEntry
|
||||
@@ -798,6 +1007,8 @@ namespace BaseGames.Editor
|
||||
public string Guid;
|
||||
public string Address;
|
||||
public bool AlreadyRegistered;
|
||||
public string PredictedGroup;
|
||||
public string PredictedLabels;
|
||||
}
|
||||
|
||||
private enum AddressFormat
|
||||
|
||||
@@ -36,11 +36,13 @@ namespace BaseGames.Editor
|
||||
public string ExpectedGroup; // null = 规则未覆盖,维持现状
|
||||
public string[] CurrentLabels;
|
||||
public string[] ExpectedLabels;
|
||||
public string[] MissingLabels; // 应有但没有
|
||||
public string[] ExtraLabels; // 有但不应有
|
||||
public string[] MissingLabels; // 应有但没有(规则要求),红色错误
|
||||
public string[] ExtraLabels; // 规则不要求且在 KnownLabels 中(多余规则标签),红色错误
|
||||
public string[] UnknownLabels; // 规则不要求且不在 KnownLabels 中(自定义标签),黄色警告,不自动删除
|
||||
public bool GroupOk => ExpectedGroup == null || CurrentGroup == ExpectedGroup;
|
||||
public bool LabelsOk => MissingLabels.Length == 0 && ExtraLabels.Length == 0;
|
||||
public bool IsOk => GroupOk && LabelsOk;
|
||||
public bool HasWarnings => UnknownLabels.Length > 0;
|
||||
}
|
||||
|
||||
// ── 状态 ──────────────────────────────────────────────────────────────
|
||||
@@ -74,7 +76,7 @@ namespace BaseGames.Editor
|
||||
public static void OpenWindow()
|
||||
{
|
||||
var win = GetWindow<AddressableRuleSyncWindow>("Addressable Rule Sync");
|
||||
win.minSize = new Vector2(900, 520);
|
||||
win.minSize = new Vector2(1040, 540);
|
||||
win.Show();
|
||||
}
|
||||
|
||||
@@ -108,6 +110,9 @@ namespace BaseGames.Editor
|
||||
if (GUILayout.Button("扫描", EditorStyles.toolbarButton, GUILayout.Width(80)))
|
||||
Scan();
|
||||
|
||||
if (GUILayout.Button("🔄 刷新", EditorStyles.toolbarButton, GUILayout.Width(60)))
|
||||
Scan();
|
||||
|
||||
GUILayout.Space(8);
|
||||
_showOk = GUILayout.Toggle(_showOk, "显示正常项", EditorStyles.toolbarButton, GUILayout.Width(80));
|
||||
GUILayout.Space(8);
|
||||
@@ -136,10 +141,12 @@ namespace BaseGames.Editor
|
||||
|
||||
int total = _reports.Count;
|
||||
int ok = _reports.Count(r => r.IsOk);
|
||||
int issues = total - ok;
|
||||
int issues = _reports.Count(r => !r.IsOk);
|
||||
int warnings = _reports.Count(r => r.IsOk && r.HasWarnings);
|
||||
int wrongGrp = _reports.Count(r => !r.GroupOk);
|
||||
int misLabel = _reports.Count(r => r.MissingLabels.Length > 0);
|
||||
int extLabel = _reports.Count(r => r.ExtraLabels.Length > 0);
|
||||
int unkLabel = _reports.Count(r => r.UnknownLabels.Length > 0);
|
||||
|
||||
EditorGUILayout.Space(2);
|
||||
using (new EditorGUILayout.HorizontalScope())
|
||||
@@ -148,9 +155,11 @@ namespace BaseGames.Editor
|
||||
GUILayout.Space(12);
|
||||
DrawColoredLabel($"✅ 正常 {ok}", ColOk);
|
||||
GUILayout.Space(12);
|
||||
DrawColoredLabel($"⚠ 问题 {issues}", issues > 0 ? ColWarn : ColOk);
|
||||
DrawColoredLabel($"❌ 问题 {issues}", issues > 0 ? ColError : ColOk);
|
||||
GUILayout.Space(8);
|
||||
DrawColoredLabel($"⚠ 自定义标签 {unkLabel}", unkLabel > 0 ? ColWarn : ColOk);
|
||||
GUILayout.Space(20);
|
||||
GUILayout.Label($"分组错误 {wrongGrp} | 标签缺失 {misLabel} | 标签多余 {extLabel}",
|
||||
GUILayout.Label($"分组错误 {wrongGrp} | 标签缺失 {misLabel} | 多余规则标签 {extLabel}",
|
||||
EditorStyles.miniLabel);
|
||||
GUILayout.FlexibleSpace();
|
||||
}
|
||||
@@ -165,10 +174,11 @@ namespace BaseGames.Editor
|
||||
using (new EditorGUILayout.HorizontalScope(EditorStyles.toolbar))
|
||||
{
|
||||
GUILayout.Label("Address", _boldStyle, GUILayout.Width(200));
|
||||
GUILayout.Label("当前分组", _boldStyle, GUILayout.Width(130));
|
||||
GUILayout.Label("期望分组", _boldStyle, GUILayout.Width(130));
|
||||
GUILayout.Label("缺失标签", _boldStyle, GUILayout.Width(140));
|
||||
GUILayout.Label("多余标签", _boldStyle, GUILayout.Width(120));
|
||||
GUILayout.Label("当前分组", _boldStyle, GUILayout.Width(120));
|
||||
GUILayout.Label("期望分组", _boldStyle, GUILayout.Width(120));
|
||||
GUILayout.Label("缺失标签", _boldStyle, GUILayout.Width(130));
|
||||
GUILayout.Label("多余规则标签", _boldStyle, GUILayout.Width(110));
|
||||
GUILayout.Label("自定义标签", _boldStyle, GUILayout.Width(110));
|
||||
GUILayout.Label("状态", _boldStyle, GUILayout.Width(80));
|
||||
}
|
||||
|
||||
@@ -211,29 +221,35 @@ namespace BaseGames.Editor
|
||||
|
||||
// 当前分组
|
||||
var grpColor = r.GroupOk ? ColOk : ColError;
|
||||
DrawColoredLabel(r.CurrentGroup ?? "—", grpColor, GUILayout.Width(130));
|
||||
DrawColoredLabel(r.CurrentGroup ?? "—", grpColor, GUILayout.Width(120));
|
||||
|
||||
// 期望分组
|
||||
var expGrpText = r.ExpectedGroup ?? "(规则未覆盖)";
|
||||
var expGrpColor = r.GroupOk ? ColOk : ColWarn;
|
||||
DrawColoredLabel(expGrpText, expGrpColor, GUILayout.Width(130));
|
||||
DrawColoredLabel(expGrpText, expGrpColor, GUILayout.Width(120));
|
||||
|
||||
// 缺失标签
|
||||
// 缺失标签(红色,须补齐)
|
||||
var missingText = r.MissingLabels.Length > 0 ? string.Join(", ", r.MissingLabels) : "—";
|
||||
DrawColoredLabel(missingText, r.MissingLabels.Length > 0 ? ColError : ColOk, GUILayout.Width(140));
|
||||
DrawColoredLabel(missingText, r.MissingLabels.Length > 0 ? ColError : ColOk, GUILayout.Width(130));
|
||||
|
||||
// 多余标签
|
||||
// 多余规则标签(红色,将被 FixEntry 移除)
|
||||
var extraText = r.ExtraLabels.Length > 0 ? string.Join(", ", r.ExtraLabels) : "—";
|
||||
DrawColoredLabel(extraText, r.ExtraLabels.Length > 0 ? ColWarn : ColOk, GUILayout.Width(120));
|
||||
DrawColoredLabel(extraText, r.ExtraLabels.Length > 0 ? ColError : ColOk, GUILayout.Width(110));
|
||||
|
||||
// 自定义标签(黄色警告,不会被自动删除,建议写入规范)
|
||||
var unknownText = r.UnknownLabels.Length > 0 ? string.Join(", ", r.UnknownLabels) : "—";
|
||||
DrawColoredLabel(unknownText, r.UnknownLabels.Length > 0 ? ColWarn : ColOk, GUILayout.Width(110));
|
||||
|
||||
// 状态 + 单条修复按钮
|
||||
if (r.IsOk)
|
||||
{
|
||||
DrawColoredLabel("✅ 正常", ColOk, GUILayout.Width(80));
|
||||
var statusColor = r.HasWarnings ? ColWarn : ColOk;
|
||||
var statusText = r.HasWarnings ? "⚠ 自定义标签" : "✅ 正常";
|
||||
DrawColoredLabel(statusText, statusColor, GUILayout.Width(80));
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawColoredLabel("⚠ 需修复", ColWarn, GUILayout.Width(60));
|
||||
DrawColoredLabel("❌ 需修复", ColError, GUILayout.Width(60));
|
||||
if (GUILayout.Button("修复", EditorStyles.miniButton, GUILayout.Width(40)))
|
||||
FixEntry(r);
|
||||
}
|
||||
@@ -247,7 +263,8 @@ namespace BaseGames.Editor
|
||||
EditorGUILayout.Space(4);
|
||||
EditorGUILayout.HelpBox(
|
||||
"规则来源:Docs/Standards/AddressablesLabelSpec.md §3 分组规则:AssetFolderSpec.md §8.1\n" +
|
||||
"「修复所有问题」仅修改已注册资产的分组/标签,不注册新资产(请用 Addressable Batch Tool)。",
|
||||
"「修复所有问题」仅修改已注册资产的分组/标签,不注册新资产,不删除自定义标签(黄色警告项)。\n" +
|
||||
"新增资产工作流:① Addressable Batch Tool → ⚡ 全量扫描 _Game/ → 注册所有 ② 返回此窗口 → 扫描 → 修复所有问题",
|
||||
MessageType.None);
|
||||
}
|
||||
|
||||
@@ -259,9 +276,6 @@ namespace BaseGames.Editor
|
||||
var settings = AddressableAssetSettingsDefaultObject.Settings;
|
||||
if (settings == null) return;
|
||||
|
||||
// 收集所有全局标签供"多余标签"判断
|
||||
var allKnownLabels = new HashSet<string>(settings.GetLabels(), StringComparer.Ordinal);
|
||||
|
||||
foreach (var group in settings.groups)
|
||||
{
|
||||
if (group == null) continue;
|
||||
@@ -275,7 +289,13 @@ namespace BaseGames.Editor
|
||||
var currentLbls = entry.labels.ToArray();
|
||||
|
||||
var missing = expectedLbls.Except(currentLbls, StringComparer.Ordinal).ToArray();
|
||||
var extra = currentLbls.Except(expectedLbls, StringComparer.Ordinal).ToArray();
|
||||
|
||||
// 区分两类"多余标签":
|
||||
// extra = 规则已知标签(KnownLabels)中规则不要求的 → 红色,FixEntry 会移除
|
||||
// unknown = 不在 KnownLabels 中的自定义标签 → 黄色警告,FixEntry 保留,建议写入规范
|
||||
var notExpected = currentLbls.Except(expectedLbls, StringComparer.Ordinal);
|
||||
var extra = notExpected.Where(l => AddressableRules.KnownLabels.Contains(l)).ToArray();
|
||||
var unknown = notExpected.Where(l => !AddressableRules.KnownLabels.Contains(l)).ToArray();
|
||||
|
||||
_reports.Add(new EntryReport
|
||||
{
|
||||
@@ -287,20 +307,24 @@ namespace BaseGames.Editor
|
||||
ExpectedLabels = expectedLbls,
|
||||
MissingLabels = missing,
|
||||
ExtraLabels = extra,
|
||||
UnknownLabels = unknown,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 问题项排前面,正常项排后面;同类按 Address 字母序
|
||||
// 问题项排前面,仅有警告的次之,正常项排最后;同类按 Address 字母序
|
||||
_reports = _reports
|
||||
.OrderBy(r => r.IsOk)
|
||||
.OrderBy(r => r.IsOk ? (r.HasWarnings ? 1 : 2) : 0)
|
||||
.ThenBy(r => r.Address, StringComparer.Ordinal)
|
||||
.ToList();
|
||||
|
||||
_scanned = true;
|
||||
Repaint();
|
||||
|
||||
int issues = _reports.Count(r => !r.IsOk);
|
||||
int warnings = _reports.Count(r => r.IsOk && r.HasWarnings);
|
||||
Debug.Log($"[AddressableRuleSync] 扫描完成:{_reports.Count} 个条目," +
|
||||
$"{_reports.Count(r => !r.IsOk)} 个需要修复。");
|
||||
$"{issues} 个需要修复,{warnings} 个含自定义标签警告。");
|
||||
}
|
||||
|
||||
// ── 修复逻辑 ──────────────────────────────────────────────────────────
|
||||
@@ -310,6 +334,22 @@ namespace BaseGames.Editor
|
||||
var issues = _reports.Where(r => !r.IsOk).ToList();
|
||||
if (issues.Count == 0) return;
|
||||
|
||||
int moveCount = issues.Count(r => !r.GroupOk);
|
||||
int addCount = issues.Sum(r => r.MissingLabels.Length);
|
||||
int removeCount = issues.Sum(r => r.ExtraLabels.Length);
|
||||
|
||||
// 干跑预览对话框
|
||||
bool confirmed = EditorUtility.DisplayDialog(
|
||||
"确认修复所有问题",
|
||||
$"将对 {issues.Count} 个条目执行以下操作:\n\n" +
|
||||
$" • 移动分组:{moveCount} 个\n" +
|
||||
$" • 添加标签:{addCount} 个\n" +
|
||||
$" • 移除多余规则标签:{removeCount} 个\n\n" +
|
||||
"⚠ 自定义标签(黄色警告项)不会被删除。\n" +
|
||||
"此操作不可撤销,请确认后继续。",
|
||||
"确认修复", "取消");
|
||||
if (!confirmed) return;
|
||||
|
||||
int fixedCount = 0;
|
||||
foreach (var r in issues)
|
||||
{
|
||||
@@ -355,7 +395,8 @@ namespace BaseGames.Editor
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// 移除多余标签
|
||||
// 移除多余规则标签(ExtraLabels 只包含 KnownLabels 中规则不要求的标签;
|
||||
// UnknownLabels 是用户自定义标签,刻意保留,不做删除)
|
||||
foreach (var lbl in r.ExtraLabels)
|
||||
{
|
||||
entry.SetLabel(lbl, false, true);
|
||||
|
||||
@@ -14,23 +14,57 @@ namespace BaseGames.Editor
|
||||
/// </summary>
|
||||
public static class AddressableRules
|
||||
{
|
||||
// ── 已知标签白名单 ────────────────────────────────────────────────────────
|
||||
// AddressableRuleSyncWindow 用此白名单区分:
|
||||
// • 规则要求但缺失 → 红色,必须补齐
|
||||
// • 规则不要求但存在且不在白名单 → 黄色警告(自定义标签,建议写进规范)
|
||||
// • 规则不要求但存在且在白名单 → 白色,合法的人工附加标签
|
||||
// 每次向 AddressablesLabelSpec 新增 Label 时,须同步在此处添加。
|
||||
public static readonly HashSet<string> KnownLabels = new(StringComparer.Ordinal)
|
||||
{
|
||||
AddressKeys.Labels.Preload,
|
||||
AddressKeys.Labels.Poolable,
|
||||
AddressKeys.Labels.Enemy,
|
||||
AddressKeys.Labels.BGM,
|
||||
AddressKeys.Labels.SFX,
|
||||
AddressKeys.Labels.Charms,
|
||||
AddressKeys.Labels.Config,
|
||||
AddressKeys.Labels.Weapon,
|
||||
};
|
||||
|
||||
// ── 前缀 → 分组名 ──────────────────────────────────────────────────────
|
||||
// 规则:按 AssetFolderSpec §8.1 Group 划分策略。
|
||||
// 顺序:更长/更具体的前缀必须排在更短/更泛化的前缀之前,否则短前缀会先匹配。
|
||||
// 特殊:Room_/Boss_ 地址的分组名在运行时动态计算,见 GetExpectedGroup()。
|
||||
public static readonly (string Prefix, string Group)[] PrefixGroupMap =
|
||||
{
|
||||
// ── 场景 ─────────────────────────────────────────────────────────
|
||||
("Scene_", "Scenes"),
|
||||
// ── 玩家 & 武器 ──────────────────────────────────────────────────
|
||||
("PLY_", "Player"),
|
||||
("WPN_", "Player"), // 武器与玩家 Prefab 同组(AssetFolderSpec §8.1)
|
||||
// ── 敌人 & 投射物 ────────────────────────────────────────────────
|
||||
("ENM_", "Enemies"),
|
||||
("PROJ_", "Projectiles"),
|
||||
("VFX_", "VFX_Common"), // 通用特效组(AssetFolderSpec §8.1)
|
||||
// ── 特效 & UI ────────────────────────────────────────────────────
|
||||
("VFX_", "VFX_Common"), // 通用特效(AssetFolderSpec §8.1)
|
||||
("UI_", "UI"),
|
||||
// ── 收集物 ──────────────────────────────────────────────────────
|
||||
("COL_", "Collectibles"),
|
||||
("CHM_", "Config"), // 护身符 SO 归入 Config 组(AddressablesLabelSpec §3.9)
|
||||
("Config/", "Config"),
|
||||
("AUD_", "Audio_Music"),
|
||||
// ── 配置数据(更具体的前缀排在泛化前缀之前)──────────────────────
|
||||
("CHM_", "Config"), // 护身符 SO(AddressablesLabelSpec §3.9)
|
||||
("SKL_", "Config"), // 技能配置 SO(AssetFolderSpec §4)
|
||||
("SPL_", "Config"), // 法术配置 SO
|
||||
("ABL_", "Config"), // 能力配置 SO
|
||||
("MAP_", "Config"), // 地图数据 SO(AssetFolderSpec §4)
|
||||
("Config/", "Config"), // 路径前缀配置(AssetFolderSpec §8.2)
|
||||
// ── 音频(AUD_BGM_ / AUD_SFX_ 必须在通配 AUD_ 之前)─────────────
|
||||
("AUD_BGM_", "Audio_Music"), // BGM 流式音频
|
||||
("AUD_SFX_", "Audio_SFX"), // SFX 音效(独立分组便于包体按需加载)
|
||||
("AUD_", "Audio_Music"), // 未细分音频归 BGM 组
|
||||
// ── 世界 & 持久化 ────────────────────────────────────────────────
|
||||
("WLD_", "World"), // 可交互世界物件 Prefab
|
||||
("SYS_", "Persistent"), // Persistent 场景管理器 Prefab
|
||||
};
|
||||
|
||||
// ── 精确地址 → 标签(优先级高于前缀规则)────────────────────────────────
|
||||
@@ -51,19 +85,33 @@ namespace BaseGames.Editor
|
||||
// 顺序:更具体的前缀(AUD_BGM_)在更泛化的前缀(AUD_)之前。
|
||||
private static readonly (string Prefix, string[] Labels)[] PrefixLabelMap =
|
||||
{
|
||||
// ── 音频(更具体的 AUD_BGM_ / AUD_SFX_ 必须排在 AUD_ 之前)──────
|
||||
("AUD_BGM_", new[] { AddressKeys.Labels.BGM }),
|
||||
("AUD_SFX_", new[] { AddressKeys.Labels.SFX }),
|
||||
("AUD_", new[] { AddressKeys.Labels.BGM }), // 未细分音频默认归 BGM
|
||||
("Scene_", Array.Empty<string>()), // 除 MainMenu 外场景无 label
|
||||
// ── 场景(除 MainMenu 外无 label,由 ExactLabelMap 特殊处理)──────
|
||||
("Scene_", Array.Empty<string>()),
|
||||
// ── 玩家 & 武器 ──────────────────────────────────────────────────
|
||||
("PLY_", new[] { AddressKeys.Labels.Preload }),
|
||||
("WPN_", new[] { AddressKeys.Labels.Weapon, AddressKeys.Labels.Preload }),
|
||||
// ── 敌人 & 投射物 ────────────────────────────────────────────────
|
||||
("ENM_", new[] { AddressKeys.Labels.Enemy }),
|
||||
("PROJ_", new[] { AddressKeys.Labels.Poolable, AddressKeys.Labels.Preload }),
|
||||
// ── 特效 & UI ────────────────────────────────────────────────────
|
||||
("VFX_", new[] { AddressKeys.Labels.Poolable, AddressKeys.Labels.Preload }),
|
||||
("UI_", Array.Empty<string>()), // 除 FloatingDamageText 外 UI 无默认 label
|
||||
// ── 收集物 ──────────────────────────────────────────────────────
|
||||
("COL_", new[] { AddressKeys.Labels.Poolable, AddressKeys.Labels.Preload }),
|
||||
// ── 配置数据 ─────────────────────────────────────────────────────
|
||||
("CHM_", new[] { AddressKeys.Labels.Charms }),
|
||||
("MAP_", new[] { AddressKeys.Labels.Config }), // 地图数据 SO 为动态加载配置
|
||||
("Config/", new[] { AddressKeys.Labels.Config }),
|
||||
// ── 技能 / 法术 / 能力 / 世界物件 / 持久化:无批量加载需求,不加 Label ──
|
||||
("SKL_", Array.Empty<string>()),
|
||||
("SPL_", Array.Empty<string>()),
|
||||
("ABL_", Array.Empty<string>()),
|
||||
("WLD_", Array.Empty<string>()),
|
||||
("SYS_", Array.Empty<string>()),
|
||||
};
|
||||
|
||||
// ── 公开 API ───────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user