2026-05-22 19:30:02 +08:00
|
|
|
|
using Newtonsoft.Json;
|
|
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
using UnityEngine.AddressableAssets;
|
2026-07-29 00:31:59 +08:00
|
|
|
|
using UnityEngine.ResourceManagement.AsyncOperations;
|
2026-05-22 19:30:02 +08:00
|
|
|
|
using static UnityEngine.AddressableAssets.Addressables;
|
|
|
|
|
|
|
2026-08-28 19:42:54 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 战神预制体用途:战神有两个预制体(展示/游戏内使用),名字相同,靠 Addressables 子标签区分。
|
|
|
|
|
|
/// - Display:Default 子标签(大厅/商城预览模型)
|
|
|
|
|
|
/// - GamePlay:ZSYX 子标签(座位上替代普通炮台的使用模型)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public enum ZhanShenUsage
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>展示用:大厅/商城预览模型(Default 子标签加载)</summary>
|
|
|
|
|
|
Display = 0,
|
|
|
|
|
|
/// <summary>游戏内用:座位上替代普通炮台的模型(ZSYX 子标签加载)</summary>
|
|
|
|
|
|
GamePlay = 1
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-22 19:30:02 +08:00
|
|
|
|
[Serializable]
|
|
|
|
|
|
public class TurretResourcesData
|
|
|
|
|
|
{
|
|
|
|
|
|
public int turretNumber;
|
2026-06-08 21:23:40 +08:00
|
|
|
|
public string turretName; // 中文名称(如"深海之眼")
|
|
|
|
|
|
public int turretType; // 1=普通炮台 2=战神 3=翅膀
|
|
|
|
|
|
public string prefabTurretResourcesPath; // 资源路径(prefab/spine/sprite 共用名)
|
|
|
|
|
|
public int animationType; // 1=spine 2=animator 3=animation
|
2026-07-12 22:30:37 +08:00
|
|
|
|
public int prefabType; // 预制体类型(战神分类:1=国色天香 2=西游传说 3=山海奇闻;炮台/翅膀=0)
|
2026-06-08 21:23:40 +08:00
|
|
|
|
public int attackAudioID; // 攻击音效 ID
|
|
|
|
|
|
public List<int> bulletID; // 子弹 ID 列表
|
2026-05-22 19:30:02 +08:00
|
|
|
|
|
2026-06-08 21:23:40 +08:00
|
|
|
|
// ---- Addressables 标签 ----
|
2026-08-28 19:42:54 +08:00
|
|
|
|
const string turret_AALabel = "PTCBZS"; // 炮台统一标签(主标签:炮台/翅膀/战神共用)
|
|
|
|
|
|
const string defaultSubLabel = "Default"; // 展示子标签(展示用预制体:普通炮台/翅膀/战神展示)
|
|
|
|
|
|
const string zhanShenGameSubLabel = "ZSYX"; // 战神游戏内子标签(战神的使用预制体,与展示同名靠标签区分)
|
2026-05-22 19:30:02 +08:00
|
|
|
|
|
2026-06-08 21:23:40 +08:00
|
|
|
|
// ---- 路径→ID 映射(用于 Addressables 加载时反查) ----
|
|
|
|
|
|
private static Dictionary<string, int> turretPrefabPathDic; // prefabPath → turretNumber
|
|
|
|
|
|
private static Dictionary<string, int> turretSpritePathDic; // spritePath → turretNumber
|
2026-05-22 19:30:02 +08:00
|
|
|
|
|
2026-06-08 21:23:40 +08:00
|
|
|
|
// ---- ID→数据/资源 映射 ----
|
|
|
|
|
|
public static Dictionary<int, TurretResourcesData> turretDataDic; // id → data
|
2026-08-28 19:42:54 +08:00
|
|
|
|
private static Dictionary<int, GameObject> turretPrefabDic; // id → 展示用预制体(Default 子标签)
|
|
|
|
|
|
private static Dictionary<int, GameObject> turretGamePrefabDic; // id → 战神游戏内使用预制体(ZSYX 子标签)
|
2026-06-08 21:23:40 +08:00
|
|
|
|
private static Dictionary<int, Sprite> turretSpriteDic; // id → sprite
|
2026-05-22 19:30:02 +08:00
|
|
|
|
|
2026-07-29 00:31:59 +08:00
|
|
|
|
// ---- 保留的 AA Handle(不释放,避免 Build 下资源被卸载)----
|
|
|
|
|
|
private static readonly List<AsyncOperationHandle> _loadedHandles = new List<AsyncOperationHandle>();
|
|
|
|
|
|
|
2026-05-22 19:30:02 +08:00
|
|
|
|
static TurretResourcesData()
|
|
|
|
|
|
{
|
|
|
|
|
|
Init();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-08 21:23:40 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 从 Resources/tbturretresources.json 加载炮台元数据。
|
|
|
|
|
|
/// 表里没有的 turretNumber,调用方用 TurretData 自身的 Inspector 默认值,不报错。
|
|
|
|
|
|
/// </summary>
|
2026-05-22 19:30:02 +08:00
|
|
|
|
async static void Init()
|
|
|
|
|
|
{
|
2026-06-08 21:23:40 +08:00
|
|
|
|
turretPrefabPathDic = new Dictionary<string, int>();
|
|
|
|
|
|
turretSpritePathDic = new Dictionary<string, int>();
|
|
|
|
|
|
turretDataDic = new Dictionary<int, TurretResourcesData>();
|
2026-05-22 19:30:02 +08:00
|
|
|
|
|
2026-06-08 21:23:40 +08:00
|
|
|
|
TextAsset jsonAsset = Resources.Load<TextAsset>("tbturretresources");
|
|
|
|
|
|
if (jsonAsset == null)
|
2026-05-22 19:30:02 +08:00
|
|
|
|
{
|
2026-06-08 21:23:40 +08:00
|
|
|
|
Debug.LogWarning("[TurretResourcesData] 资源表 tbturretresources.json 未找到,炮台数据字典为空,将使用 TurretData 默认值");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
var list = JsonConvert.DeserializeObject<List<TurretResourcesData>>(jsonAsset.text);
|
|
|
|
|
|
if (list == null)
|
2026-05-22 19:30:02 +08:00
|
|
|
|
{
|
2026-06-08 21:23:40 +08:00
|
|
|
|
Debug.LogWarning("[TurretResourcesData] 资源表解析为空,将使用 TurretData 默认值");
|
|
|
|
|
|
return;
|
2026-05-22 19:30:02 +08:00
|
|
|
|
}
|
2026-06-08 21:23:40 +08:00
|
|
|
|
|
|
|
|
|
|
foreach (var item in list)
|
|
|
|
|
|
{
|
2026-07-12 22:30:37 +08:00
|
|
|
|
// 战神(turretType==2)的 prefabTurretResourcesPath 可能为空,不跳过
|
|
|
|
|
|
// 用 turretNumber 构造默认路径,以便 Addressables 按名匹配
|
2026-06-08 21:23:40 +08:00
|
|
|
|
if (string.IsNullOrEmpty(item.prefabTurretResourcesPath))
|
2026-07-12 22:30:37 +08:00
|
|
|
|
{
|
|
|
|
|
|
if (item.turretType == 2)
|
|
|
|
|
|
item.prefabTurretResourcesPath = item.turretNumber.ToString();
|
|
|
|
|
|
else
|
|
|
|
|
|
continue; // 炮台/翅膀路径为空则跳过
|
|
|
|
|
|
}
|
2026-06-08 21:23:40 +08:00
|
|
|
|
|
|
|
|
|
|
turretPrefabPathDic[item.prefabTurretResourcesPath] = item.turretNumber;
|
|
|
|
|
|
turretSpritePathDic[item.prefabTurretResourcesPath] = item.turretNumber;
|
|
|
|
|
|
turretDataDic[item.turretNumber] = item;
|
|
|
|
|
|
|
2026-07-12 22:30:37 +08:00
|
|
|
|
Debug.Log($"[TurretResourcesData] 加载炮台: ID={item.turretNumber}, Path={item.prefabTurretResourcesPath}, Type={item.turretType}, PrefabType={item.prefabType}, BulletIDs=[{string.Join(",", item.bulletID)}]");
|
2026-06-08 21:23:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Debug.Log($"[TurretResourcesData] 初始化完成,共 {turretDataDic.Count} 个炮台配置");
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (System.Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError($"[TurretResourcesData] JSON 解析失败: {ex.Message},炮台数据字典为空,将使用 TurretData 默认值");
|
2026-05-22 19:30:02 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-08 21:23:40 +08:00
|
|
|
|
// ==================== 资源加载 ====================
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2026-08-28 19:42:54 +08:00
|
|
|
|
/// 加载炮台相关 Addressables 资源(两组标签交集):
|
|
|
|
|
|
/// - PT + Default:普通炮台/翅膀/战神展示预制体 → turretPrefabDic;
|
|
|
|
|
|
/// - PT + ZSYX:战神游戏内使用预制体 → turretGamePrefabDic(与展示同名,靠标签区分)。
|
2026-07-29 00:31:59 +08:00
|
|
|
|
/// 注意:Handle 必须保留,不能 Release,否则 Build 下资源会被立即卸载,
|
|
|
|
|
|
/// 字典中存的就是已销毁对象的引用(Editor 下因 AssetDatabase 模式不会暴露此问题)。
|
2026-06-08 21:23:40 +08:00
|
|
|
|
/// </summary>
|
2026-05-22 19:30:02 +08:00
|
|
|
|
public static async Task LoadTurretAssetsData()
|
|
|
|
|
|
{
|
|
|
|
|
|
turretPrefabDic = new Dictionary<int, GameObject>();
|
2026-08-28 19:42:54 +08:00
|
|
|
|
turretGamePrefabDic = new Dictionary<int, GameObject>();
|
2026-05-22 19:30:02 +08:00
|
|
|
|
turretSpriteDic = new Dictionary<int, Sprite>();
|
|
|
|
|
|
|
2026-07-29 00:31:59 +08:00
|
|
|
|
// 清理上一轮残留的 Handle
|
|
|
|
|
|
foreach (var h in _loadedHandles)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (h.IsValid()) Addressables.Release(h);
|
|
|
|
|
|
}
|
|
|
|
|
|
_loadedHandles.Clear();
|
|
|
|
|
|
|
2026-08-28 19:42:54 +08:00
|
|
|
|
// 展示用标签组:PT + Default(普通炮台/翅膀/战神展示预制体)
|
2026-06-08 21:23:40 +08:00
|
|
|
|
var labels = new List<string> { turret_AALabel, defaultSubLabel };
|
2026-08-28 19:42:54 +08:00
|
|
|
|
// 战神游戏内标签组:PT + ZSYX(战神的使用预制体,名字与展示相同)
|
|
|
|
|
|
var zsGameLabels = new List<string> { turret_AALabel, zhanShenGameSubLabel };
|
2026-05-22 19:30:02 +08:00
|
|
|
|
|
2026-06-08 21:23:40 +08:00
|
|
|
|
Debug.Log("[TurretResourcesData] 开始预下载炮台资源依赖 (PT + Default)...");
|
2026-05-22 19:30:02 +08:00
|
|
|
|
var depHandle = Addressables.DownloadDependenciesAsync(labels, MergeMode.Intersection);
|
|
|
|
|
|
await depHandle.Task;
|
2026-07-29 00:31:59 +08:00
|
|
|
|
Addressables.Release(depHandle); // 预下载 Handle 可以立即释放,只影响下载缓存
|
2026-06-08 21:23:40 +08:00
|
|
|
|
Debug.Log("[TurretResourcesData] 炮台依赖资源预下载完成!");
|
2026-05-22 19:30:02 +08:00
|
|
|
|
|
2026-08-28 19:42:54 +08:00
|
|
|
|
Debug.Log("[TurretResourcesData] 开始预下载战神游戏内资源依赖 (PT + ZSYX)...");
|
|
|
|
|
|
var zsDepHandle = Addressables.DownloadDependenciesAsync(zsGameLabels, MergeMode.Intersection);
|
|
|
|
|
|
await zsDepHandle.Task;
|
|
|
|
|
|
Addressables.Release(zsDepHandle);
|
|
|
|
|
|
Debug.Log("[TurretResourcesData] 战神游戏内依赖资源预下载完成!");
|
|
|
|
|
|
|
|
|
|
|
|
// ---- 加载炮台 Prefab(展示用)----
|
2026-06-08 21:23:40 +08:00
|
|
|
|
var prefabHandle = Addressables.LoadAssetsAsync<GameObject>(labels, prefab =>
|
2026-05-22 19:30:02 +08:00
|
|
|
|
{
|
2026-06-08 21:23:40 +08:00
|
|
|
|
if (turretPrefabPathDic.TryGetValue(prefab.name, out int id))
|
2026-05-22 19:30:02 +08:00
|
|
|
|
{
|
|
|
|
|
|
turretPrefabDic[id] = prefab;
|
2026-06-08 21:23:40 +08:00
|
|
|
|
Debug.Log($"[TurretResourcesData] 加载 Prefab: {prefab.name} (ID={id})");
|
2026-05-22 19:30:02 +08:00
|
|
|
|
}
|
|
|
|
|
|
}, MergeMode.Intersection);
|
|
|
|
|
|
|
|
|
|
|
|
await prefabHandle.Task;
|
2026-07-29 00:31:59 +08:00
|
|
|
|
// 不释放!保留 Handle 以保持资源存活
|
|
|
|
|
|
_loadedHandles.Add(prefabHandle);
|
2026-06-08 21:23:40 +08:00
|
|
|
|
Debug.Log($"[TurretResourcesData] 炮台 Prefab 加载完成,共 {turretPrefabDic.Count} 个");
|
|
|
|
|
|
|
2026-08-28 19:42:54 +08:00
|
|
|
|
// ---- 加载战神游戏内 Prefab(ZSYX 子标签;与展示同名,靠标签区分)----
|
|
|
|
|
|
var zsPrefabHandle = Addressables.LoadAssetsAsync<GameObject>(zsGameLabels, prefab =>
|
|
|
|
|
|
{
|
|
|
|
|
|
if (turretPrefabPathDic.TryGetValue(prefab.name, out int id)
|
|
|
|
|
|
&& turretDataDic.TryGetValue(id, out var data)
|
|
|
|
|
|
&& data.turretType == 2) // 只收战神(turretType==2)的游戏内使用预制体
|
|
|
|
|
|
{
|
|
|
|
|
|
turretGamePrefabDic[id] = prefab;
|
|
|
|
|
|
Debug.Log($"[TurretResourcesData] 加载战神游戏内 Prefab: {prefab.name} (ID={id})");
|
|
|
|
|
|
}
|
|
|
|
|
|
}, MergeMode.Intersection);
|
|
|
|
|
|
|
|
|
|
|
|
await zsPrefabHandle.Task;
|
|
|
|
|
|
// 不释放!保留 Handle 以保持资源存活
|
|
|
|
|
|
_loadedHandles.Add(zsPrefabHandle);
|
|
|
|
|
|
Debug.Log($"[TurretResourcesData] 战神游戏内 Prefab 加载完成,共 {turretGamePrefabDic.Count} 个");
|
|
|
|
|
|
|
2026-06-08 21:23:40 +08:00
|
|
|
|
// ---- 加载炮台 Sprite ----
|
|
|
|
|
|
var spriteHandle = Addressables.LoadAssetsAsync<Sprite>(labels, data =>
|
2026-05-22 19:30:02 +08:00
|
|
|
|
{
|
2026-06-08 21:23:40 +08:00
|
|
|
|
if (turretSpritePathDic.TryGetValue(data.name, out int id))
|
2026-05-22 19:30:02 +08:00
|
|
|
|
{
|
|
|
|
|
|
turretSpriteDic[id] = data;
|
2026-06-08 21:23:40 +08:00
|
|
|
|
Debug.Log($"[TurretResourcesData] 加载 Sprite: {data.name} (ID={id})");
|
2026-05-22 19:30:02 +08:00
|
|
|
|
}
|
|
|
|
|
|
}, MergeMode.Intersection);
|
|
|
|
|
|
|
|
|
|
|
|
await spriteHandle.Task;
|
2026-07-29 00:31:59 +08:00
|
|
|
|
// 不释放!保留 Handle 以保持资源存活
|
|
|
|
|
|
_loadedHandles.Add(spriteHandle);
|
2026-06-08 21:23:40 +08:00
|
|
|
|
Debug.Log($"[TurretResourcesData] 炮台 Sprite 加载完成,共 {turretSpriteDic.Count} 个");
|
2026-05-22 19:30:02 +08:00
|
|
|
|
|
2026-06-08 21:23:40 +08:00
|
|
|
|
Debug.Log("[TurretResourcesData] 所有炮台资源加载完毕!");
|
2026-05-22 19:30:02 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-08 21:23:40 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 卸载炮台 Addressables 资源。
|
2026-07-29 00:31:59 +08:00
|
|
|
|
/// 释放 LoadTurretAssetsData 中保留的所有 Handle。
|
2026-06-08 21:23:40 +08:00
|
|
|
|
/// </summary>
|
2026-07-29 00:31:59 +08:00
|
|
|
|
public static Task UnLoadTurretAssetsData()
|
2026-06-08 21:23:40 +08:00
|
|
|
|
{
|
2026-07-29 00:31:59 +08:00
|
|
|
|
foreach (var h in _loadedHandles)
|
2026-06-08 21:23:40 +08:00
|
|
|
|
{
|
2026-07-29 00:31:59 +08:00
|
|
|
|
if (h.IsValid())
|
2026-06-08 21:23:40 +08:00
|
|
|
|
{
|
2026-07-29 00:31:59 +08:00
|
|
|
|
Addressables.Release(h);
|
|
|
|
|
|
Debug.Log($"[TurretResourcesData] 释放 Handle: {h.DebugName}");
|
2026-06-08 21:23:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-07-29 00:31:59 +08:00
|
|
|
|
_loadedHandles.Clear();
|
2026-06-08 21:23:40 +08:00
|
|
|
|
|
2026-07-29 00:31:59 +08:00
|
|
|
|
turretPrefabDic?.Clear();
|
2026-08-28 19:42:54 +08:00
|
|
|
|
turretGamePrefabDic?.Clear();
|
2026-07-29 00:31:59 +08:00
|
|
|
|
turretSpriteDic?.Clear();
|
2026-06-08 21:23:40 +08:00
|
|
|
|
|
2026-08-28 19:42:54 +08:00
|
|
|
|
Debug.Log($"[TurretResourcesData] 卸载完成,剩余 Prefab: {turretPrefabDic?.Count ?? 0}, 战神游戏内: {turretGamePrefabDic?.Count ?? 0}, Sprite: {turretSpriteDic?.Count ?? 0}");
|
2026-07-29 00:31:59 +08:00
|
|
|
|
return Task.CompletedTask;
|
2026-06-08 21:23:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== 查询接口 ====================
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>根据 ID 获取炮台数据。</summary>
|
|
|
|
|
|
public static TurretResourcesData GetTurretData(int turretNumber)
|
|
|
|
|
|
{
|
|
|
|
|
|
return turretDataDic != null && turretDataDic.TryGetValue(turretNumber, out var data) ? data : null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>根据 ID 获取炮台 Prefab。</summary>
|
2026-05-22 19:30:02 +08:00
|
|
|
|
public static GameObject GetTurretPrefab(int turretNumber)
|
|
|
|
|
|
{
|
2026-06-08 21:23:40 +08:00
|
|
|
|
return turretPrefabDic != null && turretPrefabDic.TryGetValue(turretNumber, out var prefab) ? prefab : null;
|
2026-08-28 19:42:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 根据战神编号获取战神预制体(两种情况二选一)。
|
|
|
|
|
|
/// - Display:展示用(大厅/商城预览,与 GetTurretPrefab 返回同一个展示模型);
|
|
|
|
|
|
/// - GamePlay:游戏内用(座位上替代普通炮台的使用模型)。
|
|
|
|
|
|
/// 未加载、非战神或未配置使用预制体时返回 null。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public static GameObject GetZhanShenPrefab(int turretNumber, ZhanShenUsage usage = ZhanShenUsage.Display)
|
|
|
|
|
|
{
|
|
|
|
|
|
var dic = usage == ZhanShenUsage.GamePlay ? turretGamePrefabDic : turretPrefabDic;
|
|
|
|
|
|
return dic != null && dic.TryGetValue(turretNumber, out var prefab) ? prefab : null;
|
2026-05-22 19:30:02 +08:00
|
|
|
|
}
|
2026-06-08 21:23:40 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>根据 ID 获取炮台 Sprite。</summary>
|
2026-05-22 19:30:02 +08:00
|
|
|
|
public static Sprite GetTurretSprite(int turretNumber)
|
|
|
|
|
|
{
|
2026-06-08 21:23:40 +08:00
|
|
|
|
return turretSpriteDic != null && turretSpriteDic.TryGetValue(turretNumber, out var sprite) ? sprite : null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>获取所有炮台数据列表。</summary>
|
|
|
|
|
|
public static List<TurretResourcesData> GetAllTurretData()
|
|
|
|
|
|
{
|
|
|
|
|
|
return turretDataDic != null ? new List<TurretResourcesData>(turretDataDic.Values) : new List<TurretResourcesData>();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>根据类型获取炮台数据(1=普通炮台, 2=战神, 3=翅膀)。</summary>
|
|
|
|
|
|
public static List<TurretResourcesData> GetTurretDataByType(int turretType)
|
|
|
|
|
|
{
|
|
|
|
|
|
var result = new List<TurretResourcesData>();
|
|
|
|
|
|
if (turretDataDic == null) return result;
|
|
|
|
|
|
foreach (var kv in turretDataDic)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (kv.Value.turretType == turretType)
|
|
|
|
|
|
result.Add(kv.Value);
|
|
|
|
|
|
}
|
|
|
|
|
|
return result;
|
2026-05-22 19:30:02 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|