UE5/Lyra Clone Coding
[UE5] AssetManager
검정색필통
2025. 1. 21. 00:05
AssetManager란?
UAssetManager는 Unreal Engine에서 에셋(Assets)을 효율적으로 로드 및 관리하는 시스템이다.
📌 주요 기능:
✅ 게임 내 특정 리소스를 동적으로 로드 & 언로드 (메모리 최적화)
✅ Primary Asset Type을 기반으로 에셋을 그룹화하여 관리
✅ 비동기(Async) 로딩 지원 → 게임 실행 중 성능 최적화 가능
✅ 패키징과 쿠킹을 최적화하여 빌드 크기를 줄일 수 있음
즉, UAssetManager는 게임이 실행될 때 필요한 에셋만 로드하여 메모리 사용을 최적화하고, 필요하지 않은 리소스를 제거하여 성능을 최적화하는 시스템이다.
AssetManager의 필요성
AssetManager를 사요하지 않고 ConstructorHelpers::FObjectFinder를 통해 하드코딩된 경로로 Asset을 불러오면 아래와 같이 수백, 수천개의 Asset의 경로를 하드코딩을 통해 불러와야한다.
static ConstructorHelpers::FObjectFinder<UStaticMesh> SphereVisualAsset(TEXT("/Game/..."));
심지어 경로가 변경된다면..? (끔찍)
AssetManager는 Singleton
UAssetManager는 전역적으로 하나의 인스턴스만 존재하며, UAssetManager::Get()을 사용하여 어디서든 접근할 수 있다.
싱글톤인 이유 :
- UAssetManager는 Unreal Engine 전체에서 공유되는 단일 인스턴스여야 함.
- 여러 개의 AssetManager가 존재하면 에셋 로드 및 캐시 관리가 충돌할 수 있음.
- 싱글톤 패턴을 통해 메모리 및 성능을 최적화하고 동기화 문제를 방지함.
UZSAssetManager& UZSAssetManager::Get()
{
check(GEngine);
// UZSAssetManager override GEngine's AssetManager
if (UZSAssetManager* Singleton = Cast<UZSAssetManager>(GEngine->AssetManager))
{
return *Singleton;
}
UE_LOG(LogZS, Fatal, TEXT("Invalid AssetManagerClassname in DefaultEngine.ini(project settings); it must be ZSAssetManager"));
// never return because of Fatal crash, but use to compile
return *NewObject<UZSAssetManager>();
}
SynchronousLoadAsset
UObject* UZSAssetManager::SynchronousLoadAsset(const FSoftObjectPath& AssetPath)
{
if (AssetPath.IsValid())
{
TUniquePtr<FScopeLogTime> LogTimePtr;
if (ShouldLogAssetLoads())
{
LogTimePtr = MakeUnique<FScopeLogTime>(*FString::Printf(TEXT("synchronous loaded assets [%s]"), *AssetPath.ToString()), nullptr, FScopeLogTime::ScopeLog_Seconds);
}
if (UAssetManager::IsValid())
{
return UAssetManager::GetStreamableManager().LoadSynchronous(AssetPath);
}
return AssetPath.TryLoad();
}
return nullptr;
}
- AssetManager가 Asset을 불러오는데 시간을 얼마나 소비하는지 로깅. 너무 오래걸리면 동기 로딩 하면 안되니까 따로 빼야함.
- AssetManager가 있으면, StreamableManager를 통해 정적 로딩
- AssetManager가 없으면, StaticLoadObject를 통해 로딩(TryLoad). 근데 캐싱되어있으면 거기서 가져옴. 매우매우 느리기 때문에 최악의 경우임(AssetManager에 등록안되어 있는..)
GetAsset
template<typename AssetType>
AssetType* UZSAssetManager::GetAsset(const TSoftObjectPtr<AssetType>& AssetPointer, bool bKeepInMemory)
{
AssetType* LoadedAsset = nullptr;
const FSoftObjectPath& AssetPath = AssetPointer.ToSoftObjectPath();
if (AssetPath.IsValid())
{
LoadedAsset = AssetPointer.Get();
if (!LoadedAsset)
{
LoadedAsset = Cast<AssetType>(SynchronousLoadAsset(AssetPath));
ensureAlwaysMsgf(LoadedAsset, TEXT("Failed to load asset [%s]"), *AssetPointer.ToString());
}
if (LoadedAsset && bKeepInMemory)
{
Get().AddLoadedAsset(Cast<UObject>(LoadedAsset));
}
}
return LoadedAsset;
}
- TSoftObjectPtr에서 에셋의 경로(FSoftObjectPath)를 가져옴.
- 이미 로드된 에셋이 있는지 확인 (Get() 호출).
- 로드되지 않았다면 SynchronousLoadAsset()을 호출하여 동기적으로 로드.
- 로드 실패 시 ensureAlwaysMsgf()를 통해 오류 확인.
- bKeepInMemory가 true이면, AddLoadedAsset()을 호출하여 메모리에 유지. 에셋을 GC에 의해 자동으로 삭제되지 않도록 보장.
AddLoadedAsset
void UZSAssetManager::AddLoadedAsset(const UObject* Asset)
{
if (ensureAlways(Asset))
{
FScopeLock Lock(&SyncObject);
LoadedAssets.Add(Asset);
}
}
- FScopeLock Lock(&SyncObject); → 멀티스레딩 환경에서도 안전하게 관리
- LoadedAssets.Add(Asset); → 에셋을 LoadedAssets 목록에 추가하여 관리