2026-07-24 01:37:11 +08:00
|
|
|
|
using Newtonsoft.Json;
|
2026-03-27 15:18:35 +08:00
|
|
|
|
using Spine.Unity;
|
2026-02-03 11:35:19 +08:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections;
|
|
|
|
|
|
using System.Collections.Generic;
|
2026-05-01 06:26:50 +08:00
|
|
|
|
using System.Threading.Tasks;
|
2026-02-03 11:35:19 +08:00
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
using UnityEngine.AddressableAssets;
|
2026-10-08 17:04:24 +08:00
|
|
|
|
using UnityEngine.ResourceManagement.AsyncOperations;
|
2026-03-27 15:18:35 +08:00
|
|
|
|
using static UnityEngine.AddressableAssets.Addressables;
|
2026-02-03 11:35:19 +08:00
|
|
|
|
|
|
|
|
|
|
[Serializable]
|
|
|
|
|
|
public class FishResourcesData
|
|
|
|
|
|
{
|
|
|
|
|
|
public int fishNumber;
|
|
|
|
|
|
public string prefabFishResourcesPath;
|
2026-03-27 15:18:35 +08:00
|
|
|
|
public string dieEffectAssets;
|
2026-04-28 01:53:41 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 出场音频
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public List<int> showAudio;
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 移动音频
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public List<int> moveAudio;
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 死亡音效 1,2,3
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public List<int> dieAudio;
|
|
|
|
|
|
/// <summary>
|
2026-04-29 01:18:39 +08:00
|
|
|
|
/// 爆奖音效
|
2026-04-28 01:53:41 +08:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
public Dictionary<string, int> dieGlodAduio;
|
|
|
|
|
|
|
2026-03-27 15:18:35 +08:00
|
|
|
|
const string prefabFish_AALabel="FishPrefab";
|
|
|
|
|
|
const string dieSpine_AALabel = "DieSpine";
|
2026-04-29 01:18:39 +08:00
|
|
|
|
const string Sprite_AALabel = "TuJian";
|
|
|
|
|
|
|
2026-02-03 11:35:19 +08:00
|
|
|
|
|
|
|
|
|
|
|
2026-03-27 15:18:35 +08:00
|
|
|
|
private static Dictionary<string,int> FishResourcesPrefabsDic ;//prefabpath--id
|
|
|
|
|
|
private static Dictionary<string,int> FishResourcesSpineDieDic ;//dieSpinepath--id
|
2026-04-29 01:18:39 +08:00
|
|
|
|
private static Dictionary<string,int> FishResourcesSpriteDic;//Spritepath--id
|
2026-05-01 06:26:50 +08:00
|
|
|
|
static FishResourcesData()
|
2026-02-03 11:35:19 +08:00
|
|
|
|
{
|
2026-05-01 06:26:50 +08:00
|
|
|
|
Init();
|
|
|
|
|
|
}
|
|
|
|
|
|
async static void Init()
|
|
|
|
|
|
{
|
|
|
|
|
|
|
2026-02-03 11:35:19 +08:00
|
|
|
|
string prefabFishPath = Resources.Load<TextAsset>("tbfishresources").text;
|
|
|
|
|
|
var v = JsonConvert.DeserializeObject<List<FishResourcesData>>(prefabFishPath);
|
2026-05-01 06:26:50 +08:00
|
|
|
|
FishResourcesPrefabsDic = new Dictionary<string, int>();
|
2026-03-27 15:18:35 +08:00
|
|
|
|
fishResourcesDatasDic = new Dictionary<int, FishResourcesData>();
|
|
|
|
|
|
FishResourcesSpineDieDic = new Dictionary<string, int>();
|
2026-04-29 01:18:39 +08:00
|
|
|
|
FishResourcesSpriteDic = new Dictionary<string, int>();
|
2026-02-03 11:35:19 +08:00
|
|
|
|
foreach (var item in v)
|
|
|
|
|
|
{
|
2026-05-01 06:26:50 +08:00
|
|
|
|
if (item.prefabFishResourcesPath != null && item.prefabFishResourcesPath != "")
|
2026-02-03 11:35:19 +08:00
|
|
|
|
{
|
|
|
|
|
|
Debug.Log($"添加鱼资源数据: {item.fishNumber} - {item.prefabFishResourcesPath}");
|
2026-03-27 15:18:35 +08:00
|
|
|
|
FishResourcesPrefabsDic.Add(item.prefabFishResourcesPath, item.fishNumber);
|
2026-04-29 01:18:39 +08:00
|
|
|
|
FishResourcesSpriteDic.Add(item.prefabFishResourcesPath, item.fishNumber);
|
2026-05-01 06:26:50 +08:00
|
|
|
|
if (item.dieEffectAssets != null && item.dieEffectAssets != "")
|
2026-03-27 15:18:35 +08:00
|
|
|
|
{
|
|
|
|
|
|
FishResourcesSpineDieDic.Add(item.dieEffectAssets, item.fishNumber);
|
|
|
|
|
|
}
|
|
|
|
|
|
fishResourcesDatasDic.Add(item.fishNumber, item);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-04-29 01:18:39 +08:00
|
|
|
|
|
2026-02-03 11:35:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-27 15:18:35 +08:00
|
|
|
|
private static Dictionary<int, GameObject> fishPrefabDic;//id--prefabs
|
|
|
|
|
|
public static Dictionary<int, FishResourcesData> fishResourcesDatasDic;//id--FishResourcesData
|
2026-04-29 01:18:39 +08:00
|
|
|
|
public static Dictionary<int, Sprite> fishResourcesSpritesDic;//id--Sprite
|
2026-03-27 15:18:35 +08:00
|
|
|
|
private static Dictionary<int, SkeletonDataAsset> diePrefabDic;//id--SkeletonDataAsset
|
2026-10-08 17:04:24 +08:00
|
|
|
|
|
|
|
|
|
|
// ⭐ 句柄显式管理(修复"包只增不减"的根源):
|
|
|
|
|
|
// 旧实现加载完立即 Release 句柄,但 fishPrefabDic 还持有 prefab 引用 ——
|
|
|
|
|
|
// 包没被卸载全靠 AudioResourcesData 泄漏的音频句柄"误打误撞"撑着(音频物理上就在鱼包里)。
|
|
|
|
|
|
// 现在:加载时保存句柄(prefab/dieSpine/sprite 三类),离房时 UnLoadFishAssetsData 统一 Release,
|
|
|
|
|
|
// 引用归零后鱼包真正从内存卸载 —— 依次进所有玩法不再累加内存。
|
|
|
|
|
|
private static readonly List<AsyncOperationHandle> _fishHandles = new List<AsyncOperationHandle>();
|
|
|
|
|
|
|
2026-10-09 20:20:14 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 加载当前玩法的鱼资源(prefab / 死亡Spine / 图鉴Sprite)。
|
|
|
|
|
|
/// onProgress:整体进度回调 0~1(prefab 0~0.62 / spine 0.62~0.81 / sprite 0.81~1,按各段实际 PercentComplete 细分),
|
|
|
|
|
|
/// 供进房进度条使用;不传则行为与原来一致。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public static async Task LoadFishAssetsData(Action<float> onProgress = null)
|
2026-10-12 15:32:36 +08:00
|
|
|
|
{
|
|
|
|
|
|
// ⭐ 资源加载超过 0.1 秒才显示"加载中"界面(引用计数,快速加载不显示)
|
|
|
|
|
|
UIManager.BeginLoading();
|
|
|
|
|
|
try { await LoadFishAssetsDataCore(onProgress); }
|
|
|
|
|
|
finally { UIManager.EndLoading(); }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>LoadFishAssetsData 的实际逻辑(外层负责"加载中"界面)</summary>
|
|
|
|
|
|
private static async Task LoadFishAssetsDataCore(Action<float> onProgress)
|
2026-02-03 11:35:19 +08:00
|
|
|
|
{
|
2026-05-18 18:39:11 +08:00
|
|
|
|
// 清空对象池
|
2026-04-29 23:53:55 +08:00
|
|
|
|
Pool.Instance.Clear();
|
2026-02-03 11:35:19 +08:00
|
|
|
|
|
2026-03-27 15:18:35 +08:00
|
|
|
|
fishPrefabDic = new Dictionary<int, GameObject>();
|
2026-05-01 06:26:50 +08:00
|
|
|
|
diePrefabDic = new Dictionary<int, SkeletonDataAsset>();
|
2026-04-29 01:18:39 +08:00
|
|
|
|
fishResourcesSpritesDic = new Dictionary<int, Sprite>();
|
|
|
|
|
|
|
2026-10-08 17:04:24 +08:00
|
|
|
|
// ⭐ 防御:上一轮句柄未释放(如离房流程被跳过)时先释放,避免引用计数累加
|
|
|
|
|
|
foreach (var h in _fishHandles)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (h.IsValid()) Addressables.Release(h);
|
|
|
|
|
|
}
|
|
|
|
|
|
_fishHandles.Clear();
|
|
|
|
|
|
|
2026-05-18 18:39:11 +08:00
|
|
|
|
// ---------------- 1. 预下载所有依赖资源(关键) ----------------
|
|
|
|
|
|
var labels = new List<string>
|
|
|
|
|
|
{
|
|
|
|
|
|
prefabFish_AALabel,
|
|
|
|
|
|
dieSpine_AALabel,
|
|
|
|
|
|
Sprite_AALabel,
|
|
|
|
|
|
GameManager.Instance.gameData.currGameType.ToString()
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
Debug.Log("开始预下载所有鱼资源依赖...");
|
|
|
|
|
|
var depHandle = Addressables.DownloadDependenciesAsync(labels, MergeMode.Intersection);
|
|
|
|
|
|
await depHandle.Task;
|
|
|
|
|
|
Addressables.Release(depHandle);
|
|
|
|
|
|
Debug.Log("依赖资源预下载完成!");
|
|
|
|
|
|
|
2026-10-09 20:20:14 +08:00
|
|
|
|
// ---------------- 2. 加载 Prefab(0 ~ 0.62,最重的一段) ----------------
|
2026-05-18 18:39:11 +08:00
|
|
|
|
var prefabKeys = new List<string> { prefabFish_AALabel, GameManager.Instance.gameData.currGameType.ToString() };
|
|
|
|
|
|
var prefabHandle = Addressables.LoadAssetsAsync<GameObject>(prefabKeys, prefab =>
|
2026-02-03 11:35:19 +08:00
|
|
|
|
{
|
2026-05-01 06:26:50 +08:00
|
|
|
|
if (FishResourcesPrefabsDic.TryGetValue(prefab.name, out int id))
|
2026-02-03 11:35:19 +08:00
|
|
|
|
{
|
2026-05-01 06:26:50 +08:00
|
|
|
|
fishPrefabDic[id] = prefab;
|
|
|
|
|
|
|
|
|
|
|
|
var mc = prefab.GetComponent<MonsterContral>();
|
|
|
|
|
|
if (mc != null)
|
|
|
|
|
|
mc.fishTypeID = id;
|
|
|
|
|
|
|
2026-07-27 16:59:22 +08:00
|
|
|
|
// ⭐ 统一用 Pool.Instance 的 poolObj 作为父节点(不再用 CreateFishManager.poolRoot),
|
|
|
|
|
|
// 这样 Pool.Clear() 销毁 poolObj 时能连带销毁所有 poolList_XXX 子节点,避免残留
|
|
|
|
|
|
Pool.Instance.dic[id] = new PoolData(prefab, Pool.Instance.EnsurePoolRoot(), id);
|
2026-02-03 11:35:19 +08:00
|
|
|
|
}
|
2026-05-01 06:26:50 +08:00
|
|
|
|
}, MergeMode.Intersection);
|
|
|
|
|
|
|
2026-10-09 20:20:14 +08:00
|
|
|
|
await AwaitWithProgress(prefabHandle, 0f, 0.62f, onProgress);
|
2026-10-08 17:04:24 +08:00
|
|
|
|
// ⭐ 不再立即 Release:保存句柄保持包存活,离房时统一卸载
|
|
|
|
|
|
_fishHandles.Add(prefabHandle);
|
2026-05-18 18:39:11 +08:00
|
|
|
|
Debug.Log($"Prefab 加载完成,共 {fishPrefabDic.Count} 个");
|
2026-05-01 06:26:50 +08:00
|
|
|
|
|
2026-10-09 20:20:14 +08:00
|
|
|
|
// ---------------- 3. 加载死亡 Spine 动效(0.62 ~ 0.81) ----------------
|
2026-07-24 01:37:11 +08:00
|
|
|
|
// 注意:某些游戏类型下 Addressables 中 "DieSpine"+"游戏类型" 两个 label 可能没有交集资源,
|
|
|
|
|
|
// 会抛 InvalidKeyException。这里用 try-catch 容错,避免单个资源类型加载失败阻塞整体流程。
|
|
|
|
|
|
try
|
2026-03-27 15:18:35 +08:00
|
|
|
|
{
|
2026-07-24 01:37:11 +08:00
|
|
|
|
var dieKeys = new List<string> { dieSpine_AALabel, GameManager.Instance.gameData.currGameType.ToString() };
|
|
|
|
|
|
var dieHandle = Addressables.LoadAssetsAsync<SkeletonDataAsset>(dieKeys, data =>
|
2026-03-27 15:18:35 +08:00
|
|
|
|
{
|
2026-07-24 01:37:11 +08:00
|
|
|
|
if (FishResourcesSpineDieDic.TryGetValue(data.name, out int id))
|
|
|
|
|
|
{
|
|
|
|
|
|
diePrefabDic[id] = data;
|
|
|
|
|
|
}
|
|
|
|
|
|
}, MergeMode.Intersection);
|
2026-05-01 06:26:50 +08:00
|
|
|
|
|
2026-10-09 20:20:14 +08:00
|
|
|
|
await AwaitWithProgress(dieHandle, 0.62f, 0.81f, onProgress);
|
2026-10-08 17:04:24 +08:00
|
|
|
|
_fishHandles.Add(dieHandle);
|
2026-07-24 01:37:11 +08:00
|
|
|
|
Debug.Log($"死亡 Spine 加载完成,共 {diePrefabDic.Count} 个");
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
|
{
|
2026-10-09 20:20:14 +08:00
|
|
|
|
onProgress?.Invoke(0.81f);
|
2026-07-24 01:37:11 +08:00
|
|
|
|
Debug.LogWarning($"[FishResourcesData] 死亡 Spine 加载失败(游戏类型={GameManager.Instance.gameData.currGameType}),跳过此项: {e.Message}");
|
|
|
|
|
|
}
|
2026-04-29 01:18:39 +08:00
|
|
|
|
|
2026-10-09 20:20:14 +08:00
|
|
|
|
// ---------------- 4. 加载鱼 Sprite(0.81 ~ 1) ----------------
|
2026-07-24 01:37:11 +08:00
|
|
|
|
try
|
2026-04-29 01:18:39 +08:00
|
|
|
|
{
|
2026-07-24 01:37:11 +08:00
|
|
|
|
var spriteKeys = new List<string> { Sprite_AALabel, GameManager.Instance.gameData.currGameType.ToString() };
|
|
|
|
|
|
var spriteHandle = Addressables.LoadAssetsAsync<Sprite>(spriteKeys, data =>
|
2026-04-29 01:18:39 +08:00
|
|
|
|
{
|
2026-07-24 01:37:11 +08:00
|
|
|
|
if (FishResourcesSpriteDic.TryGetValue(data.name, out int id))
|
|
|
|
|
|
{
|
|
|
|
|
|
fishResourcesSpritesDic[id] = data;
|
|
|
|
|
|
}
|
|
|
|
|
|
}, MergeMode.Intersection);
|
2026-05-01 06:26:50 +08:00
|
|
|
|
|
2026-10-09 20:20:14 +08:00
|
|
|
|
await AwaitWithProgress(spriteHandle, 0.81f, 1f, onProgress);
|
2026-10-08 17:04:24 +08:00
|
|
|
|
_fishHandles.Add(spriteHandle);
|
2026-07-24 01:37:11 +08:00
|
|
|
|
Debug.Log($"Sprite 加载完成,共 {fishResourcesSpritesDic.Count} 个");
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
|
{
|
2026-10-09 20:20:14 +08:00
|
|
|
|
onProgress?.Invoke(1f);
|
2026-07-24 01:37:11 +08:00
|
|
|
|
Debug.LogWarning($"[FishResourcesData] Sprite 加载失败(游戏类型={GameManager.Instance.gameData.currGameType}),跳过此项: {e.Message}");
|
|
|
|
|
|
}
|
2026-05-18 18:39:11 +08:00
|
|
|
|
|
2026-10-09 20:20:14 +08:00
|
|
|
|
onProgress?.Invoke(1f);
|
2026-05-18 18:39:11 +08:00
|
|
|
|
Debug.Log("所有鱼资源加载完毕!");
|
2026-02-03 11:35:19 +08:00
|
|
|
|
}
|
2026-05-01 06:26:50 +08:00
|
|
|
|
|
2026-10-09 20:20:14 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 等待句柄完成并按其 PercentComplete 映射到 [from, to] 区间上报进度。
|
|
|
|
|
|
/// 50ms 轮询(进度条 20fps 刷新足够),完成后兜底上报 to。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private static async Task AwaitWithProgress(AsyncOperationHandle handle, float from, float to, Action<float> onProgress)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (onProgress == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
await handle.Task;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
while (!handle.IsDone)
|
|
|
|
|
|
{
|
|
|
|
|
|
onProgress(Mathf.Lerp(from, to, handle.PercentComplete));
|
|
|
|
|
|
await Task.Delay(50);
|
|
|
|
|
|
}
|
|
|
|
|
|
onProgress(to);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-01 06:26:50 +08:00
|
|
|
|
|
2026-10-08 17:04:24 +08:00
|
|
|
|
public static Task UnLoadFishAssetsData()
|
2026-05-01 06:26:50 +08:00
|
|
|
|
{
|
2026-10-08 17:04:24 +08:00
|
|
|
|
// ⭐ 真正的卸载:释放加载时保存的全部句柄(引用归零后鱼包才会真正卸载)。
|
|
|
|
|
|
// 旧实现"重新 Load 一遍拿句柄再 Release"是净 0 操作,什么都卸不掉。
|
2026-05-01 06:26:50 +08:00
|
|
|
|
Pool.Instance.Clear();
|
|
|
|
|
|
|
2026-10-08 17:04:24 +08:00
|
|
|
|
foreach (var h in _fishHandles)
|
2026-05-01 06:26:50 +08:00
|
|
|
|
{
|
2026-10-08 17:04:24 +08:00
|
|
|
|
if (h.IsValid())
|
|
|
|
|
|
Addressables.Release(h);
|
2026-05-01 06:26:50 +08:00
|
|
|
|
}
|
2026-10-08 17:04:24 +08:00
|
|
|
|
_fishHandles.Clear();
|
2026-05-01 06:26:50 +08:00
|
|
|
|
|
2026-10-08 17:04:24 +08:00
|
|
|
|
// ⭐ 清空字典(包卸载后对象已失效,残留引用会指向已销毁资源)
|
|
|
|
|
|
fishPrefabDic?.Clear();
|
|
|
|
|
|
diePrefabDic?.Clear();
|
|
|
|
|
|
fishResourcesSpritesDic?.Clear();
|
2026-05-01 06:26:50 +08:00
|
|
|
|
|
2026-10-08 17:04:24 +08:00
|
|
|
|
Debug.Log($"[FishResourcesData] 鱼资源卸载完成");
|
|
|
|
|
|
return Task.CompletedTask;
|
2026-05-01 06:26:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-03-27 15:18:35 +08:00
|
|
|
|
public static GameObject GetFishPrefab(int fishNumber)
|
2026-02-03 11:35:19 +08:00
|
|
|
|
{
|
|
|
|
|
|
if (fishPrefabDic != null && fishPrefabDic.ContainsKey(fishNumber))
|
|
|
|
|
|
{
|
|
|
|
|
|
return fishPrefabDic[fishNumber];
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError($"鱼Prefab未找到: {fishNumber}");
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-27 15:18:35 +08:00
|
|
|
|
public static SkeletonDataAsset GetFishDieSpine(int fishNumber)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (diePrefabDic != null && diePrefabDic.ContainsKey(fishNumber))
|
|
|
|
|
|
{
|
|
|
|
|
|
return diePrefabDic[fishNumber];
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError($"鱼死亡动效未找到: {fishNumber}");
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-02-03 11:35:19 +08:00
|
|
|
|
|
|
|
|
|
|
}
|