From 4a8c66dcafac6950ec7e9ed662fa4fa4187224ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A3=AE2003?= <2384017988@qq.com> Date: Thu, 8 Oct 2026 17:04:24 +0800 Subject: [PATCH] 1 --- .../AddressableAssetSettings.asset | 2 +- .../AssetGroups/Default Local Group.asset | 56 ++ .../AssetGroups/Fish_FLZY.asset | 14 - .../AssetGroups/Fish_HHWK.asset | 21 - .../AssetGroups/Fish_XJMH.asset | 97 ++-- .../Windows/addressables_content_state.bin | Bin 4130 -> 4130 bytes Assets/MyGame/Scenes/UIScene.unity | 15 +- Assets/MyGame/Scripts/Debug.meta | 8 + .../MyGame/Scripts/Debug/AABundleMonitor.cs | 513 ++++++++++++++++++ .../Scripts/Debug/AABundleMonitor.cs.meta | 11 + Assets/MyGame/Scripts/Turret/SitPosData.cs | 20 +- .../fishResources/AudioResourcesData.cs | 71 +-- .../fishResources/FishResourcesData.cs | 104 ++-- .../MyGame/aa/Game/FLZY/BOSS/LTZZ/4052.prefab | 2 +- .../MyGame/aa/Game/FLZY/BOSS/SLZP/1507.prefab | 5 + .../MyGame/aa/Game/FLZY/BOSS/SSQL/7005.prefab | 2 +- .../XJMH/Boss/XingYunFeiDie/RotatePan_XJMH.cs | 3 +- .../aa/UI/DefaultPanel/592228afbce16145.asset | 108 ++++ .../DefaultPanel/592228afbce16145.asset.meta | 8 + .../aa/UI/DefaultPanel/592228afbce16145.png | Bin 0 -> 9056 bytes .../UI/DefaultPanel/592228afbce16145.png.meta | 153 ++++++ .../DefaultPanel/BossBoardCarstGongGao.prefab | 2 +- .../aa/UI/Game/GameMainPanel_FishBase.cs | 105 +++- .../aa/UI/Home/Sub/UIHome_BY_BackPackPanel.cs | 40 +- 24 files changed, 1132 insertions(+), 228 deletions(-) create mode 100644 Assets/MyGame/Scripts/Debug.meta create mode 100644 Assets/MyGame/Scripts/Debug/AABundleMonitor.cs create mode 100644 Assets/MyGame/Scripts/Debug/AABundleMonitor.cs.meta create mode 100644 Assets/MyGame/aa/UI/DefaultPanel/592228afbce16145.asset create mode 100644 Assets/MyGame/aa/UI/DefaultPanel/592228afbce16145.asset.meta create mode 100644 Assets/MyGame/aa/UI/DefaultPanel/592228afbce16145.png create mode 100644 Assets/MyGame/aa/UI/DefaultPanel/592228afbce16145.png.meta diff --git a/Assets/AddressableAssetsData/AddressableAssetSettings.asset b/Assets/AddressableAssetsData/AddressableAssetSettings.asset index bfecbaea5..bbd70078d 100644 --- a/Assets/AddressableAssetsData/AddressableAssetSettings.asset +++ b/Assets/AddressableAssetsData/AddressableAssetSettings.asset @@ -15,7 +15,7 @@ MonoBehaviour: m_DefaultGroup: a263e71fce79f2f4cb5b5f1fa6b7291a m_currentHash: serializedVersion: 2 - Hash: e88239bd385df7eeb8432db63ac72d7d + Hash: 3b2ddf0423273ab9d68b887ff1cc1809 m_OptimizeCatalogSize: 0 m_BuildRemoteCatalog: 0 m_BundleLocalCatalog: 0 diff --git a/Assets/AddressableAssetsData/AssetGroups/Default Local Group.asset b/Assets/AddressableAssetsData/AssetGroups/Default Local Group.asset index d46ee1e08..7c6935f7e 100644 --- a/Assets/AddressableAssetsData/AssetGroups/Default Local Group.asset +++ b/Assets/AddressableAssetsData/AssetGroups/Default Local Group.asset @@ -51,6 +51,13 @@ MonoBehaviour: - PTCBZS - ZSYX FlaggedDuringContentUpdateRestriction: 0 + - m_GUID: 4ccd2be5159a8434689185dad378dd01 + m_Address: Assets/Art/FengLei/Audio/FLZY_bgm_default.ogg + m_ReadOnly: 0 + m_SerializedLabels: + - Audio + - Default + FlaggedDuringContentUpdateRestriction: 0 - m_GUID: 55692611ed57c594fa0d7ecba7dcf704 m_Address: Assets/MyGame/aa/Game/ZhanShen/Game/display_wargod_14901_0_dev.prefab m_ReadOnly: 0 @@ -70,6 +77,41 @@ MonoBehaviour: m_ReadOnly: 0 m_SerializedLabels: [] FlaggedDuringContentUpdateRestriction: 0 + - m_GUID: 8e07162e07c25974cae59b29d808aaf2 + m_Address: Assets/MyGame/aa/Game/XJMH/Sound/PW_BgFree.ogg + m_ReadOnly: 0 + m_SerializedLabels: + - Audio + - Default + FlaggedDuringContentUpdateRestriction: 0 + - m_GUID: 964dfa5d6b6f9b947983721026622299 + m_Address: Assets/MyGame/aa/Game/XJMH/Sound/PW_Shoot.ogg + m_ReadOnly: 0 + m_SerializedLabels: + - Audio + - PTCBZS + FlaggedDuringContentUpdateRestriction: 0 + - m_GUID: 98b1baf987b5c8942baa65ac95c04a15 + m_Address: Assets/Audios/HHWK/HHWK_ziyouxiBGM.ogg + m_ReadOnly: 0 + m_SerializedLabels: + - Audio + - Default + FlaggedDuringContentUpdateRestriction: 0 + - m_GUID: a5531410c3acf2a45990658c82c7db06 + m_Address: Assets/Art/FengLei/Audio/FLZY_bgm_20.ogg + m_ReadOnly: 0 + m_SerializedLabels: + - Audio + - Default + FlaggedDuringContentUpdateRestriction: 0 + - m_GUID: b9f66d4b543101b4a8f6529f7e1c3fc8 + m_Address: Assets/Audios/HHWK/HHWK_youxiBGM2.ogg + m_ReadOnly: 0 + m_SerializedLabels: + - Audio + - Default + FlaggedDuringContentUpdateRestriction: 0 - m_GUID: c0665105ad467074a9d951a074b6c71b m_Address: Assets/MyGame/aa/Game/XJMH/Boss/XingJiXunHang/PW_Enemy_4000.prefab m_ReadOnly: 0 @@ -77,6 +119,20 @@ MonoBehaviour: - FishPrefab - XJMH FlaggedDuringContentUpdateRestriction: 0 + - m_GUID: c37a24b1b9382e04c86ecae674a7e071 + m_Address: Assets/MyGame/aa/Game/XJMH/Sound/PW_Bg.ogg + m_ReadOnly: 0 + m_SerializedLabels: + - Audio + - Default + FlaggedDuringContentUpdateRestriction: 0 + - m_GUID: ec58d755545af58489ae35ed82c1e07d + m_Address: Assets/Audios/HHWK/HHWK_youxiBGM.ogg + m_ReadOnly: 0 + m_SerializedLabels: + - Audio + - Default + FlaggedDuringContentUpdateRestriction: 0 - m_GUID: fd77a1da22db21441ab0b116128a5e3a m_Address: Assets/MyGame/aa/Game/ZhanShen/Game/display_wargod_in_14053_0_dev.prefab m_ReadOnly: 0 diff --git a/Assets/AddressableAssetsData/AssetGroups/Fish_FLZY.asset b/Assets/AddressableAssetsData/AssetGroups/Fish_FLZY.asset index 2c72de5b6..37b881152 100644 --- a/Assets/AddressableAssetsData/AssetGroups/Fish_FLZY.asset +++ b/Assets/AddressableAssetsData/AssetGroups/Fish_FLZY.asset @@ -206,13 +206,6 @@ MonoBehaviour: - FLZY - FishPrefab FlaggedDuringContentUpdateRestriction: 0 - - m_GUID: 4ccd2be5159a8434689185dad378dd01 - m_Address: Assets/Art/FengLei/Audio/FLZY_bgm_default.ogg - m_ReadOnly: 0 - m_SerializedLabels: - - Audio - - Default - FlaggedDuringContentUpdateRestriction: 0 - m_GUID: 4f07750ceee651741ba0228ffd45e954 m_Address: Assets/Art/FengLei/bundles/by_fish_base/bundles/prefab/fishbase@/12.prefab m_ReadOnly: 0 @@ -528,13 +521,6 @@ MonoBehaviour: - FLZY - FishPrefab FlaggedDuringContentUpdateRestriction: 0 - - m_GUID: a5531410c3acf2a45990658c82c7db06 - m_Address: Assets/Art/FengLei/Audio/FLZY_bgm_20.ogg - m_ReadOnly: 0 - m_SerializedLabels: - - Audio - - Default - FlaggedDuringContentUpdateRestriction: 0 - m_GUID: a718714f431d35247b001c93ad1683b9 m_Address: Assets/Art/TuJianFishTexture/1595.png m_ReadOnly: 0 diff --git a/Assets/AddressableAssetsData/AssetGroups/Fish_HHWK.asset b/Assets/AddressableAssetsData/AssetGroups/Fish_HHWK.asset index 0eb69dd09..7f0265d5d 100644 --- a/Assets/AddressableAssetsData/AssetGroups/Fish_HHWK.asset +++ b/Assets/AddressableAssetsData/AssetGroups/Fish_HHWK.asset @@ -1200,13 +1200,6 @@ MonoBehaviour: - FishPrefab - HHWK FlaggedDuringContentUpdateRestriction: 0 - - m_GUID: 98b1baf987b5c8942baa65ac95c04a15 - m_Address: Assets/Audios/HHWK/HHWK_ziyouxiBGM.ogg - m_ReadOnly: 0 - m_SerializedLabels: - - Audio - - Default - FlaggedDuringContentUpdateRestriction: 0 - m_GUID: 9a71ec700d97e9442be4e7ca1ec88b88 m_Address: Assets/Art/HHWK/TuJianTexture/fish_1022 Variant.png m_ReadOnly: 0 @@ -1501,13 +1494,6 @@ MonoBehaviour: - FishPrefab - HHWK FlaggedDuringContentUpdateRestriction: 0 - - m_GUID: b9f66d4b543101b4a8f6529f7e1c3fc8 - m_Address: Assets/Audios/HHWK/HHWK_youxiBGM2.ogg - m_ReadOnly: 0 - m_SerializedLabels: - - Audio - - Default - FlaggedDuringContentUpdateRestriction: 0 - m_GUID: bbddd2883bdfd1644ba49d4261a73d27 m_Address: Assets/game_download/games/game177/prefab/fish/Game177_yilong_sj_2.prefab m_ReadOnly: 0 @@ -1963,13 +1949,6 @@ MonoBehaviour: - FishPrefab - HHWK FlaggedDuringContentUpdateRestriction: 0 - - m_GUID: ec58d755545af58489ae35ed82c1e07d - m_Address: Assets/Audios/HHWK/HHWK_youxiBGM.ogg - m_ReadOnly: 0 - m_SerializedLabels: - - Audio - - Default - FlaggedDuringContentUpdateRestriction: 0 - m_GUID: ec8b574c01d5d354db2c62b9737d7c49 m_Address: Assets/Art/HHWK/TuJianTexture/Fish_2009 Variant.png m_ReadOnly: 0 diff --git a/Assets/AddressableAssetsData/AssetGroups/Fish_XJMH.asset b/Assets/AddressableAssetsData/AssetGroups/Fish_XJMH.asset index 47b9c2b6c..c16dcc58a 100644 --- a/Assets/AddressableAssetsData/AssetGroups/Fish_XJMH.asset +++ b/Assets/AddressableAssetsData/AssetGroups/Fish_XJMH.asset @@ -178,6 +178,12 @@ MonoBehaviour: - FishPrefab - XJMH FlaggedDuringContentUpdateRestriction: 0 + - m_GUID: 1abfe2b699a5f764c896951d8bc859ad + m_Address: Assets/Art/XJMH/pw/pw_sprite/freemodel/PW_FreeItem_1.png + m_ReadOnly: 0 + m_SerializedLabels: + - XJMH + FlaggedDuringContentUpdateRestriction: 0 - m_GUID: 1c991808cacc37a4884f95a3bd88a29c m_Address: Assets/MyGame/aa/Game/XJMH/Sound/PW_FlyCoin.ogg m_ReadOnly: 0 @@ -423,6 +429,12 @@ MonoBehaviour: - TuJian - XJMH FlaggedDuringContentUpdateRestriction: 0 + - m_GUID: 3b6ce020d078b524ab9f8ea29d7fe431 + m_Address: Assets/Art/XJMH/pw/pw_sprite/common/zd_jn_zd.png + m_ReadOnly: 0 + m_SerializedLabels: + - XJMH + FlaggedDuringContentUpdateRestriction: 0 - m_GUID: 3c361fb6838b59a40b183bc1dbf9ae2a m_Address: Assets/Art/XJMH/pw/pw_prefab/game/enemy/XJMHEnemy/PW_Enemy_2.prefab m_ReadOnly: 0 @@ -801,6 +813,12 @@ MonoBehaviour: - Audio - XJMH FlaggedDuringContentUpdateRestriction: 0 + - m_GUID: 689bc9268484c3c4fab1743b9c99ee9e + m_Address: Assets/Art/XJMH/pw/pw_sprite/freemodel/PW_FreeItem_2.png + m_ReadOnly: 0 + m_SerializedLabels: + - XJMH + FlaggedDuringContentUpdateRestriction: 0 - m_GUID: 68e9db06ce8a52043bce8e20dd84fb7e m_Address: Assets/Art/XJMH/pw/pw_prefab/game/enemy/XJMHEnemy/PW_Enemy_9.prefab m_ReadOnly: 0 @@ -983,13 +1001,6 @@ MonoBehaviour: - Audio - XJMH FlaggedDuringContentUpdateRestriction: 0 - - m_GUID: 8e07162e07c25974cae59b29d808aaf2 - m_Address: Assets/MyGame/aa/Game/XJMH/Sound/PW_BgFree.ogg - m_ReadOnly: 0 - m_SerializedLabels: - - Audio - - Default - FlaggedDuringContentUpdateRestriction: 0 - m_GUID: 8e9f22d048bac0a41a86b7a8f89efb0f m_Address: Assets/MyGame/aa/Game/XJMH/Boss/XiaoChouPuKe/PW_Enemy_113.prefab m_ReadOnly: 0 @@ -1032,11 +1043,10 @@ MonoBehaviour: - TuJian - XJMH FlaggedDuringContentUpdateRestriction: 0 - - m_GUID: 964dfa5d6b6f9b947983721026622299 - m_Address: Assets/MyGame/aa/Game/XJMH/Sound/PW_Shoot.ogg + - m_GUID: 9bf752fc3d63b374998b79d5b9e169a0 + m_Address: Assets/Art/XJMH/pw/pw_sprite/freemodel/PW_FreeItem_3.png m_ReadOnly: 0 m_SerializedLabels: - - Audio - XJMH FlaggedDuringContentUpdateRestriction: 0 - m_GUID: 9cd48272e3e176b4b86b3ce703cfa03b @@ -1128,6 +1138,12 @@ MonoBehaviour: - TuJian - XJMH FlaggedDuringContentUpdateRestriction: 0 + - m_GUID: adf779a3413c62744b56021a0ad5c93d + m_Address: Assets/Art/XJMH/pw/pw_sprite/common/zd_jn_ss.png + m_ReadOnly: 0 + m_SerializedLabels: + - XJMH + FlaggedDuringContentUpdateRestriction: 0 - m_GUID: aea38e560a41ff44ba0a68d3b3194052 m_Address: Assets/MyGame/aa/Game/XJMH/Sound/PW_TextDragon.ogg m_ReadOnly: 0 @@ -1156,6 +1172,12 @@ MonoBehaviour: - Audio - XJMH FlaggedDuringContentUpdateRestriction: 0 + - m_GUID: b40ddb35d439d4546ab220424de7e930 + m_Address: Assets/Art/XJMH/pw/pw_sprite/common/zd_jn_sd.png + m_ReadOnly: 0 + m_SerializedLabels: + - XJMH + FlaggedDuringContentUpdateRestriction: 0 - m_GUID: b494aa76324a7c84ba8d1b26c9b3d231 m_Address: Assets/Art/XJMH/pw/pw_sprite/handbook/PW_Enemy_32.png m_ReadOnly: 0 @@ -1247,13 +1269,6 @@ MonoBehaviour: - FishPrefab - XJMH FlaggedDuringContentUpdateRestriction: 0 - - m_GUID: c37a24b1b9382e04c86ecae674a7e071 - m_Address: Assets/MyGame/aa/Game/XJMH/Sound/PW_Bg.ogg - m_ReadOnly: 0 - m_SerializedLabels: - - Audio - - Default - FlaggedDuringContentUpdateRestriction: 0 - m_GUID: c37fb4f7c76b1154a9d20ad974a14010 m_Address: Assets/Art/XJMH/pw/pw_sprite/handbook/PW_Enemy_104.png m_ReadOnly: 0 @@ -1525,6 +1540,12 @@ MonoBehaviour: - FishPrefab - XJMH FlaggedDuringContentUpdateRestriction: 0 + - m_GUID: e54c60eb5da5278478389292015f8e8a + m_Address: Assets/Art/XJMH/pw/pw_sprite/freemodel/PW_FreeItem_4.png + m_ReadOnly: 0 + m_SerializedLabels: + - XJMH + FlaggedDuringContentUpdateRestriction: 0 - m_GUID: e590af03990a88a4c842c66df7dde040 m_Address: Assets/MyGame/aa/Game/XJMH/Sound/PW_Prop_GammaStorm_Attack.ogg m_ReadOnly: 0 @@ -1733,48 +1754,6 @@ MonoBehaviour: - Audio - XJMH FlaggedDuringContentUpdateRestriction: 0 - - m_GUID: b40ddb35d439d4546ab220424de7e930 - m_Address: Assets/Art/XJMH/pw/pw_sprite/common/zd_jn_sd.png - m_ReadOnly: 0 - m_SerializedLabels: - - XJMH - FlaggedDuringContentUpdateRestriction: 0 - - m_GUID: 3b6ce020d078b524ab9f8ea29d7fe431 - m_Address: Assets/Art/XJMH/pw/pw_sprite/common/zd_jn_zd.png - m_ReadOnly: 0 - m_SerializedLabels: - - XJMH - FlaggedDuringContentUpdateRestriction: 0 - - m_GUID: adf779a3413c62744b56021a0ad5c93d - m_Address: Assets/Art/XJMH/pw/pw_sprite/common/zd_jn_ss.png - m_ReadOnly: 0 - m_SerializedLabels: - - XJMH - FlaggedDuringContentUpdateRestriction: 0 - - m_GUID: 1abfe2b699a5f764c896951d8bc859ad - m_Address: Assets/Art/XJMH/pw/pw_sprite/freemodel/PW_FreeItem_1.png - m_ReadOnly: 0 - m_SerializedLabels: - - XJMH - FlaggedDuringContentUpdateRestriction: 0 - - m_GUID: 689bc9268484c3c4fab1743b9c99ee9e - m_Address: Assets/Art/XJMH/pw/pw_sprite/freemodel/PW_FreeItem_2.png - m_ReadOnly: 0 - m_SerializedLabels: - - XJMH - FlaggedDuringContentUpdateRestriction: 0 - - m_GUID: 9bf752fc3d63b374998b79d5b9e169a0 - m_Address: Assets/Art/XJMH/pw/pw_sprite/freemodel/PW_FreeItem_3.png - m_ReadOnly: 0 - m_SerializedLabels: - - XJMH - FlaggedDuringContentUpdateRestriction: 0 - - m_GUID: e54c60eb5da5278478389292015f8e8a - m_Address: Assets/Art/XJMH/pw/pw_sprite/freemodel/PW_FreeItem_4.png - m_ReadOnly: 0 - m_SerializedLabels: - - XJMH - FlaggedDuringContentUpdateRestriction: 0 m_ReadOnly: 0 m_Settings: {fileID: 11400000, guid: 3d7ea06370502a34e82011fabf822b45, type: 2} m_SchemaSet: diff --git a/Assets/AddressableAssetsData/Windows/addressables_content_state.bin b/Assets/AddressableAssetsData/Windows/addressables_content_state.bin index ec818b67b70d467f982695c2aa238ce68407f9ea..c8bb5aba9f5241c9b774ac5e1b2c0f3dc6b3d133 100644 GIT binary patch delta 459 zcmaivy-EW?6ouW4L?MYOOe2C!U>{)a{Ov4+ux%_A)JnWFv*R;pA$C?)qZV2SX#@)q zwoK=HC>8;05mGyEu$Oz!sqW!?b2_K*mxKld9-L)LIEG9)$AI%-xDck0TwwD^CK*L1 zlwqiAE>#IE_`s!e9!$`@MpZYfb&*k~q(Vd~wb!UcK+8Ob+5rmB09c%LuV}$Rr)Z6! zYgZbjePN9)z<6C?X-)IfU1Zdl8ZCmAya?J!Yf31CkfIdnT_SHjHpiX3t^AvD=W(NV z+ga_$hu&-6hH*$vi|G3mxjX)gFa7(xjcB#l`HW|SY8t;gWGzjWb}#uTT~{Y*^cpM1namxL#z|s?1 +/// Addressables / AssetBundle 已加载包实时监控悬浮窗(调试插件)。 +/// +/// 【用途】 +/// 实时查看当前进程里加载了哪些 AA 包(bundle)、数量与内存概况, +/// 用于定位"进房加载资源时内存暴涨 / 被 iOS jetsam 杀掉"这类问题。 +/// 注意:本项目所有 Addressables 组都是 LZMA 压缩,包一旦被打开就会整体解压进内存 +/// (实测 fish_hhwk 解压 487MB、fish_flzy 436MB、fish_xjmh 219MB), +/// 所以看到哪个大包出现时,概览里的内存数字会立刻抬升,可直接对上。 +/// +/// 【使用方式】 +/// 1. 编辑器 Play / Development Build:自动出现在左上角,无需任何操作; +/// 2. 其他构建(如 Release 真机包):加 AA_MONITOR 宏,或把本组件手动挂到任意常驻物体上。 +/// +/// 【操作】 +/// - 左上角小标签:可拖动;轻点 = 展开 / 收起面板(编辑器下 F9 也可切换) +/// - 面板:上一页 / 下一页翻页;"导出日志"把完整列表 + 内存打入 Player Log(真机看 Xcode / idevice 日志) +/// - 每次有新包加载 / 卸载时自动打日志(logOnChange,默认开) +/// +/// 【注意】 +/// 项目 Active Input Handling = Input System(New),IMGUI 收不到点击事件, +/// 所以本窗口的交互(拖动 / 轻点 / 翻页)全部用新输入系统自行采样实现,不依赖 GUILayout 按钮; +/// 也因此悬浮层上的点击无法从游戏输入里"吃掉",测试时的小误触属正常现象。 +/// +public class AABundleMonitor : MonoBehaviour +{ + #region 配置 + + /// 新包加载 / 卸载时自动输出日志(真机排查用,建议保持开启) + public static bool logOnChange = true; + + /// 刷新间隔(秒):0.5 秒扫一次已加载包列表,避免每帧开销 + private const float RefreshInterval = 0.5f; + + /// 新增包高亮保持时长(秒) + private const float HighlightDuration = 5f; + + #endregion + + #region 自动安装 + +#if UNITY_EDITOR || DEVELOPMENT_BUILD || AA_MONITOR + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)] + private static void AutoInstall() + { + if (FindFirstObjectByType() != null) return; + var go = new GameObject("[AABundleMonitor]"); + DontDestroyOnLoad(go); + go.AddComponent(); + } +#endif + + #endregion + + #region 数据 + + /// 单个已加载包的记录 + private sealed class BundleRecord + { + public string name; // 完整 bundle 名(导出日志用) + public string shortName; // 截断后的显示名 + public float firstSeen; // 首次出现时间(realtimeSinceStartup) + public bool streamedScene; // 是否是场景包 + public string tag; // 内容标识(扫描包内资源路径得出,如 XJMH/HHWK/FLZY/LOGIN) + public int assetCount; // 包内资源数量(GetAllAssetNames 只读目录,不会加载资源) + public List samplePaths; // 样例资源路径(导出日志用,帮助辨认包内容) + } + + private readonly Dictionary _records = new Dictionary(); + private readonly List _sorted = new List(); // 按加载时间倒序(最新在前) + private readonly HashSet _loadedNow = new HashSet(); // 本次扫描到的包名 + private readonly List _removed = new List(); // 本次扫描发现已卸载的包名 + + private float _appStartTime; + private float _nextRefreshTime; + + // 悬浮标签(可拖动) + private Vector2 _ballPos = new Vector2(16f, 16f); + private Rect _ballRect; + private bool _ballDragging; + private Vector2 _ballDragOffset; + private bool _ballMoved; + + // 面板 + private bool _panelOpen = true; + private int _page; + private int _rowsPerPage = 12; + + // 上一帧 OnGUI 画出的按钮区域(供自采样点击命中,IMGUI 事件在本项目不可用) + private Rect _prevPageRect, _nextPageRect, _exportRect; + + // 样式缓存 + private GUIStyle _styleTitle, _styleLine, _styleOld, _styleNew, _styleButton, _styleWrap; + + // 编辑器 PlayMode Script 名称(仅编辑器下有效,反射读取,失败为空) + private string _editorPlayModeName; + + #endregion + + #region 生命周期 + + private void Awake() + { + _appStartTime = Time.realtimeSinceStartup; + if (Application.isEditor) _editorPlayModeName = ReadEditorPlayModeName(); + Refresh(); + } + + private void Update() + { + PollPointer(); + if (Time.unscaledTime >= _nextRefreshTime) + Refresh(); + } + + #endregion + + #region 扫描已加载包 + + private void Refresh() + { + _nextRefreshTime = Time.unscaledTime + RefreshInterval; + + int addedCount = 0; + _loadedNow.Clear(); + var bundles = AssetBundle.GetAllLoadedAssetBundles(); + if (bundles != null) + { + foreach (var b in bundles) + { + if (b == null) continue; + string n = b.name; + _loadedNow.Add(n); + + if (!_records.ContainsKey(n)) + { + string baseName = n.EndsWith(".bundle") ? n.Substring(0, n.Length - 7) : n; + var rec = new BundleRecord + { + name = n, + shortName = baseName.Length > 16 ? baseName.Substring(0, 16) + "…" : baseName, + firstSeen = Time.realtimeSinceStartup, + streamedScene = b.isStreamedSceneAssetBundle + }; + + // ⭐ 扫描包内资源清单辨认内容(GetAllAssetNames 只读包目录、不会把资源加载进内存) + try + { + var assets = b.GetAllAssetNames(); + if (assets != null && assets.Length > 0) + { + rec.assetCount = assets.Length; + rec.tag = GuessTag(assets); + rec.samplePaths = new List(); + for (int i = 0; i < assets.Length && rec.samplePaths.Count < 4; i++) + rec.samplePaths.Add(assets[i]); + } + } + catch { } + + _records[n] = rec; + addedCount++; + if (logOnChange) + Debug.Log(string.IsNullOrEmpty(rec.tag) + ? $"[AA包监控] + {n}({rec.assetCount} 项)" + : $"[AA包监控] + {n}【{rec.tag}】({rec.assetCount} 项)"); + } + } + } + + // 已卸载的包:从记录中移除 + _removed.Clear(); + foreach (var kv in _records) + if (!_loadedNow.Contains(kv.Key)) _removed.Add(kv.Key); + foreach (var n in _removed) + { + _records.Remove(n); + if (logOnChange) + Debug.Log($"[AA包监控] - {n}"); + } + + // 排序:最新加载的排最前(进房时新增的包会直接顶到列表上方) + _sorted.Clear(); + _sorted.AddRange(_records.Values); + _sorted.Sort((a, b) => b.firstSeen.CompareTo(a.firstSeen)); + + if (logOnChange && (addedCount > 0 || _removed.Count > 0)) + Debug.Log($"[AA包监控] 变动:+{addedCount} / -{_removed.Count},当前共 {_records.Count} 个包 | {MemSummary()}"); + } + + private static string MemSummary() + { + long allocated = Profiler.GetTotalAllocatedMemoryLong(); + long reserved = Profiler.GetTotalReservedMemoryLong(); + long mono = Profiler.GetMonoUsedSizeLong(); + return $"内存: 已分配 {allocated / 1048576f:F1}MB / 预留 {reserved / 1048576f:F1}MB / Mono {mono / 1048576f:F1}MB"; + } + + /// + /// 根据包内资源路径猜内容标识:优先识别玩法关键词(XJMH/HHWK/FLZY), + /// 否则取出现最多的路径段(如 login/home/zhanshen 等),识别不出返回空。 + /// + private static string GuessTag(string[] assets) + { + int sample = Mathf.Min(assets.Length, 400); + int xjmh = 0, hhwk = 0, flzy = 0; + var freq = new Dictionary(StringComparer.OrdinalIgnoreCase); + + for (int i = 0; i < sample; i++) + { + string a = assets[i].ToLowerInvariant(); + if (a.Contains("xjmh")) xjmh++; + else if (a.Contains("hhwk")) hhwk++; + else if (a.Contains("flzy") || a.Contains("fenglei")) flzy++; + + var parts = a.Split('/'); + for (int k = 1; k < parts.Length && k <= 3; k++) + { + string seg = parts[k]; + if (seg.Length == 0 || seg.Length > 12) continue; + if (seg == "assets" || seg == "mygame" || seg == "art" || seg == "game" || seg == "aa" || seg == "ui") continue; + freq.TryGetValue(seg, out int c); + freq[seg] = c + 1; + } + } + + int minHits = Mathf.Max(3, sample / 5); + if (xjmh >= minHits && xjmh >= hhwk && xjmh >= flzy) return "XJMH"; + if (hhwk >= minHits && hhwk >= xjmh && hhwk >= flzy) return "HHWK"; + if (flzy >= minHits && flzy >= xjmh && flzy >= hhwk) return "FLZY"; + + string best = null; + int bestCount = 0; + foreach (var kv in freq) + if (kv.Value > bestCount) { bestCount = kv.Value; best = kv.Key; } + return bestCount >= minHits ? best.ToUpperInvariant() : null; + } + + /// + /// 编辑器里显示当前 Addressables Play Mode Script 状态: + /// Use Asset Database / Simulate Groups 模式下资源直接走工程读取、不产生 bundle, + /// 本窗口看不到包属正常;0 个包时附带"去哪看真实包"的操作指引。 + /// + private string EditorStatusLine() + { + if (!Application.isEditor) return null; + string mode = string.IsNullOrEmpty(_editorPlayModeName) ? "未知" : _editorPlayModeName; + if (_records.Count == 0 && mode != "Use Existing Build") + return $"编辑器 PlayMode:{mode} —— 此模式资源不走 bundle,0 个包属正常。" + + "要看真实包:构建 Windows 内容后切 Use Existing Build,或用真机 Dev Build"; + return $"编辑器 PlayMode:{mode}"; + } + + /// + /// 反射读取 Addressables 编辑器设置里的 PlayMode Script 索引 + /// (0=Use Asset Database 1=Simulate Groups 2=Use Existing Build 3=Default Build Script)。 + /// 用反射是为了不依赖 Unity.Addressables.Editor 程序集,运行时/真机编译不受影响。 + /// + private static string ReadEditorPlayModeName() + { + try + { + var t = Type.GetType("UnityEditor.AddressableAssets.Settings.ProjectConfigData, Unity.Addressables.Editor"); + var prop = t?.GetProperty("ActivePlayModeIndex", BindingFlags.Public | BindingFlags.Static); + if (prop != null) + { + int idx = (int)prop.GetValue(null); + switch (idx) + { + case 0: return "Use Asset Database"; + case 1: return "Simulate Groups"; + case 2: return "Use Existing Build"; + case 3: return "Default Build Script"; + } + return $"index={idx}"; + } + } + catch { } + return "未知"; + } + + #endregion + + #region 输入(新输入系统自采样:拖动标签 / 轻点切换 / 翻页 / 导出) + + private void PollPointer() + { + // 键盘便捷开关(编辑器 / 带键盘设备;真机为 null 自动跳过) + var kb = Keyboard.current; + if (kb != null && kb.f9Key.wasPressedThisFrame) + _panelOpen = !_panelOpen; + + Vector2 screenPos; + bool isPressed, wasPressed, wasReleased; + + var ts = Touchscreen.current; + var m = Mouse.current; + + bool touchActive = ts != null && + (ts.primaryTouch.press.isPressed || ts.primaryTouch.press.wasPressedThisFrame || ts.primaryTouch.press.wasReleasedThisFrame); + + if (touchActive) + { + screenPos = ts.primaryTouch.position.ReadValue(); + isPressed = ts.primaryTouch.press.isPressed; + wasPressed = ts.primaryTouch.press.wasPressedThisFrame; + wasReleased = ts.primaryTouch.press.wasReleasedThisFrame; + } + else if (m != null) + { + screenPos = m.position.ReadValue(); + isPressed = m.leftButton.isPressed; + wasPressed = m.leftButton.wasPressedThisFrame; + wasReleased = m.leftButton.wasReleasedThisFrame; + } + else + { + return; + } + + // 输入系统的坐标原点在左下,OnGUI 在左上 + var guiPos = new Vector2(screenPos.x, Screen.height - screenPos.y); + + if (wasPressed) + OnPointerPressed(guiPos); + + if (_ballDragging) + { + if (isPressed) + { + Vector2 target = guiPos - _ballDragOffset; + if (Vector2.Distance(target, _ballPos) > 6f) _ballMoved = true; + _ballPos = ClampToScreen(target); + } + else if (wasReleased) + { + _ballDragging = false; + if (!_ballMoved) _panelOpen = !_panelOpen; // 轻点 = 切换面板 + } + } + } + + private void OnPointerPressed(Vector2 guiPos) + { + _ballDragging = false; + _ballMoved = false; + + if (_ballRect.Contains(guiPos)) + { + _ballDragging = true; + _ballDragOffset = guiPos - _ballPos; + return; + } + + if (!_panelOpen) return; + + if (_prevPageRect.Contains(guiPos)) { _page--; return; } + if (_nextPageRect.Contains(guiPos)) { _page++; return; } + if (_exportRect.Contains(guiPos)) { ExportLog(); return; } + } + + private static Vector2 ClampToScreen(Vector2 pos) + { + return new Vector2( + Mathf.Clamp(pos.x, 0f, Mathf.Max(0f, Screen.width - 160f)), + Mathf.Clamp(pos.y, 0f, Mathf.Max(0f, Screen.height - 64f))); + } + + #endregion + + #region 绘制 + + private void OnGUI() + { + EnsureStyles(); + DrawBall(); + if (_panelOpen) + DrawPanel(); + } + + private void DrawBall() + { + _ballRect = new Rect(_ballPos.x, _ballPos.y, 152f, 56f); + + GUI.color = new Color(0f, 0f, 0f, 0.72f); + GUI.DrawTexture(_ballRect, Texture2D.whiteTexture); + GUI.color = Color.white; + + GUI.Label(new Rect(_ballRect.x + 12f, _ballRect.y + 3f, 140f, 26f), $"AA包 {_records.Count}", _styleTitle); + GUI.Label(new Rect(_ballRect.x + 12f, _ballRect.y + 28f, 140f, 24f), _panelOpen ? "点我收起" : "点我展开", _styleLine); + } + + private void DrawPanel() + { + float w = Mathf.Min(Screen.width - 32f, 980f); + float h = Mathf.Min(Screen.height - 140f, 1500f); + float x = Mathf.Clamp(_ballPos.x, 8f, Mathf.Max(8f, Screen.width - w - 8f)); + float y = Mathf.Clamp(_ballPos.y + 64f, 8f, Mathf.Max(8f, Screen.height - h - 8f)); + var panel = new Rect(x, y, w, h); + + GUI.color = new Color(0f, 0f, 0f, 0.84f); + GUI.DrawTexture(panel, Texture2D.whiteTexture); + GUI.color = Color.white; + + string status = EditorStatusLine(); + float statusH = status == null ? 0f : 58f; + float headerH = 108f + statusH; + const float footerH = 64f; + const float rowH = 30f; + + // ---- 概要 ---- + GUI.Label(new Rect(panel.x + 16f, panel.y + 8f, w - 32f, 30f), $"AA 已加载包监控 共 {_records.Count} 个", _styleTitle); + GUI.Label(new Rect(panel.x + 16f, panel.y + 42f, w - 32f, 26f), MemSummary(), _styleLine); + GUI.Label(new Rect(panel.x + 16f, panel.y + 70f, w - 32f, 26f), + $"运行 {(Time.realtimeSinceStartup - _appStartTime):F0}s 黄色 = 近 {HighlightDuration:F0} 秒新增(新加载的包在列表最上方)", _styleLine); + if (status != null) + GUI.Label(new Rect(panel.x + 16f, panel.y + 96f, w - 32f, statusH), status, _styleWrap); + + // ---- 列表分页 ---- + _rowsPerPage = Mathf.Max(4, (int)((panel.height - headerH - footerH - 12f) / rowH)); + int totalPages = Mathf.Max(1, Mathf.CeilToInt(_sorted.Count / (float)_rowsPerPage)); + _page = Mathf.Clamp(_page, 0, totalPages - 1); + int start = _page * _rowsPerPage; + + float rowY = panel.y + headerH; + for (int i = 0; i < _rowsPerPage; i++) + { + int idx = start + i; + if (idx >= _sorted.Count) break; + + var r = _sorted[idx]; + bool isNew = Time.realtimeSinceStartup - r.firstSeen < HighlightDuration; + var style = isNew ? _styleNew : _styleOld; + string sceneTag = r.streamedScene ? "[场景] " : ""; + string info = string.IsNullOrEmpty(r.tag) + ? (r.assetCount > 0 ? $"{r.assetCount}项" : "") + : $"【{r.tag}】{r.assetCount}项"; + string text = $"{sceneTag}{r.shortName} {info} (+{r.firstSeen - _appStartTime:F0}s)"; + GUI.Label(new Rect(panel.x + 18f, rowY, w - 36f, rowH), text, style); + rowY += rowH; + } + + // ---- 底部按钮(自采样点击,IMGUI 按钮不可用)---- + float btnY = panel.yMax - 52f; + float btnW = 150f; + _prevPageRect = new Rect(panel.x + 16f, btnY, btnW, 44f); + _nextPageRect = new Rect(panel.x + 16f + btnW + 12f, btnY, btnW, 44f); + _exportRect = new Rect(panel.xMax - btnW - 16f, btnY, btnW, 44f); + + DrawFakeButton(_prevPageRect, "上一页"); + DrawFakeButton(_nextPageRect, "下一页"); + DrawFakeButton(_exportRect, "导出日志"); + + GUI.Label(new Rect(panel.x + 16f + (btnW + 12f) * 2f, btnY + 10f, 160f, 26f), $"{_page + 1} / {totalPages}", _styleLine); + } + + private void DrawFakeButton(Rect r, string text) + { + GUI.color = new Color(1f, 1f, 1f, 0.22f); + GUI.DrawTexture(r, Texture2D.whiteTexture); + GUI.color = Color.white; + GUI.Label(new Rect(r.x, r.y + 8f, r.width, 28f), text, _styleButton); + } + + private void EnsureStyles() + { + if (_styleTitle != null) return; + + _styleTitle = new GUIStyle(GUI.skin.label) { fontSize = 26, fontStyle = FontStyle.Bold }; + _styleTitle.normal.textColor = Color.white; + + _styleLine = new GUIStyle(GUI.skin.label) { fontSize = 22 }; + _styleLine.normal.textColor = new Color(0.82f, 0.9f, 1f); + + _styleOld = new GUIStyle(GUI.skin.label) { fontSize = 23 }; + _styleOld.normal.textColor = Color.white; + + _styleNew = new GUIStyle(GUI.skin.label) { fontSize = 23, fontStyle = FontStyle.Bold }; + _styleNew.normal.textColor = new Color(1f, 0.82f, 0.2f); + + _styleButton = new GUIStyle(GUI.skin.label) { fontSize = 24, alignment = TextAnchor.UpperCenter }; + _styleButton.normal.textColor = Color.white; + + _styleWrap = new GUIStyle(GUI.skin.label) { fontSize = 20, wordWrap = true }; + _styleWrap.normal.textColor = new Color(0.95f, 0.9f, 0.62f); + } + + #endregion + + #region 导出 + + private void ExportLog() + { + var sb = new StringBuilder(); + sb.AppendLine($"[AA包监控] 导出时间 {DateTime.Now:yyyy-MM-dd HH:mm:ss},共 {_records.Count} 个包 | {MemSummary()}"); + foreach (var r in _sorted) + sb.AppendLine($" {(r.streamedScene ? "[场景] " : "")}{r.name} (+{r.firstSeen - _appStartTime:F1}s)"); + + string text = sb.ToString(); + Debug.Log(text); + try { GUIUtility.systemCopyBuffer = text; } catch { } + } + + #endregion +} \ No newline at end of file diff --git a/Assets/MyGame/Scripts/Debug/AABundleMonitor.cs.meta b/Assets/MyGame/Scripts/Debug/AABundleMonitor.cs.meta new file mode 100644 index 000000000..9c7291ae6 --- /dev/null +++ b/Assets/MyGame/Scripts/Debug/AABundleMonitor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bd70cad3c45605a4f824c9eaf1eb081d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/MyGame/Scripts/Turret/SitPosData.cs b/Assets/MyGame/Scripts/Turret/SitPosData.cs index 9fb85a2d4..5d2715469 100644 --- a/Assets/MyGame/Scripts/Turret/SitPosData.cs +++ b/Assets/MyGame/Scripts/Turret/SitPosData.cs @@ -261,12 +261,15 @@ public class SitPosData : MonoBehaviour, IPointerDownHandler, IPointerUpHandler if (btn_TurretMinus != null) btn_TurretMinus.onClick.AddListener(() => ChangeTurretMinusConsumeNum()); - // ⭐ 自动攻击开关:先检测自身 GameObject 激活状态,没激活就激活(预制体/外部可能把它藏了, - // 隐藏状态下点不到,自动攻击无法开关);再挂监听:关闭时本地座位炮台切回 Idle(原 GameMainPanel 的 toggle_SkillAuto 逻辑) + // ⭐ 自动攻击开关:默认隐藏,等玩家入座时由 ShowPlayer 按"是否本地座位"决定显隐。 + // ⚠️ 以前这里是"没激活就无条件激活"(为修预制体把按钮藏了、点不到的问题), + // 但第二个人加入时他的座位才第一次激活、Awake 此刻才执行 → 会把他的"自动"按钮也打开, + // 屏幕上出现两个"自动"按钮。改为默认隐藏 + 入座时按本地/他座显隐(本地按钮不会再被藏)。 + // 监听照旧:关闭时本地座位炮台切回 Idle(原 GameMainPanel 的 toggle_SkillAuto 逻辑) if (toggle_AutoAttack != null) { - if (!toggle_AutoAttack.gameObject.activeSelf) - toggle_AutoAttack.gameObject.SetActive(true); + if (toggle_AutoAttack.gameObject.activeSelf) + toggle_AutoAttack.gameObject.SetActive(false); toggle_AutoAttack.onValueChanged.AddListener(OnAutoAttackToggleChanged); } @@ -1101,6 +1104,11 @@ public class SitPosData : MonoBehaviour, IPointerDownHandler, IPointerUpHandler transform.GetChild(1).gameObject.SetActive(false); } + // ⭐ "自动"按钮只属于本地玩家:本地座位显示、其他座位隐藏 + // (第二个人加入时他的座位才第一次激活,Awake 已改为默认隐藏,这里按座位归属再定一次) + if (toggle_AutoAttack != null) + toggle_AutoAttack.gameObject.SetActive(user == GameManager.Instance?.user?.id); + // 如果是本地玩家的座位,自动注册并加载装备 if (user == GameManager.Instance?.user?.id) { @@ -1134,6 +1142,10 @@ public class SitPosData : MonoBehaviour, IPointerDownHandler, IPointerUpHandler /// public void ShowWaiting() { + // ⭐ 离座:自动按钮一并隐藏(XJMH 也要处理——空座位不该留"自动"按钮) + if (toggle_AutoAttack != null) + toggle_AutoAttack.gameObject.SetActive(false); + if (turretControlMode == TurretControlMode.XJMH) return; if (transform.childCount > 0) diff --git a/Assets/MyGame/Scripts/fishResources/AudioResourcesData.cs b/Assets/MyGame/Scripts/fishResources/AudioResourcesData.cs index 6a0fa3d2a..4c2de34a7 100644 --- a/Assets/MyGame/Scripts/fishResources/AudioResourcesData.cs +++ b/Assets/MyGame/Scripts/fishResources/AudioResourcesData.cs @@ -1,4 +1,4 @@ -using Newtonsoft.Json; +using Newtonsoft.Json; using Spine.Unity; using System; using System.Collections; @@ -20,10 +20,20 @@ public class AudioResourcesData static Dictionary audioClipDefaultDic=new Dictionary(); static Dictionary audioClipGameDic=new Dictionary(); const string Audio_AALabel="Audio"; + + // ⭐ 句柄显式管理(修复"包只增不减"的根源): + // 之前 Load 的句柄直接丢弃(泄漏,包永不卸载),UnLoad 再重新 Load 一遍拿句柄释放 + // (净引用数 0,等于什么都没卸载)。现在:加载时保存句柄,卸载时真正 Release。 + // 注意:游戏音频物理上打在鱼包里(Audio+玩法 交集 = 鱼包),释放本句柄是鱼包能卸载的前提之一。 + /// 默认音频句柄(Audio+Default,进程常驻,不卸载) + static AsyncOperationHandle> _defaultAudioHandle; + /// 当前玩法游戏音频句柄(Audio+玩法,进房加载/离房释放) + static AsyncOperationHandle> _gameAudioHandle; + static AudioResourcesData() { - - + + //这里要添加默认的音效 背景 按钮点击 LoadDefaultAudioGame(); } @@ -39,7 +49,7 @@ public class AudioResourcesData name_IDDic[item.audioResourcesPath]=item.audioID; } var keys = new List { Audio_AALabel, "Default" }; - await Addressables.LoadAssetsAsync(keys, audio => + _defaultAudioHandle = Addressables.LoadAssetsAsync(keys, audio => { Debug.Log("加载音频: " + audio.name); if (name_IDDic.ContainsKey(audio.name)) @@ -47,7 +57,8 @@ public class AudioResourcesData Debug.Log("加载音频ID: " + name_IDDic[audio.name]); audioClipDefaultDic.Add(name_IDDic[audio.name],audio); } - }, MergeMode.Intersection).Task; + }, MergeMode.Intersection); + await _defaultAudioHandle.Task; foreach (var item in audioClipDefaultDic) { audioClipDic.Add(item.Key, item.Value); @@ -82,13 +93,22 @@ public class AudioResourcesData try { - await Addressables.LoadAssetsAsync(keys, audio => + // ⭐ 防御:上一轮句柄未释放(如离房被跳过)时先释放,避免引用计数累加 + if (_gameAudioHandle.IsValid()) + Addressables.Release(_gameAudioHandle); + + // ⭐ 双标签交集(Audio + 当前玩法):只加载当前玩法标记的音频。 + // Union 会按 "Audio" 标签把其它玩法的音频包一并打开, + // 而这些音频物理上和鱼的 Prefab 打在同一个 LZMA 包里 → 整包解压进内存导致 iOS 被杀 + // 句柄保存到 _gameAudioHandle,离房时 UnLoadAudioGame 真正释放 → 鱼包才能卸载 + _gameAudioHandle = Addressables.LoadAssetsAsync(keys, audio => { if (name_IDDic != null && name_IDDic.ContainsKey(audio.name)) { audioClipGameDic[name_IDDic[audio.name]] = audio; } - }, MergeMode.Union).Task; + }, MergeMode.Intersection); + await _gameAudioHandle.Task; audioClipDic = new Dictionary(); if (audioClipDefaultDic != null) @@ -107,40 +127,21 @@ public class AudioResourcesData Debug.LogError($"加载游戏音频失败: {e.Message}"); } } - public static async Task UnLoadAudioGame() + public static Task UnLoadAudioGame() { - GameType gameType = GameManager.Instance.gameData.currGameType; - var keys = new List { Audio_AALabel, gameType.ToString() }; + // ⭐ 真正的卸载:释放加载时保存的句柄(引用归零后 Audio+玩法 命中的 bundle 才会真正卸载)。 + // 旧实现"重新 Load 一遍拿句柄再 Release"是净 0 操作,什么都卸不掉。 + if (_gameAudioHandle.IsValid()) + Addressables.Release(_gameAudioHandle); - // 加载对应的句柄(只是为了拿到引用,之后释放) - AsyncOperationHandle> handle = Addressables.LoadAssetsAsync(keys, null, MergeMode.Intersection); - await handle.Task; + // 清空游戏音频字典(包卸载后 AudioClip 对象已失效,残留引用会导致播放 null) + audioClipGameDic.Clear(); - // 遍历卸载对应的音频 - foreach (var audio in handle.Result) - { - if (name_IDDic.ContainsKey(audio.name)) - { - int id = name_IDDic[audio.name]; - if (audioClipGameDic.ContainsKey(id)) - { - audioClipGameDic.Remove(id); - Debug.Log($"卸载音频: {audio.name} (ID={id})"); - } - } - } - - // 释放 Addressables 资源句柄 - Addressables.Release(handle); - - // 重建总字典:保留默认音频 + 剩余游戏音频 + // 重建总字典:只保留默认音频 audioClipDic = new Dictionary(audioClipDefaultDic); - foreach (var item in audioClipGameDic) - { - audioClipDic[item.Key] = item.Value; - } Debug.Log("卸载完成,当前音频数量:" + audioClipDic.Count); + return Task.CompletedTask; } diff --git a/Assets/MyGame/Scripts/fishResources/FishResourcesData.cs b/Assets/MyGame/Scripts/fishResources/FishResourcesData.cs index 2bcc69aea..83d59f31e 100644 --- a/Assets/MyGame/Scripts/fishResources/FishResourcesData.cs +++ b/Assets/MyGame/Scripts/fishResources/FishResourcesData.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using UnityEngine; using UnityEngine.AddressableAssets; +using UnityEngine.ResourceManagement.AsyncOperations; using static UnityEngine.AddressableAssets.Addressables; [Serializable] @@ -77,6 +78,14 @@ public class FishResourcesData public static Dictionary fishResourcesDatasDic;//id--FishResourcesData public static Dictionary fishResourcesSpritesDic;//id--Sprite private static Dictionary diePrefabDic;//id--SkeletonDataAsset + + // ⭐ 句柄显式管理(修复"包只增不减"的根源): + // 旧实现加载完立即 Release 句柄,但 fishPrefabDic 还持有 prefab 引用 —— + // 包没被卸载全靠 AudioResourcesData 泄漏的音频句柄"误打误撞"撑着(音频物理上就在鱼包里)。 + // 现在:加载时保存句柄(prefab/dieSpine/sprite 三类),离房时 UnLoadFishAssetsData 统一 Release, + // 引用归零后鱼包真正从内存卸载 —— 依次进所有玩法不再累加内存。 + private static readonly List _fishHandles = new List(); + public static async Task LoadFishAssetsData() { // 清空对象池 @@ -86,6 +95,13 @@ public class FishResourcesData diePrefabDic = new Dictionary(); fishResourcesSpritesDic = new Dictionary(); + // ⭐ 防御:上一轮句柄未释放(如离房流程被跳过)时先释放,避免引用计数累加 + foreach (var h in _fishHandles) + { + if (h.IsValid()) Addressables.Release(h); + } + _fishHandles.Clear(); + // ---------------- 1. 预下载所有依赖资源(关键) ---------------- var labels = new List { @@ -120,7 +136,8 @@ public class FishResourcesData }, MergeMode.Intersection); await prefabHandle.Task; - Addressables.Release(prefabHandle); + // ⭐ 不再立即 Release:保存句柄保持包存活,离房时统一卸载 + _fishHandles.Add(prefabHandle); Debug.Log($"Prefab 加载完成,共 {fishPrefabDic.Count} 个"); // ---------------- 3. 加载死亡 Spine 动效 ---------------- @@ -138,7 +155,7 @@ public class FishResourcesData }, MergeMode.Intersection); await dieHandle.Task; - Addressables.Release(dieHandle); + _fishHandles.Add(dieHandle); Debug.Log($"死亡 Spine 加载完成,共 {diePrefabDic.Count} 个"); } catch (Exception e) @@ -159,7 +176,7 @@ public class FishResourcesData }, MergeMode.Intersection); await spriteHandle.Task; - Addressables.Release(spriteHandle); + _fishHandles.Add(spriteHandle); Debug.Log($"Sprite 加载完成,共 {fishResourcesSpritesDic.Count} 个"); } catch (Exception e) @@ -171,81 +188,26 @@ public class FishResourcesData } - public static async Task UnLoadFishAssetsData() + public static Task UnLoadFishAssetsData() { + // ⭐ 真正的卸载:释放加载时保存的全部句柄(引用归零后鱼包才会真正卸载)。 + // 旧实现"重新 Load 一遍拿句柄再 Release"是净 0 操作,什么都卸不掉。 Pool.Instance.Clear(); - // 卸载 Prefab - var keys = new List { prefabFish_AALabel, GameManager.Instance.gameData.currGameType.ToString() }; - var prefabHandle = Addressables.LoadAssetsAsync(keys, null, MergeMode.Intersection); - await prefabHandle.Task; - - if (prefabHandle.Result != null) + foreach (var h in _fishHandles) { - foreach (var prefab in prefabHandle.Result) - { - if (FishResourcesPrefabsDic.TryGetValue(prefab.name, out int id)) - { - if (fishPrefabDic.Remove(id)) - { - Debug.Log($"卸载鱼Prefab: {prefab.name} (ID={id})"); - } - } - } + if (h.IsValid()) + Addressables.Release(h); } - Addressables.Release(prefabHandle); + _fishHandles.Clear(); - // 卸载死亡 Spine 动效 - keys = new List { dieSpine_AALabel, GameManager.Instance.gameData.currGameType.ToString() }; - var dieHandle = Addressables.LoadAssetsAsync(keys, null, MergeMode.Intersection); - await dieHandle.Task; + // ⭐ 清空字典(包卸载后对象已失效,残留引用会指向已销毁资源) + fishPrefabDic?.Clear(); + diePrefabDic?.Clear(); + fishResourcesSpritesDic?.Clear(); - if (dieHandle.Result != null) - { - foreach (var data in dieHandle.Result) - { - if (FishResourcesSpineDieDic.TryGetValue(data.name, out int id)) - { - if (diePrefabDic.Remove(id)) - { - Debug.Log($"卸载鱼死亡动效: {data.name} (ID={id})"); - } - } - } - } - Addressables.Release(dieHandle); - - // 卸载鱼 Sprite - // ⭐ try-catch 防护:Addressables 可能找不到 TuJian+游戏类型 交集的 Sprite, - // 抛 InvalidKeyException 导致整个 UnLoadFishAssetsData 中断,后续清理无法执行。 - // Sprite 加载时已有 try-catch,卸载时也应对称处理 - try - { - keys = new List { Sprite_AALabel, GameManager.Instance.gameData.currGameType.ToString() }; - var spriteHandle = Addressables.LoadAssetsAsync(keys, null, MergeMode.Intersection); - await spriteHandle.Task; - - if (spriteHandle.Result != null) - { - foreach (var sprite in spriteHandle.Result) - { - if (FishResourcesSpriteDic.TryGetValue(sprite.name, out int id)) - { - if (fishResourcesSpritesDic.Remove(id)) - { - Debug.Log($"卸载鱼Sprite: {sprite.name} (ID={id})"); - } - } - } - } - Addressables.Release(spriteHandle); - } - catch (Exception e) - { - Debug.LogWarning($"[FishResourcesData] Sprite 卸载失败(游戏类型={GameManager.Instance.gameData.currGameType}),跳过此项: {e.Message}"); - } - - Debug.Log($"卸载完成,剩余Prefab数量: {fishPrefabDic.Count}, 死亡动效数量: {diePrefabDic.Count}, Sprite数量: {fishResourcesSpritesDic.Count}"); + Debug.Log($"[FishResourcesData] 鱼资源卸载完成"); + return Task.CompletedTask; } diff --git a/Assets/MyGame/aa/Game/FLZY/BOSS/LTZZ/4052.prefab b/Assets/MyGame/aa/Game/FLZY/BOSS/LTZZ/4052.prefab index fbbf8db11..26baf846d 100644 --- a/Assets/MyGame/aa/Game/FLZY/BOSS/LTZZ/4052.prefab +++ b/Assets/MyGame/aa/Game/FLZY/BOSS/LTZZ/4052.prefab @@ -11994,7 +11994,7 @@ MonoBehaviour: drawLineSegments: 60 drawLineColor: {r: 1, g: 0.92156863, b: 0.015686275, a: 1} bossName: - isToUI: 1 + isToUI: 0 tMP_CurrBossScore: {fileID: 0} currBossScoreValue: 0 bossScoreValue: 0 diff --git a/Assets/MyGame/aa/Game/FLZY/BOSS/SLZP/1507.prefab b/Assets/MyGame/aa/Game/FLZY/BOSS/SLZP/1507.prefab index 776145fd1..7337d88ab 100644 --- a/Assets/MyGame/aa/Game/FLZY/BOSS/SLZP/1507.prefab +++ b/Assets/MyGame/aa/Game/FLZY/BOSS/SLZP/1507.prefab @@ -1035,6 +1035,11 @@ PrefabInstance: propertyPath: m_Layer value: 6 objectReference: {fileID: 0} + - target: {fileID: 1758318735507541674, guid: f11065bf68488f64e9d9b8acc8c4caff, + type: 3} + propertyPath: m_TagString + value: Fish + objectReference: {fileID: 0} - target: {fileID: 1848564584791591582, guid: f11065bf68488f64e9d9b8acc8c4caff, type: 3} propertyPath: m_Layer diff --git a/Assets/MyGame/aa/Game/FLZY/BOSS/SSQL/7005.prefab b/Assets/MyGame/aa/Game/FLZY/BOSS/SSQL/7005.prefab index 12dd9ae79..a0dc8bf9a 100644 --- a/Assets/MyGame/aa/Game/FLZY/BOSS/SSQL/7005.prefab +++ b/Assets/MyGame/aa/Game/FLZY/BOSS/SSQL/7005.prefab @@ -14838,7 +14838,7 @@ MonoBehaviour: drawLineSegments: 60 drawLineColor: {r: 1, g: 0.92156863, b: 0.015686275, a: 1} bossName: - isToUI: 1 + isToUI: 0 tMP_CurrBossScore: {fileID: 0} currBossScoreValue: 0 bossScoreValue: 0 diff --git a/Assets/MyGame/aa/Game/XJMH/Boss/XingYunFeiDie/RotatePan_XJMH.cs b/Assets/MyGame/aa/Game/XJMH/Boss/XingYunFeiDie/RotatePan_XJMH.cs index 5ea6aa603..1e99942f8 100644 --- a/Assets/MyGame/aa/Game/XJMH/Boss/XingYunFeiDie/RotatePan_XJMH.cs +++ b/Assets/MyGame/aa/Game/XJMH/Boss/XingYunFeiDie/RotatePan_XJMH.cs @@ -192,7 +192,8 @@ public class RotatePan_XJMH : BJRedPacketEvent // 内圈:可在选项范围内随机偏移(中心角度 ± 半角) float neiCenterAngle = OptionIdToAngle((int)_innerOpt.optionId, innerCount); float neiHalfAngle = (360f / innerCount) / 2f; - float neiTargetAngle = neiCenterAngle + Random.Range(-neiHalfAngle, neiHalfAngle); + //float neiTargetAngle = neiCenterAngle + Random.Range(-neiHalfAngle, neiHalfAngle); + float neiTargetAngle = neiCenterAngle; Debug.Log($"[XingYunFeiDie] 内圈目标角度: OptionId={_innerOpt.optionId} count={innerCount} 中心={neiCenterAngle}° 随机后={neiTargetAngle}°"); // 先转内圈 diff --git a/Assets/MyGame/aa/UI/DefaultPanel/592228afbce16145.asset b/Assets/MyGame/aa/UI/DefaultPanel/592228afbce16145.asset new file mode 100644 index 000000000..172b7f621 --- /dev/null +++ b/Assets/MyGame/aa/UI/DefaultPanel/592228afbce16145.asset @@ -0,0 +1,108 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 84a92b25f83d49b9bc132d206b370281, type: 3} + m_Name: 592228afbce16145 + m_EditorClassIdentifier: + hashCode: -1919871374 + material: {fileID: 8548082164682178837} + materialHashCode: 0 + m_Version: 1.1.0 + m_FaceInfo: + m_FaceIndex: 0 + m_FamilyName: + m_StyleName: + m_PointSize: 0 + m_Scale: 0 + m_UnitsPerEM: 0 + m_LineHeight: 0 + m_AscentLine: 0 + m_CapLine: 0 + m_MeanLine: 0 + m_Baseline: 0 + m_DescentLine: 0 + m_SuperscriptOffset: 0 + m_SuperscriptSize: 0 + m_SubscriptOffset: 0 + m_SubscriptSize: 0 + m_UnderlineOffset: 0 + m_UnderlineThickness: 0 + m_StrikethroughOffset: 0 + m_StrikethroughThickness: 0 + m_TabWidth: 0 + spriteSheet: {fileID: 2800000, guid: 1b70eadb887fad44abd4ece91ee38a6d, type: 3} + m_SpriteCharacterTable: + - m_ElementType: 2 + m_Unicode: 65534 + m_GlyphIndex: 0 + m_Scale: 1 + m_Name: 592228afbce16145 + m_HashCode: -1919871374 + m_SpriteGlyphTable: + - m_Index: 0 + m_Metrics: + m_Width: 127 + m_Height: 136 + m_HorizontalBearingX: -63.5 + m_HorizontalBearingY: 117.5 + m_HorizontalAdvance: 127 + m_GlyphRect: + m_X: 0 + m_Y: 0 + m_Width: 127 + m_Height: 136 + m_Scale: 1 + m_AtlasIndex: 0 + m_ClassDefinitionType: 0 + sprite: {fileID: 21300000, guid: 1b70eadb887fad44abd4ece91ee38a6d, type: 3} + spriteInfoList: [] + fallbackSpriteAssets: [] +--- !u!21 &8548082164682178837 +Material: + serializedVersion: 8 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: TextMeshPro/Sprite + m_Shader: {fileID: 4800000, guid: cf81c85f95fe47e1a27f6ae460cf182c, type: 3} + m_Parent: {fileID: 0} + m_ModifiedSerializedProperties: 0 + m_ValidKeywords: [] + m_InvalidKeywords: [] + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_LockedProperties: + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _MainTex: + m_Texture: {fileID: 2800000, guid: 1b70eadb887fad44abd4ece91ee38a6d, type: 3} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _ColorMask: 15 + - _CullMode: 0 + - _Stencil: 0 + - _StencilComp: 8 + - _StencilOp: 0 + - _StencilReadMask: 255 + - _StencilWriteMask: 255 + - _UseUIAlphaClip: 0 + m_Colors: + - _ClipRect: {r: -32767, g: -32767, b: 32767, a: 32767} + - _Color: {r: 1, g: 1, b: 1, a: 1} + m_BuildTextureStacks: [] diff --git a/Assets/MyGame/aa/UI/DefaultPanel/592228afbce16145.asset.meta b/Assets/MyGame/aa/UI/DefaultPanel/592228afbce16145.asset.meta new file mode 100644 index 000000000..6dd10bb6a --- /dev/null +++ b/Assets/MyGame/aa/UI/DefaultPanel/592228afbce16145.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ca36565f9fa2172499feaedffabf3ee8 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/MyGame/aa/UI/DefaultPanel/592228afbce16145.png b/Assets/MyGame/aa/UI/DefaultPanel/592228afbce16145.png new file mode 100644 index 0000000000000000000000000000000000000000..4527873c17d2e1931ac47bd62f98212c15fe1b66 GIT binary patch literal 9056 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 Y{J*dCR1^O1IRP-zGt({Cz8m{L0DO5KMF0Q* literal 0 HcmV?d00001 diff --git a/Assets/MyGame/aa/UI/DefaultPanel/592228afbce16145.png.meta b/Assets/MyGame/aa/UI/DefaultPanel/592228afbce16145.png.meta new file mode 100644 index 000000000..804b00726 --- /dev/null +++ b/Assets/MyGame/aa/UI/DefaultPanel/592228afbce16145.png.meta @@ -0,0 +1,153 @@ +fileFormatVersion: 2 +guid: 1b70eadb887fad44abd4ece91ee38a6d +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 13 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: iPhone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: WebGL + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/MyGame/aa/UI/DefaultPanel/BossBoardCarstGongGao.prefab b/Assets/MyGame/aa/UI/DefaultPanel/BossBoardCarstGongGao.prefab index 59c238f35..72a28c7b4 100644 --- a/Assets/MyGame/aa/UI/DefaultPanel/BossBoardCarstGongGao.prefab +++ b/Assets/MyGame/aa/UI/DefaultPanel/BossBoardCarstGongGao.prefab @@ -161,7 +161,7 @@ MonoBehaviour: bottomLeft: {r: 1, g: 1, b: 1, a: 1} bottomRight: {r: 1, g: 1, b: 1, a: 1} m_fontColorGradientPreset: {fileID: 0} - m_spriteAsset: {fileID: 0} + m_spriteAsset: {fileID: 11400000, guid: ca36565f9fa2172499feaedffabf3ee8, type: 2} m_tintAllSprites: 0 m_StyleSheet: {fileID: 0} m_TextStyleHashCode: -1183493901 diff --git a/Assets/MyGame/aa/UI/Game/GameMainPanel_FishBase.cs b/Assets/MyGame/aa/UI/Game/GameMainPanel_FishBase.cs index a39f63d65..b27c80cfe 100644 --- a/Assets/MyGame/aa/UI/Game/GameMainPanel_FishBase.cs +++ b/Assets/MyGame/aa/UI/Game/GameMainPanel_FishBase.cs @@ -103,6 +103,7 @@ using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.EventSystems; using UnityEngine.InputSystem; +using UnityEngine.ResourceManagement.AsyncOperations; using UnityEngine.Playables; using UnityEngine.SceneManagement; using UnityEngine.UI; @@ -699,23 +700,38 @@ public class GameMainPanel_FishBase : GameMainPanel_Base }); // ⭐ 大喇叭:开/关弹幕输入面板 + // ⭐ 绑定在 OnShow 里:先 RemoveAllListeners 再 Add,防止面板重复 OnShow 叠加监听器 + // (叠加后点一次发送 = 发 N 次 API = 多扣 N 个道具) if (dalaba != null) + { + dalaba.onClick.RemoveAllListeners(); dalaba.onClick.AddListener(() => { if (obj_DaLaBaPanel != null) obj_DaLaBaPanel.SetActive(!obj_DaLaBaPanel.activeSelf); }); + } if (btn_DaLaBaSend != null) + { + btn_DaLaBaSend.onClick.RemoveAllListeners(); btn_DaLaBaSend.onClick.AddListener(OnClickDaLaBaSend); + } if (btn_DaLaBaClose != null) + { + btn_DaLaBaClose.onClick.RemoveAllListeners(); btn_DaLaBaClose.onClick.AddListener(() => { - if (obj_DaLaBaPanel != null) obj_DaLaBaPanel.SetActive(false); + if (obj_DaLaBaPanel != null) + obj_DaLaBaPanel.SetActive(false); }); + } - // ⭐ 召唤卡:使用召唤卡召唤Boss + // ⭐ 召唤卡:使用召唤卡召唤Boss(同样防重复绑定) if (zhanhuanka != null) + { + zhanhuanka.onClick.RemoveAllListeners(); zhanhuanka.onClick.AddListener(OnClickZhanHuanKa); + } // 服务器广播监听 EventCenter.Instance.AddEventListener(ConstEventString.SyncGameData_Rotate, SyncGameData_Rotate); @@ -1253,13 +1269,21 @@ public class GameMainPanel_FishBase : GameMainPanel_Base await AudioResourcesData.LoadAudioGame(); if (flow != _resourceFlowToken) return; - await Addressables.LoadAssetsAsync("Audio", clip => - { - if (flow != _resourceFlowToken) return; // 残留流程不再写面板字段 - if (clip.name == "PW_Shoot") - bulletClip = clip; - }).Task; + // ⭐ 开火音效 PW_Shoot 在 Default Local Group(Audio+PTCBZS 双标签交集检索): + // 只命中 Default 小包,不把其它玩法的巨型音频包拉进内存;句柄由 LoadBulletClip 静态保存,离房释放 + await LoadBulletClip(); if (flow != _resourceFlowToken) return; + if (_bulletClipHandle.IsValid() && _bulletClipHandle.Result != null) + { + foreach (var clip in _bulletClipHandle.Result) + { + if (clip.name == "PW_Shoot") + { + bulletClip = clip; + break; + } + } + } SetLoadProgress(0.8f); await Task.Delay(100); @@ -1321,6 +1345,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base if (flow != _resourceFlowToken) return; // ⭐ 期间已有新流程(新面板加载),剩余卸载作废 await AudioResourcesData.UnLoadAudioGame(); if (flow != _resourceFlowToken) return; + UnLoadBulletClip(); // ⭐ 释放开火音效句柄(引用计数归零) await MonsterMoveLine.UnLoadBezier(); if (flow != _resourceFlowToken) return; // ⭐ 清空子弹池 + 特效池(鱼池已在 UnLoadFishAssetsData 内清理) @@ -1332,6 +1357,32 @@ public class GameMainPanel_FishBase : GameMainPanel_Base /// 资源是否已卸载(避免 OnHide 重复卸载) private bool _resourcesUnloaded = false; + /// + /// ⭐ 开火音效 PW_Shoot 的加载句柄(static:新老面板实例共享)。 + /// PW_Shoot 在 Default Local Group(Audio+PTCBZS),该包因默认音频常驻本就不会卸载, + /// 但句柄仍要显式保存/释放,避免每次进房引用计数 +1 无限累加。 + /// + private static AsyncOperationHandle> _bulletClipHandle; + + /// 加载开火音效(Audio+PTCBZS 交集,只命中 Default Local Group 里的 PW_Shoot) + private static async Task LoadBulletClip() + { + // 防御:上一轮句柄未释放时先释放 + if (_bulletClipHandle.IsValid()) + Addressables.Release(_bulletClipHandle); + + _bulletClipHandle = Addressables.LoadAssetsAsync(new List { "Audio", "PTCBZS" }, null, + Addressables.MergeMode.Intersection); + await _bulletClipHandle.Task; + } + + /// 释放开火音效句柄(离房卸载流程调用) + private static void UnLoadBulletClip() + { + if (_bulletClipHandle.IsValid()) + Addressables.Release(_bulletClipHandle); + } + /// 离房 socket 响应等待信号 private TaskCompletionSource _leaveRoomResponseTcs; /// 正在离开的游戏面板类型(完成后关闭它切到 home) @@ -1383,11 +1434,20 @@ public class GameMainPanel_FishBase : GameMainPanel_Base { if (JinDuTiao != null) JinDuTiao.Show(); + // ⭐ 先退房销毁场上活鱼,再卸载资源(与 UnLoadResources 顺序一致): + // 现在句柄真正会释放、包内资源(贴图/Spine)会被销毁, + // 若先卸载后销毁鱼,残留活鱼会在淡出期间丢失材质花屏 + CreateFishManager.Instance.ExitRoom(); + CreateFishManager.Instance.isResources = false; + BulletPool.Instance.ClearAll(); + EffectPool.Instance.ClearAll(); + SetUnloadProgress(0.2f); await FishResourcesData.UnLoadFishAssetsData(); SetUnloadProgress(0.4f); await AudioResourcesData.UnLoadAudioGame(); + UnLoadBulletClip(); // ⭐ 释放开火音效句柄(引用计数归零) SetUnloadProgress(0.6f); await MonsterMoveLine.UnLoadBezier(); @@ -1395,11 +1455,6 @@ public class GameMainPanel_FishBase : GameMainPanel_Base SetUnloadProgress(0.8f); await Task.Delay(100); - CreateFishManager.Instance.ExitRoom(); - CreateFishManager.Instance.isResources = false; - // ⭐ 清空子弹池 + 特效池(鱼池已在 UnLoadFishAssetsData 内清理) - BulletPool.Instance.ClearAll(); - EffectPool.Instance.ClearAll(); SetUnloadProgress(0.9f); // ⭐ 卡在 0.9 等待 socket 返回 LeaveRoomResult @@ -1862,8 +1917,13 @@ public class GameMainPanel_FishBase : GameMainPanel_Base /// 大喇叭发送:输入弹幕 → /api/Hall/SendBroadcast 全服广播 /// (与背包大喇叭 OnClickFaSong 流程一致),发送成功后清空输入框并关闭面板。 /// + /// 大喇叭发送中标志:防止回调返回前连点发送按钮导致多发多扣道具 + private bool _isSendingDaLaBa; + private void OnClickDaLaBaSend() { + // ⭐ 防重入:上次请求回调还没回来时连点直接忽略(多发一次 = 多扣一个大喇叭) + if (_isSendingDaLaBa) return; string msg = tmp_DaLaBaInput?.text?.Trim(); if (string.IsNullOrEmpty(msg)) { @@ -1871,8 +1931,13 @@ public class GameMainPanel_FishBase : GameMainPanel_Base return; } - APIRequestList.SendBroadcast("广播:" + msg, (result) => + _isSendingDaLaBa = true; + // ⭐ 显示格式:"【小*鱼】:内容"(前面加喇叭图标 TMP 标签;名字打码:首尾保留、中间一个 *) + var userName = GameManager.Instance.user?.BaseInfo?.userName; + if (string.IsNullOrEmpty(userName)) userName = "玩家"; + APIRequestList.SendBroadcast($"【{MaskBroadcastName(userName)}】:{msg}", (result) => { + _isSendingDaLaBa = false; // 发送成功:清空输入框并关闭面板(广播内容会通过 LoginBroadcast 全服弹幕显示) if (tmp_DaLaBaInput != null) tmp_DaLaBaInput.text = ""; if (obj_DaLaBaPanel != null) obj_DaLaBaPanel.SetActive(false); @@ -1887,6 +1952,18 @@ public class GameMainPanel_FishBase : GameMainPanel_Base }); } + /// + /// 玩家名打码(广播格式):保留首尾字符、中间一个 *("小金鱼"→"小*鱼"、"我是大帅哥"→"我*哥")。 + /// 与召唤卡提示的 MaskUserName(三个星)区分,广播用单星。 + /// + private static string MaskBroadcastName(string name) + { + if (string.IsNullOrEmpty(name)) return name; + name = name.Trim(); + if (name.Length <= 1) return name; + return name[0] + "*" + name[name.Length - 1]; + } + /// /// 使用召唤卡:从背包找到召唤卡道具(type=11)拿道具编号 /// → /api/SummonCard/use 召唤Boss(Boss鱼由服务端 OnFishSpawn 下发给房间所有人) diff --git a/Assets/MyGame/aa/UI/Home/Sub/UIHome_BY_BackPackPanel.cs b/Assets/MyGame/aa/UI/Home/Sub/UIHome_BY_BackPackPanel.cs index 949a01173..80daf5afa 100644 --- a/Assets/MyGame/aa/UI/Home/Sub/UIHome_BY_BackPackPanel.cs +++ b/Assets/MyGame/aa/UI/Home/Sub/UIHome_BY_BackPackPanel.cs @@ -72,7 +72,11 @@ public class UIHome_BY_BackPackPanel : SubUIBase if (DaLaBaPanel != null) DaLaBaPanel.SetActive(false); if (btn_FaSong != null) + { + // ⭐ 先清再绑:防止面板重复 Show 叠加监听器(点一次发 N 次 = 多扣 N 个大喇叭) + btn_FaSong.onClick.RemoveAllListeners(); btn_FaSong.onClick.AddListener(OnClickFaSong); + } // 数量选择面板(默认隐藏) if (useObj != null) useObj.SetActive(false); @@ -147,23 +151,51 @@ public class UIHome_BY_BackPackPanel : SubUIBase _curUseCountAction = null; } + /// 大喇叭发送中标志:防止回调返回前连点发送按钮导致多发多扣道具 + private bool _isSendingFaSong; + private void OnClickFaSong() { - string msg = "广播:"+ tMPText_DaLaBaLabel?.text?.Trim(); - if (string.IsNullOrEmpty(msg)) + // ⭐ 防重入:上次请求回调还没回来时连点直接忽略 + if (_isSendingFaSong) return; + // ⭐ 先取文本判空再拼前缀(原"广播:"+text 判空永远不成立,空内容也会发送扣道具) + string content = tMPText_DaLaBaLabel?.text?.Trim(); + if (string.IsNullOrEmpty(content)) { UIManager.Instance.ShowBoardError("请输入广播内容"); return; } - APIRequestList.SendBroadcast(msg, (result) => + _isSendingFaSong = true; + // ⭐ 显示格式:"【小*鱼】:内容"(前面加喇叭图标 TMP 标签;名字打码与游戏内大喇叭同格式) + var userName = GameManager.Instance.user?.BaseInfo?.userName; + if (string.IsNullOrEmpty(userName)) userName = "玩家"; + APIRequestList.SendBroadcast($"【{MaskBroadcastName(userName)}】:{content}", (result) => { - //UIManager.Instance.ShowBoardGongGao($"广播: {msg}"); + _isSendingFaSong = false; tMPText_DaLaBaLabel.text = ""; DaLaBaPanel.SetActive(false); + // ⭐ 发送成功=服务端已扣大喇叭:拉最新用户数据刷新背包数量 + APIRequestList.GetUser((user) => + { + if (user != null) + GameManager.Instance.UpdataUserData(user); + }); }); } + /// + /// 玩家名打码(广播格式):保留首尾字符、中间一个 *("小金鱼"→"小*鱼")。 + /// 与 GameMainPanel_FishBase.MaskBroadcastName 同格式(背包面板独立副本)。 + /// + private static string MaskBroadcastName(string name) + { + if (string.IsNullOrEmpty(name)) return name; + name = name.Trim(); + if (name.Length <= 1) return name; + return name[0] + "*" + name[name.Length - 1]; + } + private void OnClickConfirmChangeName() { string newName = tMPText_NewName?.text?.Trim();