ProjectDDD/Assets/_DDD/_Scripts/AssetManagement/AssetManager.cs

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 리소스를 강제 해제했습니다.");
}
}
}