using System.Text; using UnityEditor; using UnityEditor.AddressableAssets; using UnityEditor.AddressableAssets.Settings; using UnityEditor.AddressableAssets.Settings.GroupSchemas; using UnityEngine; /// /// AA 依赖链修复迁移工具(一次性)。 /// /// 【背景】Addressables 加载 bundle 时会递归加载其依赖 bundle,而构建报告显示存在 /// 跨玩法依赖链,导致"未登录就加载鱼包 / 进 A 玩法拉 B 玩法鱼包": /// defaultlocalgroup → fish_xjmh → fish_hhwk (启动链:默认音频一碰 D LG 全链拖入) /// fish_flzy → fish_hhwk (进 FLZY 房拉 HHWK 鱼包) /// gameuires → fish_xjmh (进任何房间拉 XJMH 鱼包) /// /// 【修复内容】(对应 2026-10-08 16:41 构建报告分析出的确切依赖边) /// 1. PW_Enemy_4000.prefab(XJMH Boss,误在 D LG)→ Fish_XJMH 组 断 D LG→fish_xjmh /// 2. XJMHCreateFish.asset(XJMH 配置,误在 D LG)→ Fish_XJMH 组 /// 3. 新建 SharedRes 组:Game177_baigujing.png(HHWK 图鉴贴图被 XJMH 小丑扑克引用)、 /// fish_4003 Variant.png(HHWK 图鉴贴图被 FLZY 闪电引用)→ SharedRes /// 断 fish_xjmh→fish_hhwk、fish_flzy→fish_hhwk(两边都只依赖小共享包) /// 4. GameMainPanelXJMH 引用的 7 个 XJMH 资源(Boss出场特效/帮助Item/技能sprite) /// → GameUIRes 组 断 gameuires→fish_xjmh /// (若鱼包资产也引用它们,产生 fish_xjmh→gameuires 边——进房必载面板,无害) /// /// 【使用】Unity 菜单 Tools → AA依赖修复 → 执行修复并重建Addressables, /// 完成后用 AABundleMonitor 悬浮窗验证:启动仅 4 个包、进房只加当前玩法鱼包。 /// public static class AAFixDependencyMigration { [MenuItem("Tools/AA依赖修复/执行修复并重建Addressables")] public static void Run() { if (!EditorUtility.DisplayDialog("AA依赖修复", "将执行以下迁移(断掉跨玩法 bundle 依赖链):\n\n" + "1. PW_Enemy_4000 / XJMHCreateFish → Fish_XJMH 组\n" + "2. 新建 SharedRes 组,移入 2 张被跨玩法引用的贴图\n" + "3. GameMainPanelXJMH 引用的 7 个资源 → GameUIRes 组\n\n" + "迁移完成后可选择立即重建 Addressables。\n继续?", "执行", "取消")) return; var settings = AddressableAssetSettingsDefaultObject.Settings; if (settings == null) { Debug.LogError("[AA依赖修复] 找不到 AddressableAssetSettings"); return; } var log = new StringBuilder(); // ---------- ① D LG 里误放的 XJMH 资产 → Fish_XJMH(断 D LG→fish_xjmh 启动链) ---------- var fishXjmh = settings.FindGroup("Fish_XJMH"); if (fishXjmh == null) { Debug.LogError("[AA依赖修复] 找不到 Fish_XJMH 组"); return; } MoveWithLabels(settings, fishXjmh, "Assets/MyGame/aa/Game/XJMH/Boss/XingJiXunHang/PW_Enemy_4000.prefab", new[] { "FishPrefab", "XJMH" }, log); MoveWithLabels(settings, fishXjmh, "Assets/MyGame/aa/UI/Game/XJMH/XJMHCreateFish.asset", new[] { "XJMH" }, log); // ---------- ② 新建 SharedRes 组,移入被跨玩法引用的贴图(断 xjmh/flzy→hhwk) ---------- var shared = settings.FindGroup("SharedRes"); if (shared == null) { shared = CreateGroupLike(settings, "SharedRes", "Fish_XJMH"); log.AppendLine("[组] 已创建 SharedRes(打包配置复制自 Fish_XJMH)"); } // 保留原 TuJian/HHWK 标签:HHWK 玩法的图鉴 Sprite 交集加载(TuJian+HHWK)仍能命中, // 只是从 SharedRes 包里取;XJMH/FLZY 对它们的引用改为依赖小共享包 MoveKeepLabels(settings, shared, "Assets/Art/HHWK/res/texture/dynamic/Game177_baigujing.png", log); MoveKeepLabels(settings, shared, "Assets/Art/HHWK/TuJianTexture/fish_4003 Variant.png", log); // ---------- ③ GameMainPanelXJMH 引用的资源 → GameUIRes(断 gameuires→fish_xjmh) ---------- var gameUi = settings.FindGroup("GameUIRes"); if (gameUi == null) { Debug.LogError("[AA依赖修复] 找不到 GameUIRes 组"); return; } string[] guiTargets = { "Assets/Art/XJMH/pw/pw_prefab/game/effect/PW_Effect_Boss_Appear.prefab", "Assets/Art/XJMH/pw/pw_prefab/item/PW_HelpItem1.prefab", "Assets/Art/XJMH/pw/pw_prefab/item/PW_HelpItem2.prefab", "Assets/Art/XJMH/pw/pw_prefab/item/PW_SetLockItem.prefab", "Assets/Art/XJMH/pw/pw_sprite/common/zd_jn_sd.png", "Assets/Art/XJMH/pw/pw_sprite/common/zd_jn_ss.png", "Assets/Art/XJMH/pw/pw_sprite/common/zd_jn_zd.png", }; foreach (var t in guiTargets) MoveKeepLabels(settings, gameUi, t, log); AssetDatabase.SaveAssets(); Debug.Log("[AA依赖修复] 迁移完成:\n" + log); // ---------- ④ 重建 ---------- if (EditorUtility.DisplayDialog("AA依赖修复", "迁移完成:\n" + log + "\n现在重建 Addressables?(约几分钟)", "立即重建", "稍后手动重建")) { Debug.Log("[AA依赖修复] 开始重建 Addressables..."); AddressableAssetSettings.BuildPlayerContent(out var result); if (result == null || !string.IsNullOrEmpty(result.Error)) { Debug.LogError("[AA依赖修复] 构建失败: " + (result != null ? result.Error : "无返回结果")); EditorUtility.DisplayDialog("AA依赖修复", "构建失败: " + (result != null ? result.Error : "无返回结果"), "确定"); } else { Debug.Log("[AA依赖修复] 构建完成"); EditorUtility.DisplayDialog("AA依赖修复", "构建完成!\n\n验证步骤:\n" + "1. Play 启动到登录页 → 悬浮窗应只有 4 个包(builtinshaders/LoginUIRes/DefaultLocalGroup/Turret)\n" + "2. 进任一玩法房间 → 只新增该玩法的鱼包 + gameuires", "确定"); } } } /// 移动资产到目标组并覆盖标签 private static void MoveWithLabels(AddressableAssetSettings settings, AddressableAssetGroup target, string path, string[] labels, StringBuilder log) { string guid = AssetDatabase.AssetPathToGUID(path); if (string.IsNullOrEmpty(guid)) { log.AppendLine("[跳过] 找不到资产: " + path); return; } var entry = settings.CreateOrMoveEntry(guid, target); if (entry == null) { log.AppendLine("[跳过] 移动失败: " + path); return; } entry.labels.Clear(); foreach (var l in labels) entry.labels.Add(l); log.AppendLine($"[移动] {path}\n → {target.name}(标签: {string.Join(",", labels)})"); } /// 移动资产到目标组,保留原有标签 private static void MoveKeepLabels(AddressableAssetSettings settings, AddressableAssetGroup target, string path, StringBuilder log) { string guid = AssetDatabase.AssetPathToGUID(path); if (string.IsNullOrEmpty(guid)) { log.AppendLine("[跳过] 找不到资产: " + path); return; } var entry = settings.CreateOrMoveEntry(guid, target); if (entry == null) { log.AppendLine("[跳过] 移动失败: " + path); return; } log.AppendLine($"[移动] {path}\n → {target.name}(保留标签: {string.Join(",", entry.labels)})"); } /// 按源组的打包配置(BundledAssetGroupSchema/ContentUpdateGroupSchema)创建新组。 /// AddSchema(schema) 内部会复制 schema 并正确挂到新组上,无需手动 CopySerialized。 private static AddressableAssetGroup CreateGroupLike(AddressableAssetSettings settings, string newName, string srcGroupName) { var src = settings.FindGroup(srcGroupName); var group = settings.CreateGroup(newName, false, false, false, null); var srcBundled = src.GetSchema(); if (srcBundled != null) group.AddSchema(srcBundled, false); var srcContent = src.GetSchema(); if (srcContent != null) group.AddSchema(srcContent, false); return group; } }