330 lines
13 KiB
C#
330 lines
13 KiB
C#
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using UnityEngine;
|
|
using UnityEngine.AddressableAssets;
|
|
using UnityEngine.ResourceManagement.AsyncOperations;
|
|
using UnityEngine.ResourceManagement.ResourceProviders;
|
|
using UnityEngine.SceneManagement;
|
|
|
|
namespace DDD
|
|
{
|
|
public class AssetManager : Singleton<AssetManager>, IManager
|
|
{
|
|
private struct CachedAsset
|
|
{
|
|
public AsyncOperationHandle Handle;
|
|
public int ReferenceCount;
|
|
|
|
public CachedAsset(AsyncOperationHandle handle, int count = 1)
|
|
{
|
|
Handle = handle;
|
|
ReferenceCount = count;
|
|
}
|
|
}
|
|
|
|
private struct CachedScene
|
|
{
|
|
public AsyncOperationHandle<SceneInstance> Handle;
|
|
public int ReferenceCount;
|
|
|
|
public CachedScene(AsyncOperationHandle<SceneInstance> handle, int count = 1)
|
|
{
|
|
Handle = handle;
|
|
ReferenceCount = count;
|
|
}
|
|
}
|
|
|
|
[SerializeField] private bool _enableDebugLog = false;
|
|
|
|
private readonly Dictionary<string, CachedAsset> _cachedAssets = new();
|
|
private readonly Dictionary<string, CachedScene> _cachedScenes = new();
|
|
|
|
protected override void OnApplicationQuit()
|
|
{
|
|
base.OnApplicationQuit();
|
|
|
|
ReleaseAllCached();
|
|
}
|
|
|
|
public void PreInit() { }
|
|
|
|
public async Task Init()
|
|
{
|
|
await Addressables.InitializeAsync().Task;
|
|
}
|
|
|
|
public void PostInit() { }
|
|
|
|
private string GetSafeAssetName(AssetReference assetReference)
|
|
{
|
|
var editorAssetName = assetReference.editorAsset.name;
|
|
if (string.IsNullOrWhiteSpace(editorAssetName) == false) return editorAssetName;
|
|
|
|
return assetReference.ToString();
|
|
}
|
|
|
|
public async Task<T> LoadAssetAsync<T>(AssetReference assetReference) where T : Object
|
|
{
|
|
var guidKey = assetReference.AssetGUID;
|
|
|
|
if (_cachedAssets.TryGetValue(guidKey, out var cachedAsset))
|
|
{
|
|
if (cachedAsset.Handle.IsValid() && cachedAsset.Handle.Result is T result)
|
|
{
|
|
cachedAsset.ReferenceCount++;
|
|
_cachedAssets[guidKey] = cachedAsset;
|
|
|
|
if (_enableDebugLog) Debug.Log($"[AssetManager] 에셋 참조 카운트 증가: {GetSafeAssetName(assetReference)} -> {cachedAsset.ReferenceCount}\n실제 키 값: {guidKey}");
|
|
return result;
|
|
}
|
|
|
|
_cachedAssets.Remove(guidKey);
|
|
Debug.LogWarning($"[AssetManager] 무효한 핸들 제거됨: {GetSafeAssetName(assetReference)}\n실제 키 값: {guidKey}");
|
|
}
|
|
|
|
var newHandle = Addressables.LoadAssetAsync<T>(guidKey);
|
|
await newHandle.Task;
|
|
|
|
if (newHandle.Status == AsyncOperationStatus.Succeeded)
|
|
{
|
|
_cachedAssets[guidKey] = new CachedAsset(newHandle, 1);
|
|
if (_enableDebugLog) Debug.Log($"[AssetManager] 에셋 로드 및 캐싱 완료: {GetSafeAssetName(assetReference)} (참조수: {_cachedAssets[guidKey].ReferenceCount})\n실제 키 값: {guidKey}");
|
|
return newHandle.Result;
|
|
}
|
|
|
|
if (newHandle.IsValid())
|
|
{
|
|
Addressables.Release(newHandle);
|
|
}
|
|
|
|
Debug.LogError($"[AssetManager] 에셋 로드 실패: {GetSafeAssetName(assetReference)}\n실제 키 값: {guidKey}");
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// ScriptSingleton을 위한 동기 로딩
|
|
/// </summary>
|
|
public T LoadAsset<T>(string key) where T : Object
|
|
{
|
|
if (_cachedAssets.TryGetValue(key, out var cachedAsset))
|
|
{
|
|
if (cachedAsset.Handle.IsValid() && cachedAsset.Handle.Result is T result)
|
|
{
|
|
cachedAsset.ReferenceCount++;
|
|
_cachedAssets[key] = cachedAsset;
|
|
|
|
if (_enableDebugLog) Debug.Log($"[AssetManager] 동기 로딩 - 에셋 참조 카운트 증가: {key} -> {cachedAsset.ReferenceCount}");
|
|
return result;
|
|
}
|
|
|
|
_cachedAssets.Remove(key);
|
|
Debug.LogWarning($"[AssetManager] 무효한 핸들 제거됨: {key}");
|
|
}
|
|
|
|
var handle = Addressables.LoadAssetAsync<T>(key);
|
|
var loaded = handle.WaitForCompletion();
|
|
|
|
if (handle.Status == AsyncOperationStatus.Succeeded)
|
|
{
|
|
_cachedAssets[key] = new CachedAsset(handle, 1);
|
|
if (_enableDebugLog) Debug.Log($"[AssetManager] 동기 로딩 - 에셋 로드 및 캐싱 완료: {key} (참조수: {key})");
|
|
return loaded;
|
|
}
|
|
|
|
if (handle.IsValid())
|
|
{
|
|
Addressables.Release(handle);
|
|
}
|
|
|
|
Debug.LogError($"[AssetManager] 동기 로딩 - 에셋 로드 실패: {key}");
|
|
return null;
|
|
}
|
|
|
|
public async Task<List<T>> LoadAssetsByLabel<T>(string label) where T : Object
|
|
{
|
|
if (_cachedAssets.TryGetValue(label, out var cachedAsset))
|
|
{
|
|
if (cachedAsset.Handle.IsValid() && cachedAsset.Handle.Result is IList<T> cachedResult)
|
|
{
|
|
cachedAsset.ReferenceCount++;
|
|
_cachedAssets[label] = cachedAsset;
|
|
|
|
if (_enableDebugLog) Debug.Log($"[AssetManager] 라벨 에셋 참조 카운트 증가: {label} -> {cachedAsset.ReferenceCount}");
|
|
return cachedResult.ToList();
|
|
}
|
|
|
|
_cachedAssets.Remove(label);
|
|
if (_enableDebugLog) Debug.LogWarning($"[AssetManager] 무효한 라벨 핸들 제거됨: {label}");
|
|
}
|
|
|
|
var handle = Addressables.LoadAssetsAsync<T>(label);
|
|
await handle.Task;
|
|
|
|
if (handle.Status == AsyncOperationStatus.Succeeded)
|
|
{
|
|
_cachedAssets[label] = new CachedAsset(handle, 1);
|
|
if (_enableDebugLog) Debug.Log($"[AssetManager] 라벨 에셋 로드 및 캐싱 완료: {label} (참조수: {_cachedAssets[label].ReferenceCount})");
|
|
return handle.Result.ToList();
|
|
}
|
|
|
|
if (handle.IsValid())
|
|
{
|
|
Addressables.Release(handle);
|
|
}
|
|
|
|
Debug.LogError($"[AssetManager] 라벨 에셋 로드 실패: {label}");
|
|
return new List<T>();
|
|
}
|
|
|
|
public void ReleaseAsset(AssetReference assetReference)
|
|
{
|
|
var guidKey = assetReference.AssetGUID;
|
|
|
|
if (_cachedAssets.TryGetValue(guidKey, out var cachedAsset))
|
|
{
|
|
cachedAsset.ReferenceCount--;
|
|
|
|
if (cachedAsset.ReferenceCount <= 0)
|
|
{
|
|
if (cachedAsset.Handle.IsValid())
|
|
{
|
|
Addressables.Release(cachedAsset.Handle);
|
|
if (_enableDebugLog) Debug.Log($"[AssetManager] 에셋 실제 해제됨: {GetSafeAssetName(assetReference)}\n실제 키 값: {guidKey}");
|
|
}
|
|
_cachedAssets.Remove(guidKey);
|
|
}
|
|
else
|
|
{
|
|
_cachedAssets[guidKey] = cachedAsset;
|
|
if (_enableDebugLog) Debug.Log($"[AssetManager] 에셋 참조 카운트 감소: {GetSafeAssetName(assetReference)} -> {cachedAsset.ReferenceCount}\n실제 키 값: {guidKey}");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (_enableDebugLog) Debug.LogWarning($"[AssetManager] 캐시에서 에셋을 찾을 수 없음: {GetSafeAssetName(assetReference)}\n실제 키 값: {guidKey}");
|
|
}
|
|
}
|
|
|
|
public void ReleaseAsset(string key)
|
|
{
|
|
if (_cachedAssets.TryGetValue(key, out var cachedAsset))
|
|
{
|
|
cachedAsset.ReferenceCount--;
|
|
|
|
if (cachedAsset.ReferenceCount <= 0)
|
|
{
|
|
if (cachedAsset.Handle.IsValid())
|
|
{
|
|
Addressables.Release(cachedAsset.Handle);
|
|
if (_enableDebugLog) Debug.Log($"[AssetManager] 에셋 실제 해제됨: {key}");
|
|
}
|
|
_cachedAssets.Remove(key);
|
|
}
|
|
else
|
|
{
|
|
_cachedAssets[key] = cachedAsset;
|
|
if (_enableDebugLog) Debug.Log($"[AssetManager] 에셋 참조 카운트 감소: {key} -> {cachedAsset.ReferenceCount}");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (_enableDebugLog) Debug.LogWarning($"[AssetManager] 캐시에서 에셋을 찾을 수 없음: {key}");
|
|
}
|
|
}
|
|
|
|
public async Task<SceneInstance> LoadScene(AssetReference assetReference, LoadSceneMode mode = LoadSceneMode.Additive, bool activateOnLoad = true)
|
|
{
|
|
var guidKey = assetReference.AssetGUID;
|
|
|
|
if (_cachedScenes.TryGetValue(guidKey, out var cachedScene))
|
|
{
|
|
if (cachedScene.Handle.IsValid())
|
|
{
|
|
cachedScene.ReferenceCount++;
|
|
_cachedScenes[guidKey] = cachedScene;
|
|
|
|
if (_enableDebugLog) Debug.Log($"[AssetManager] 씬 참조 카운트 증가: {GetSafeAssetName(assetReference)} -> {cachedScene.ReferenceCount}\n실제 키 값: {guidKey}");
|
|
return cachedScene.Handle.Result;
|
|
}
|
|
|
|
_cachedScenes.Remove(guidKey);
|
|
if (_enableDebugLog) Debug.LogWarning($"[AssetManager] 무효한 씬 핸들 제거됨: {GetSafeAssetName(assetReference)}\n실제 키 값: {guidKey}");
|
|
}
|
|
|
|
var handle = Addressables.LoadSceneAsync(assetReference, mode, activateOnLoad);
|
|
await handle.Task;
|
|
|
|
if (handle.Status == AsyncOperationStatus.Succeeded)
|
|
{
|
|
_cachedScenes[guidKey] = new CachedScene(handle, 1);
|
|
var sceneName = handle.Result.Scene.name;
|
|
if (_enableDebugLog) Debug.Log($"[AssetManager] 씬 로드 및 캐싱 완료: {sceneName} (참조수: {_cachedScenes[guidKey].ReferenceCount})\n실제 키 값: {guidKey}");
|
|
return handle.Result;
|
|
}
|
|
|
|
if (handle.IsValid())
|
|
{
|
|
Addressables.UnloadSceneAsync(handle);
|
|
}
|
|
|
|
Debug.LogError($"[AssetManager] 씬 로드 실패: {GetSafeAssetName(assetReference)}\n실제 키 값: {guidKey}");
|
|
return default;
|
|
}
|
|
|
|
public async Task UnloadScene(AssetReference assetReference)
|
|
{
|
|
var guidKey = assetReference.AssetGUID;
|
|
|
|
if (_cachedScenes.TryGetValue(guidKey, out var cachedScene))
|
|
{
|
|
cachedScene.ReferenceCount--;
|
|
|
|
if (cachedScene.ReferenceCount <= 0)
|
|
{
|
|
if (cachedScene.Handle.IsValid())
|
|
{
|
|
var sceneName = cachedScene.Handle.Result.Scene.name;
|
|
await Addressables.UnloadSceneAsync(cachedScene.Handle).Task;
|
|
if (_enableDebugLog) Debug.Log($"[AssetManager] 씬 실제 언로드됨: {sceneName}\n실제 키 값: {guidKey}");
|
|
}
|
|
_cachedScenes.Remove(guidKey);
|
|
}
|
|
else
|
|
{
|
|
_cachedScenes[guidKey] = cachedScene;
|
|
if (_enableDebugLog) Debug.Log($"[AssetManager] 씬 참조 카운트 감소: {GetSafeAssetName(assetReference)} -> {cachedScene.ReferenceCount}\n실제 키 값: {guidKey}");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (_enableDebugLog) Debug.LogWarning($"[AssetManager] 캐시에서 씬을 찾을 수 없음: {GetSafeAssetName(assetReference)}\n실제 키 값: {guidKey}");
|
|
}
|
|
}
|
|
|
|
public void ReleaseAllCached()
|
|
{
|
|
foreach (var kvp in _cachedAssets)
|
|
{
|
|
var cachedAsset = kvp.Value;
|
|
if (cachedAsset.Handle.IsValid())
|
|
{
|
|
Addressables.Release(cachedAsset.Handle);
|
|
}
|
|
}
|
|
_cachedAssets.Clear();
|
|
|
|
foreach (var kvp in _cachedScenes)
|
|
{
|
|
var cachedScene = kvp.Value;
|
|
if (cachedScene.Handle.IsValid())
|
|
{
|
|
Addressables.UnloadSceneAsync(cachedScene.Handle);
|
|
}
|
|
}
|
|
_cachedScenes.Clear();
|
|
|
|
if (_enableDebugLog) Debug.Log("[AssetManager] 모든 캐시된 Addressable 리소스를 강제 해제했습니다.");
|
|
}
|
|
}
|
|
} |