2025-07-16 04:33:47 +00:00
using System.Collections.Generic ;
2025-07-10 10:18:07 +00:00
using System.Linq ;
using System.Threading.Tasks ;
2025-07-09 09:45:11 +00:00
using UnityEngine ;
using UnityEngine.AddressableAssets ;
using UnityEngine.ResourceManagement.AsyncOperations ;
2025-07-10 10:18:07 +00:00
using UnityEngine.ResourceManagement.ResourceProviders ;
using UnityEngine.SceneManagement ;
2025-07-09 09:45:11 +00:00
namespace DDD
{
public class AssetManager : Singleton < AssetManager > , IManager
{
2025-08-27 08:52:33 +00:00
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 ( ) ;
2025-07-29 18:52:13 +00:00
protected override void OnApplicationQuit ( )
{
base . OnApplicationQuit ( ) ;
ReleaseAllCached ( ) ;
}
2025-08-27 08:52:33 +00:00
public void PreInit ( ) { }
2025-07-09 09:45:11 +00:00
2025-07-21 04:11:38 +00:00
public async Task Init ( )
2025-07-09 09:45:11 +00:00
{
2025-07-21 04:11:38 +00:00
await Addressables . InitializeAsync ( ) . Task ;
}
2025-08-27 08:52:33 +00:00
public void PostInit ( ) { }
private string GetSafeAssetName ( AssetReference assetReference )
2025-07-21 04:11:38 +00:00
{
2025-08-27 08:52:33 +00:00
var editorAssetName = assetReference . editorAsset . name ;
if ( string . IsNullOrWhiteSpace ( editorAssetName ) = = false ) return editorAssetName ;
2025-07-21 04:11:38 +00:00
2025-08-27 08:52:33 +00:00
return assetReference . ToString ( ) ;
2025-07-10 05:48:44 +00:00
}
2025-08-27 08:52:33 +00:00
public async Task < T > LoadAssetAsync < T > ( AssetReference assetReference ) where T : Object
2025-07-10 05:48:44 +00:00
{
2025-08-27 08:52:33 +00:00
var guidKey = assetReference . AssetGUID ;
if ( _cachedAssets . TryGetValue ( guidKey , out var cachedAsset ) )
2025-07-10 10:18:07 +00:00
{
2025-08-27 08:52:33 +00:00
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}" ) ;
2025-07-29 18:52:13 +00:00
return result ;
2025-08-27 08:52:33 +00:00
}
_cachedAssets . Remove ( guidKey ) ;
Debug . LogWarning ( $"[AssetManager] 무효한 핸들 제거됨: {GetSafeAssetName(assetReference)}\n실제 키 값: {guidKey}" ) ;
2025-07-10 10:18:07 +00:00
}
2025-08-27 08:52:33 +00:00
var newHandle = Addressables . LoadAssetAsync < T > ( guidKey ) ;
2025-07-29 18:52:13 +00:00
await newHandle . Task ;
2025-07-10 10:18:07 +00:00
2025-07-29 18:52:13 +00:00
if ( newHandle . Status = = AsyncOperationStatus . Succeeded )
{
2025-08-27 08:52:33 +00:00
_cachedAssets [ guidKey ] = new CachedAsset ( newHandle , 1 ) ;
if ( _enableDebugLog ) Debug . Log ( $"[AssetManager] 에셋 로드 및 캐싱 완료: {GetSafeAssetName(assetReference)} (참조수: {_cachedAssets[guidKey].ReferenceCount})\n실제 키 값: {guidKey}" ) ;
2025-07-29 18:52:13 +00:00
return newHandle . Result ;
}
2025-07-10 10:18:07 +00:00
2025-08-27 08:52:33 +00:00
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}" ) ;
2025-07-10 10:18:07 +00:00
return null ;
}
2025-08-27 08:52:33 +00:00
public async Task < List < T > > LoadAssetsByLabel < T > ( string label ) where T : Object
2025-07-16 04:33:47 +00:00
{
2025-08-27 08:52:33 +00:00
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 ) ;
2025-07-16 04:33:47 +00:00
await handle . Task ;
if ( handle . Status = = AsyncOperationStatus . Succeeded )
2025-08-27 08:52:33 +00:00
{
_cachedAssets [ label ] = new CachedAsset ( handle , 1 ) ;
if ( _enableDebugLog ) Debug . Log ( $"[AssetManager] 라벨 에셋 로드 및 캐싱 완료: {label} (참조수: {_cachedAssets[label].ReferenceCount})" ) ;
2025-07-16 04:33:47 +00:00
return handle . Result . ToList ( ) ;
2025-08-27 08:52:33 +00:00
}
if ( handle . IsValid ( ) )
{
Addressables . Release ( handle ) ;
}
2025-07-16 04:33:47 +00:00
2025-08-27 08:52:33 +00:00
Debug . LogError ( $"[AssetManager] 라벨 에셋 로드 실패: {label}" ) ;
2025-07-16 04:33:47 +00:00
return new List < T > ( ) ;
}
2025-08-01 05:42:31 +00:00
2025-08-27 08:52:33 +00:00
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 )
2025-07-10 10:18:07 +00:00
{
2025-08-27 08:52:33 +00:00
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}" ) ;
}
2025-08-19 09:24:36 +00:00
var handle = Addressables . LoadSceneAsync ( assetReference , mode , activateOnLoad ) ;
2025-07-10 10:18:07 +00:00
await handle . Task ;
if ( handle . Status = = AsyncOperationStatus . Succeeded )
2025-08-27 08:52:33 +00:00
{
_cachedScenes [ guidKey ] = new CachedScene ( handle , 1 ) ;
var sceneName = handle . Result . Scene . name ;
if ( _enableDebugLog ) Debug . Log ( $"[AssetManager] 씬 로드 및 캐싱 완료: {sceneName} (참조수: {_cachedScenes[guidKey].ReferenceCount})\n실제 키 값: {guidKey}" ) ;
2025-07-10 10:18:07 +00:00
return handle . Result ;
2025-08-27 08:52:33 +00:00
}
if ( handle . IsValid ( ) )
{
Addressables . UnloadSceneAsync ( handle ) ;
}
2025-07-10 10:18:07 +00:00
2025-08-27 08:52:33 +00:00
Debug . LogError ( $"[AssetManager] 씬 로드 실패: {GetSafeAssetName(assetReference)}\n실제 키 값: {guidKey}" ) ;
2025-07-10 10:18:07 +00:00
return default ;
}
2025-08-27 08:52:33 +00:00
public async Task UnloadScene ( AssetReference assetReference )
2025-07-29 18:52:13 +00:00
{
2025-08-27 08:52:33 +00:00
var guidKey = assetReference . AssetGUID ;
2025-07-29 18:52:13 +00:00
2025-08-27 08:52:33 +00:00
if ( _cachedScenes . TryGetValue ( guidKey , out var cachedScene ) )
2025-07-29 18:52:13 +00:00
{
2025-08-27 08:52:33 +00:00
cachedScene . ReferenceCount - - ;
if ( cachedScene . ReferenceCount < = 0 )
2025-07-29 18:52:13 +00:00
{
2025-08-27 08:52:33 +00:00
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}" ) ;
2025-07-29 18:52:13 +00:00
}
}
2025-08-27 08:52:33 +00:00
else
{
if ( _enableDebugLog ) Debug . LogWarning ( $"[AssetManager] 캐시에서 씬을 찾을 수 없음: {GetSafeAssetName(assetReference)}\n실제 키 값: {guidKey}" ) ;
}
2025-07-29 18:52:13 +00:00
}
2025-08-27 08:52:33 +00:00
public void ReleaseAllCached ( )
2025-07-10 10:18:07 +00:00
{
2025-08-27 08:52:33 +00:00
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 ( ) ;
2025-07-10 10:18:07 +00:00
2025-08-27 08:52:33 +00:00
if ( _enableDebugLog ) Debug . Log ( "[AssetManager] 모든 캐시된 Addressable 리소스를 강제 해제했습니다." ) ;
2025-07-10 10:18:07 +00:00
}
2025-07-09 09:45:11 +00:00
}
}