using System.Collections.Generic; using System.Linq; using UnityEditor; using UnityEditor.AddressableAssets; using UnityEditor.AddressableAssets.Settings; using UnityEditor.AddressableAssets.Settings.GroupSchemas; using UnityEngine; namespace BaseGames.Editor { /// /// 可复用的 Addressable 注册器:把资产登记到正确的分组并按规范打标。 /// /// 分组与标签均由 推导(规范来源: /// Docs/Standards/AddressablesLabelSpec.md §3 / AssetFolderSpec.md §8), /// 因此结果与 AddressableManagerWindowAddressKeyValidator 完全一致。 /// /// 供各脚手架(如 SceneObjectPlacerTool 创建 Prefab 后)一键注册, /// 避免每个工具重复实现 Addressables Settings API。 /// public static class AddressableRegistrar { /// /// 将指定路径的资产注册为 Addressable。 /// 地址默认取文件名(可用 覆盖); /// 分组与标签按 推导, /// 在规范标签之上追加(如 E003 幼蛭额外的 Poolable/Preload)。 /// /// 最终写入的地址;Addressables 未初始化或资产不存在时返回 null。 public static string Register(string assetPath, string addressOverride = null, IEnumerable extraLabels = null, List report = null) { var settings = AddressableAssetSettingsDefaultObject.Settings; if (settings == null) { report?.Add("Addressable Asset Settings 不存在(未初始化),已跳过 Addressable 注册。"); return null; } string guid = AssetDatabase.AssetPathToGUID(assetPath); if (string.IsNullOrEmpty(guid)) { report?.Add($"资产不存在,无法注册 Addressable:{assetPath}"); return null; } string address = string.IsNullOrEmpty(addressOverride) ? System.IO.Path.GetFileNameWithoutExtension(assetPath) : addressOverride; string groupName = AddressableRules.GetExpectedGroup(address); var group = groupName != null ? EnsureGroup(settings, groupName) : settings.DefaultGroup; var entry = settings.FindAssetEntry(guid) ?? settings.CreateOrMoveEntry(guid, group, false, false); if (entry == null) { report?.Add($"创建 Addressable 条目失败:{assetPath}"); return null; } entry.address = address; settings.MoveEntry(entry, group, false, false); var labels = new HashSet(AddressableRules.GetExpectedLabels(address)); if (extraLabels != null) foreach (var l in extraLabels) if (!string.IsNullOrWhiteSpace(l)) labels.Add(l); foreach (var l in labels) SetLabel(settings, entry, l); settings.SetDirty(AddressableAssetSettings.ModificationEvent.EntryModified, entry, true); report?.Add($"✅ Addressable 注册:{address} → 分组 {group.name}" + (labels.Count > 0 ? $",标签 [{string.Join(", ", labels.OrderBy(x => x))}]" : ",无标签")); return address; } private static AddressableAssetGroup EnsureGroup(AddressableAssetSettings settings, string name) { var existing = settings.groups.FirstOrDefault(g => g != null && g.name == name); if (existing != null) return existing; var tmpl = settings.GroupTemplateObjects.FirstOrDefault() as AddressableAssetGroupTemplate; var schemas = tmpl != null ? new List(tmpl.SchemaObjects) : null; var created = settings.CreateGroup(name, false, false, true, schemas); if (created != null) Debug.Log($"[AddressableRegistrar] 已自动创建分组:{name}"); return created ?? settings.DefaultGroup; } private static void SetLabel(AddressableAssetSettings settings, AddressableAssetEntry entry, string label) { if (!settings.GetLabels().Contains(label)) { settings.AddLabel(label, true); Debug.Log($"[AddressableRegistrar] 已创建标签:{label}"); } entry.SetLabel(label, true, true); } } }