using IVDataFormat; using Spine; using Spine.Unity.AttachmentTools; using System.Collections.Generic; using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.ResourceManagement.AsyncOperations; using UnityEngine.ResourceManagement.ResourceLocations; using UnityEngine.U2D; public class AddressableMgr { #region Path // Resources, Adressable 등에서 사용하는 경로 모음. private const string PathCosCloth = "coscloth/"; private const string PathCosWeapon = "cosweapon/"; public const string PathMon = "mon/{0}/{0}_SkeletonData.asset"; public const string PathBg = "bg/{0}"; public const string PathBgEffect = "bgefc/{0}"; public const string PathBgm = "bgm/{0}"; public const string PathBgmClip = "bgm/ac{0}.wav"; public const string PathPet = "pet/{0}/{0}_SkeletonData.asset"; public const string PathGuardian = "spine/Guardian/{0}/{0}_SkeletonData.asset"; private const string PBagWeapon = "wp"; private const string PBagCape = "cape"; private const string PBagHat = "hat"; private const string PBagShoes = "shoes"; private const string PBagEar = "ear"; private const string PBagNeck = "neck"; private const string PBagRing = "ring"; private const string PBagTreasure = "trs"; public const string PCosCloth = "c"; public const string PCosClothBase = "c1"; public const string PCosWeapon = "w"; public const string PCosWeaponBase = "w2"; public const string PPetInS = "in_s"; public const string PPetInL = "in_l"; public const string PPetOutlineS = "outline_s"; public const string PPetOutlineL = "outline_l"; #endregion Path #region Init private static bool bInit = false; // [기본] atlasBagIcon, atlasCosIcon, atlasEnhanceIcon, atlasPetIcon, atlasPlayerIcon, atlasSkillIcon, goodsicon + // [나머지] BASE, BG, BGM, CLOTH, IMG, EFFECT, EVENT, MON, PET, SPINE, WEAPON, AREA // 기본 7개 나머지 12개. 어드레서블 추가시 +1 public static int I_LoadingCnt { get; private set; } = 19; public static void Init() { if (bInit) return; bInit = true; // 게임 전체에서 일반적으로 사용하는 리소스는 미리 다운로드. // 추후 일부 항목은 별도 다운로드로 분리. Addressables.DownloadDependenciesAsync("BASE").Completed += ADownBaseComp; Addressables.DownloadDependenciesAsync("BG").Completed += ADownEtcComp; Addressables.DownloadDependenciesAsync("BGM").Completed += ADownEtcComp; Addressables.DownloadDependenciesAsync("CLOTH").Completed += ADownEtcComp; Addressables.DownloadDependenciesAsync("EFFECT").Completed += ADownEtcComp; Addressables.DownloadDependenciesAsync("EVENT").Completed += ADownEtcComp; Addressables.DownloadDependenciesAsync("IMG").Completed += ADownEtcComp; Addressables.DownloadDependenciesAsync("AREA").Completed += ADownEtcComp; Addressables.DownloadDependenciesAsync("MON").Completed += ADownEtcComp; Addressables.DownloadDependenciesAsync("PET").Completed += ADownEtcComp; Addressables.DownloadDependenciesAsync("SPINE").Completed += ADownEtcComp; Addressables.DownloadDependenciesAsync("WEAPON").Completed += ADownEtcComp; } public static bool AssetExists(object key) { if (Application.isPlaying) { foreach (var l in Addressables.ResourceLocators) { IList locs; if (l.Locate(key, null, out locs)) return true; } return false; } else if (Application.isEditor && !Application.isPlaying) { #if UNITY_EDITOR // note: my keys are always asset file paths return System.IO.File.Exists(System.IO.Path.Combine(Application.dataPath, (string)key)); #endif } return false; } public static bool LoadAssetAsync(object key, out AsyncOperationHandle op) { op = default; if(bInit) { op = Addressables.LoadAssetAsync(key); } return op.IsValid(); } public static void ReleaseAsset(T loadedObj) => Addressables.Release(loadedObj); private static void ADownBaseComp(AsyncOperationHandle obj) { Addressables.LoadAssetAsync("atlasBagIcon").Completed += AInitBagIcon; Addressables.LoadAssetAsync("atlasCosIcon").Completed += AInitCosIcon; Addressables.LoadAssetAsync("atlasEnhanceIcon").Completed += AInitEnhanceIcon; Addressables.LoadAssetAsync("atlasPetIcon").Completed += AInitPetIcon; Addressables.LoadAssetAsync("atlasPlayerIcon").Completed += AInitPlayerIcon; Addressables.LoadAssetAsync("atlasWeaponIcon").Completed += AInitWeaponIcon; Addressables.LoadAssetAsync("atlasSkillIcon").Completed += AInitSkillIcon; Addressables.LoadAssetAsync("atlasGradeIcon").Completed += AInitGradeIcon; Addressables.LoadAssetAsync("atlasGradeLatterIcon").Completed += AInitGradeLatterIcon; Addressables.LoadAssetAsync("atlasRelicIcon").Completed += AInitRelicIcon; Addressables.LoadAssetAsync("atlasDungeonPanel").Completed += AInitDungeonPanel; Addressables.LoadAssetAsync("goodsicon").Completed += AInitGoodsIcon; Addressables.Release(obj); I_LoadingCnt--; } private static void ADownEtcComp(AsyncOperationHandle obj) { Addressables.Release(obj); I_LoadingCnt--; } #endregion Init #region General Icon public static Sprite GetIcon(int type, int code) { switch (type) { case cGoods.TCurrency: return GetGoodsIcon(code); case cGoods.TBagWeapon: { var gear = DataHandler.GetGearEquip(type, code); return GetBagIcon(FormatString.CombineAllString(PBagWeapon, gear.rarity, "_", gear.grade)); } case cGoods.TBagArmorCape: { var gear = DataHandler.GetGearEquip(type, code); return GetBagIcon(FormatString.CombineAllString(PBagCape, gear.rarity, "_", gear.grade)); } case cGoods.TBagArmorHat: { var gear = DataHandler.GetGearEquip(type, code); return GetBagIcon(FormatString.CombineAllString(PBagHat, gear.rarity, "_", gear.grade)); } case cGoods.TBagArmorShoes: { var gear = DataHandler.GetGearEquip(type, code); return GetBagIcon(FormatString.CombineAllString(PBagShoes, gear.rarity, "_", gear.grade)); } case cGoods.TBagAcceEar: { var gear = DataHandler.GetGearEquip(type, code); return GetBagIcon(FormatString.CombineAllString(PBagEar, gear.rarity, "_", gear.grade)); } case cGoods.TBagAcceNeck: { var gear = DataHandler.GetGearEquip(type, code); return GetBagIcon(FormatString.CombineAllString(PBagNeck, gear.rarity, "_", gear.grade)); } case cGoods.TBagAcceRing: { var gear = DataHandler.GetGearEquip(type, code); return GetBagIcon(FormatString.CombineAllString(PBagRing, gear.rarity, "_", gear.grade)); } case cGoods.TBagTreasure: return GetBagIcon(FormatString.CombineAllString(PBagTreasure, code.ToString())); case cGoods.TSkillActive: return GetSkillActiveIcon(code); case cGoods.TSkillPassive: return GetSkillPassiveIcon(code); case cGoods.TCosCloth: return GetCosClothIcon(code); case cGoods.TCosWeapon: return GetCosWeaponIcon(code); case cGoods.TCosClrHair: case cGoods.TCosClrTop: case cGoods.TCosClrEye: return null; case cGoods.TPet: return GetPetIcon(code); case cGoods.TPetSpirit: if (code < 0) return GetGoodsIcon(1000000 + type); return GetPetIcon(code); case cGoods.TBox: if (code < 0) return GetGoodsIcon(1000000 + type); return GetBagIcon(DataHandler.SysBoxes[code].path); case cGoods.TProfileIcon: return GetPlayerIcon(code); } return GetGoodsIcon(1000000 + type); } public static Sprite GetIcon(int itype, int icode, int irarity) { switch (itype) { case cGoods.TCurrency: return GetGoodsIcon(icode); case cGoods.TBagWeapon: return GetBagIcon(FormatString.CombineAllString(PBagWeapon, irarity.ToString())); case cGoods.TBagArmorCape: return GetBagIcon(FormatString.CombineAllString(PBagCape, irarity.ToString())); case cGoods.TBagArmorHat: return GetBagIcon(FormatString.CombineAllString(PBagHat, irarity.ToString())); case cGoods.TBagArmorShoes: return GetBagIcon(FormatString.CombineAllString(PBagShoes, irarity.ToString())); case cGoods.TBagAcceEar: return GetBagIcon(FormatString.CombineAllString(PBagEar, irarity.ToString())); case cGoods.TBagAcceNeck: return GetBagIcon(FormatString.CombineAllString(PBagNeck, irarity.ToString())); case cGoods.TBagAcceRing: return GetBagIcon(FormatString.CombineAllString(PBagRing, irarity.ToString())); case cGoods.TBagTreasure: return GetBagIcon(FormatString.CombineAllString(PBagTreasure, icode.ToString())); case cGoods.TSkillActive: return GetSkillActiveIcon(icode); case cGoods.TSkillPassive: return GetSkillPassiveIcon(icode); case cGoods.TCosCloth: return GetCosClothIcon(icode); case cGoods.TCosWeapon: return GetCosWeaponIcon(icode); case cGoods.TCosClrHair: case cGoods.TCosClrTop: case cGoods.TCosClrEye: return null; case cGoods.TPet: return GetPetIcon(icode); case cGoods.TPetSpirit: if (icode < 0) return GetGoodsIcon(1000000 + itype); return GetPetIcon(icode); case cGoods.TBox: if (icode < 0) return GetGoodsIcon(1000000 + itype); return GetBagIcon(DataHandler.SysBoxes[icode].path); } return GetGoodsIcon(1000000 + itype); } #endregion General Icon #region Goods Icon public static AsyncOperationHandle handleGoodsIcons; public static StoIntSpr goodsIcons = null; private static void AInitGoodsIcon(AsyncOperationHandle obj) { goodsIcons = obj.Result; handleGoodsIcons = obj; I_LoadingCnt--; } public static Sprite GetGoodsIcon(int key) { #if UNITY_EDITOR if (I_LoadingCnt > 0) return null; #endif if (key < 0) return null; if (goodsIcons.datas.ContainsKey(key)) return goodsIcons.datas[key]; return null; } #endregion Goods Icon #region Bag Icon public static AsyncOperationHandle handleBagIcons; public static SpriteAtlas bagIcons = null; private static void AInitBagIcon(AsyncOperationHandle obj) { bagIcons = obj.Result; handleBagIcons = obj; I_LoadingCnt--; } public static Sprite GetBagIcon(string strpath) { #if UNITY_EDITOR if (I_LoadingCnt > 0) return null; #endif return bagIcons.GetSprite(strpath); } #endregion Bag Icon #region Pet Icon public static AsyncOperationHandle handlePetIcons; public static SpriteAtlas petIcons = null; private static void AInitPetIcon(AsyncOperationHandle obj) { petIcons = obj.Result; handlePetIcons = obj; I_LoadingCnt--; } public static Sprite GetPetIcon(int key) { #if UNITY_EDITOR if (I_LoadingCnt > 0) return null; #endif return petIcons.GetSprite(key.ToString()); } #endregion Pet Icon #region GradeIcon public static AsyncOperationHandle handleGradeIcons; public static AsyncOperationHandle handleGradeLatterIcons; public static SpriteAtlas GradeIcons = null; public static SpriteAtlas GradeLatterIcons = null; private static void AInitGradeIcon(AsyncOperationHandle obj) { GradeIcons = obj.Result; handleGradeIcons = obj; I_LoadingCnt--; } public static Sprite GetGradeIcon(int key) { #if UNITY_EDITOR if (I_LoadingCnt > 0) return null; #endif return GradeIcons.GetSprite(key.ToString()); } private static void AInitGradeLatterIcon(AsyncOperationHandle obj) { GradeLatterIcons = obj.Result; handleGradeLatterIcons = obj; I_LoadingCnt--; } public static Sprite GetGradeLatterIcon(int key) { #if UNITY_EDITOR if (I_LoadingCnt > 0) return null; #endif return GradeLatterIcons.GetSprite(key.ToString()); } #endregion #region Costume Icon public static AsyncOperationHandle handleCosIcons; public static SpriteAtlas cosIcons = null; private static void AInitCosIcon(AsyncOperationHandle obj) { cosIcons = obj.Result; handleCosIcons = obj; I_LoadingCnt--; } public static Sprite GetCosClothIcon(int key) { #if UNITY_EDITOR if (I_LoadingCnt > 0) return null; #endif return cosIcons.GetSprite(FormatString.CombineAllString(PCosCloth, key.ToString())); } public static Sprite GetCosWeaponIcon(int key) { #if UNITY_EDITOR if (I_LoadingCnt > 0) return null; #endif return cosIcons.GetSprite(FormatString.CombineAllString(PCosWeapon, key.ToString())); } #endregion Costume Icon #region Enhance Icon public static AsyncOperationHandle handleEnhanceIcons; public static SpriteAtlas enhanceIcons = null; private static void AInitEnhanceIcon(AsyncOperationHandle obj) { enhanceIcons = obj.Result; handleEnhanceIcons = obj; I_LoadingCnt--; } public static Sprite GetEnhanceIcon(int key) { #if UNITY_EDITOR if (I_LoadingCnt > 0) return null; #endif return enhanceIcons.GetSprite(key.ToString()); } #endregion Enhance Icon #region Skill Icon public static AsyncOperationHandle handleSkillIcons; public static SpriteAtlas skillIcons = null; private static void AInitSkillIcon(AsyncOperationHandle obj) { skillIcons = obj.Result; handleSkillIcons = obj; I_LoadingCnt--; } public static Sprite GetSkillActiveIcon(int key) { #if UNITY_EDITOR if (I_LoadingCnt > 0) return null; #endif return skillIcons.GetSprite(key.ToString()); } public static Sprite GetSkillPassiveIcon(int key) { #if UNITY_EDITOR if (I_LoadingCnt > 0) return null; #endif key += 1000; return skillIcons.GetSprite(key.ToString()); } public static Sprite GetSkillActiveIconS(int key) { #if UNITY_EDITOR if (I_LoadingCnt > 0) return null; #endif key += 500; return skillIcons.GetSprite(key.ToString()); } #endregion Skill Icon #region Weapon Icon public static AsyncOperationHandle handleWeaponIcons; public static SpriteAtlas weaponIcons = null; private static void AInitWeaponIcon(AsyncOperationHandle obj) { weaponIcons = obj.Result; handleWeaponIcons = obj; I_LoadingCnt--; } public static Sprite GetWeaponIcon(int key) { #if UNITY_EDITOR if (I_LoadingCnt > 0) return null; #endif return weaponIcons.GetSprite(key.ToString()); } #endregion Weapon Icon #region Player Icon public static AsyncOperationHandle handlePlayerIcons; public static SpriteAtlas playerIcons = null; private static void AInitPlayerIcon(AsyncOperationHandle obj) { playerIcons = obj.Result; handlePlayerIcons = obj; I_LoadingCnt--; } public static Sprite GetPlayerIcon(int key) { #if UNITY_EDITOR if (I_LoadingCnt > 0) return null; #endif return playerIcons.GetSprite(key.ToString()); } #endregion Player Icon #region Costume Cloth private static Dictionary> dicClothSpine = new Dictionary>(); private static List>> clothSpineCompList = new List>>(); public static string GetCosInfo(int key) { if (key < 0) return "null"; if (!dicClothSpine.ContainsKey(key)) return "Not Contain"; return FormatString.CombineAllString("addr loading ", dicClothSpine[key].isLoading.ToString(), ", count ", dicClothSpine[key].loadCnt.ToString(), ", valid ", dicClothSpine[key].handle.IsValid().ToString()); } // 의상 리소스 가져오기. public static void LoadClothSpine(int key, System.Action actioncomp) { if (key < 0) return; if (key == 0) key = 1; // 목록에 있는 항목. if (dicClothSpine.ContainsKey(key)) { // 로딩중일 경우. 로딩 완료 후 작업 목록에 추가. if (dicClothSpine[key].isLoading) { clothSpineCompList.Add(new KeyValuePair>(key, actioncomp)); return; } // 로드 된 항목인 경우. 작업 실행. dicClothSpine[key].loadCnt++; actioncomp(dicClothSpine[key].handle.Result); return; } // 목록에 없는 항목일 경우 추가. dicClothSpine.Add(key, new jAddrCnt()); clothSpineCompList.Add(new KeyValuePair>(key, actioncomp)); // 리소스 로드. Addressables.LoadAssetAsync(FormatString.CombineAllString(PathCosCloth, DataHandler.GetCosClothPath(key))).Completed += ALoadClothComp; } // 의상 리소스 가져오기. 로드 카운트 증가 안 시킴. public static void LoadClothSpineSoon(int key, System.Action actioncomp) { if (key < 0) return; //Debug.LogWarning("ADDR LoadClothSpine [" + key + "]"); // 목록에 있는 항목. if (!dicClothSpine.ContainsKey(key)) return; // 로딩중일 경우. if (dicClothSpine[key].isLoading) return; // 로드 된 항목인 경우. 작업 실행. actioncomp(dicClothSpine[key].handle.Result); } // 의상 리소스 로드 카운트 증가. public static void LoadClothAdd(int key) { if (dicClothSpine.ContainsKey(key)) { dicClothSpine[key].loadCnt++; //GameUIMgr.SPrintTestLog(FormatString.CombineAllString("CountAdd [", key.ToString(), "] ", dicClothSpine[key].loadCnt.ToString())); } } // 의상 리소스 로드 완료. private static void ALoadClothComp(AsyncOperationHandle obj) { int key = int.Parse(obj.Result.name.Replace("c", "")); if (dicClothSpine.ContainsKey(key)) { dicClothSpine[key].handle = obj; dicClothSpine[key].isLoading = false; // 작업 목록 확인하여 해당하는 항목의 작업 실행. for (int k = clothSpineCompList.Count - 1; k >= 0; k--) { if (clothSpineCompList[k].Key == key) { dicClothSpine[key].loadCnt++; clothSpineCompList[k].Value(obj.Result); clothSpineCompList.RemoveAt(k); } } //GameUIMgr.SPrintTestLog(FormatString.CombineAllString("Comp [", key.ToString(), "] ", dicClothSpine[key].loadCnt.ToString())); } } // 의상 리소스 해제. public static void ReleaseClothSpine(int key) { // 목록에 있는 항목. if (dicClothSpine.ContainsKey(key)) { dicClothSpine[key].loadCnt--; //GameUIMgr.SPrintTestLog(FormatString.CombineAllString("Release [", key.ToString(), "] ", dicClothSpine[key].loadCnt.ToString(), " : ", dicClothSpine[key].handle.IsValid().ToString(), " :: ", dicClothSpine[key].isLoading.ToString())); if (dicClothSpine[key].isLoading) return; if (dicClothSpine[key].loadCnt <= 0) { if (dicClothSpine[key].handle.IsValid()) Addressables.Release(dicClothSpine[key].handle); dicClothSpine.Remove(key); SAddUnload(); } } } #endregion Costume Cloth #region Costume Weapon private const string SkinName = "default"; private const string SlotNameWeapon = "weapon"; private const string AtchNameWeapon = "weapon"; private static int iSlotIndexWeapon; private static Material matWeaponBase = null; private static Attachment atchWeaponBase = null; private static Dictionary> dicWeaponSpine = new Dictionary>(); private static Dictionary dicWeaponSkin = new Dictionary(); private static List>> weaponSpineCompList = new List>>(); // 스킨 생성 필요 정보 세팅. public static void InitWeaponSkin(Skeleton skeleton, Material mat) { matWeaponBase = mat; Skin skintemp = skeleton.Data.FindSkin(SkinName); iSlotIndexWeapon = skeleton.Data.FindSlot(SlotNameWeapon).Index; atchWeaponBase = skintemp.GetAttachment(iSlotIndexWeapon, AtchNameWeapon); } // 무기 리소스 가져오기. public static void LoadWeaponSpine(int key, System.Action actioncomp) { if (key < 0) return; if (key == 0) key = 1; // 목록에 있는 항목. if (dicWeaponSpine.ContainsKey(key)) { // 로딩중일 경우. 로딩 완료 후 작업 목록에 추가. if (dicWeaponSpine[key].isLoading) { weaponSpineCompList.Add(new KeyValuePair>(key, actioncomp)); return; } // 로드 된 항목인 경우. 작업 실행. dicWeaponSpine[key].loadCnt++; actioncomp(dicWeaponSkin[key]); return; } // 목록에 없는 항목일 경우 추가. dicWeaponSpine.Add(key, new jAddrCnt()); dicWeaponSkin.Add(key, null); weaponSpineCompList.Add(new KeyValuePair>(key, actioncomp)); //리소스 로드. Addressables.LoadAssetAsync(FormatString.CombineAllString(PathCosWeapon, DataHandler.GetCosWeaponPath(key))).Completed += ALoadWeaponComp; } // 무기 리소스 로드 카운트 증가. public static void LoadWeaponAdd(int key) { if (dicWeaponSpine.ContainsKey(key)) { dicWeaponSpine[key].loadCnt++; //Debug.LogWarning("ADDR LoadWeaponAdd [" + key + "] " + dicWeaponSpine[key].loadCnt); } } // 무기 리소스 로드 완료. private static void ALoadWeaponComp(AsyncOperationHandle obj) { int key = int.Parse(obj.Result.name.Replace("w", "")); if (dicWeaponSpine.ContainsKey(key)) { dicWeaponSkin[key] = CreateWeaponSkin(obj.Result); dicWeaponSpine[key].handle = obj; dicWeaponSpine[key].isLoading = false; // 작업 목록 확인하여 해당하는 항목의 작업 실행. for (int k = weaponSpineCompList.Count - 1; k >= 0; k--) { if (weaponSpineCompList[k].Key == key) { dicWeaponSpine[key].loadCnt++; weaponSpineCompList[k].Value(dicWeaponSkin[key]); weaponSpineCompList.RemoveAt(k); } } } } // 무기 리소스 해제. public static void ReleaseWeaponSpine(int key) { // 목록에 있는 항목. if (dicWeaponSpine.ContainsKey(key)) { dicWeaponSpine[key].loadCnt--; Debug.LogWarning("ADDR ReleaseWeaponSpine [" + key + "] " + dicWeaponSpine[key].loadCnt); if (dicWeaponSpine[key].isLoading) return; if (dicWeaponSpine[key].loadCnt <= 0) { dicWeaponSkin.Remove(key); if (dicWeaponSpine[key].handle.IsValid()) Addressables.Release(dicWeaponSpine[key].handle); dicWeaponSpine.Remove(key); SAddUnload(); } } } // 스킨 생성해서 리턴. private static Skin CreateWeaponSkin(Sprite sprwp) { Skin skin = new Skin("customskin"); Attachment acthwp = atchWeaponBase.GetRemappedClone(sprwp, matWeaponBase, pivotShiftsMeshUVCoords: false); if (acthwp != null) skin.SetAttachment(iSlotIndexWeapon, AtchNameWeapon, acthwp); else skin.SetAttachment(iSlotIndexWeapon, AtchNameWeapon, atchWeaponBase); AtlasUtilities.ClearCache(); return skin; } #endregion Costume Weapon #region Relic public static AsyncOperationHandle handleRelicIcons; public static SpriteAtlas RelicIcons = null; private static void AInitRelicIcon(AsyncOperationHandle obj) { RelicIcons = obj.Result; handleRelicIcons = obj; I_LoadingCnt--; } public static Sprite GetRelicIcon(int key) { #if UNITY_EDITOR if (I_LoadingCnt > 0) return null; #endif return RelicIcons.GetSprite(key.ToString()); } #endregion #region Relic public static AsyncOperationHandle handleDungeonPanel; public static SpriteAtlas DungeonPanel = null; private static void AInitDungeonPanel(AsyncOperationHandle obj) { DungeonPanel = obj.Result; handleDungeonPanel = obj; I_LoadingCnt--; } public static Sprite GetDungeonPanel(int key) { #if UNITY_EDITOR if (I_LoadingCnt > 0) return null; #endif return DungeonPanel.GetSprite(key.ToString()); } #endregion #region Unload static readonly int _unloadMax = 30; private static int I_UnloadCount = 0; public static void SAddUnload() { if (I_UnloadCount++ >= _unloadMax) { I_UnloadCount = 0; Resources.UnloadUnusedAssets(); } } public static void SForceUnload() { I_UnloadCount = 0; Resources.UnloadUnusedAssets(); } #endregion Unload }