AssetManager 기능 정리

조정원·2025년 11월 5일

Asset Manager

Streamable Managers스트리밍을 관리하는 네이티브 구조체
Primary Assets게임의 상태에 따라 직접 로드 및 언로드하는 주요 에셋
Secondary Assets프라이머리 에셋에 의존해 자동 로드 및 언로드 하는 에셋
Asset Bundles런타임에 함께 로드 될 수 있도록 이름 별로 묶은 논리적 에셋 그룹
Asset Manager프라이머리 에셋과 에셋 번들 정보를 관리하는 싱글톤

FPrimaryAsset

  • PrimaryAssetType : FName 기반의 타입
  • PrimaryAssetName : 에셋의 이름, FName
  • PrimaryAssetType:PrimaryAssetName : 게임 내에서 유니크한 페어
  • 타입과 이름은 AssetRegistry tag에 직접적으로 저장됨, 프라이머리 에셋이 디스크에 저장되면 asset registry에서 직접 찾을 수 있음

FStreamableManager

  • 비동기 로드 및 동기 로드 된 객체를 필요 없어질 때까지 메모리에 유지하는 네이티브 구조체

  • FStreamableHandle

    • 스트리밍 작업의 결과로 shared pointer에 의해 추적됨
    • 활성된 핸들은 참조된 에셋을 메모리에 유지함, 로딩 중에는 모든 핸들이 활성
    • 로딩 완료 뒤 취소되거나 해제될 때까지 활성 상태를 유지함
  • FStreamableHandle::ReleaseHandle()

    • 참조를 명시적으로 해제할 때 호출
    • shared pointer가 파괴되면 자동으로 호출
  • FStreamableHandle::CancelHandle()

    • 로드를 중단하고 완료 콜백을 호출하지 않음.
  • FStreamableHandle::WaitUntilComplete()

    • 요청한 에셋이 로드될 때까지 Block
    • 요청한 에셋의 우선순위를 가장 높게 푸쉬하지만, 모든 비동기 요청을 비우지는 않음
    • 일반적으로 LoadObject보다 빠
  • RequestAsyncLoad

    • 스트리밍의 주된 작업으로 문자열 기반 에셋 참조를 전달하면 모든 에셋을 비동기로 로드함
    • 완료 시 콜백을 호출하고, Streamable Handle을 리턴
  • RequestSyncLoad

    • 동기 로드 버전
    • 내부적으로 비동기 로드, WaitUntilComplete나 LoadObject 중 빠른것을 호출
  • LoadSynchronous

    • ReqeustSyncload의 단일 에셋 버전
    • 템플릿을 사용한 안전한 타입 지원
  • bManageActiveHandle

    • true로 설정할 시 GetActiveHanle로 요청하기 전까지 Streamable Manger가 로드 요청 핸들을 직접 관리
  • 예시

    // 비동기 로드
    FStreamableManger StreamableManager;
    FStringAssetReference MeshRef(TEXT("Game/Meshes/SM_Sword.SM_Sword"));
    TSharedPtr<FStreamableHandle> Handle = SteamableManagerRequestAsyncLoad(
    	MeshRef,
    	FStreamableDelegate::CreateLambda([MeshRef]()
    	{
    		UE_LOG(LogTemp, Log, TEXT("비동기 로드 완료"), *MeshRef.ToString());
    	});
    	
    Handle->WaitUntilComplete();
    UObject* LoadedAsset = Handle->GetLoadedAsset();
    
    // 동기 로드 RequestSyncLoad
    TSharedPtr<FStreamableHandle> Handle = StreamableManager.RequestSyncLoad(MeshRef);
    UObject* LoadedAsset = Handle->GetLaodedAsset();
    
    // 동기 로드 LoadSynchronous
    UStaticMesh* AxeMesh = StreamableManager.LoadSynchronous<UStaticMesh>(MeshRef);

AssetManager

  • 런타임에 프라이머리 에셋을 로드하거나 스캔하는 기능을 제공하는 싱글톤 UObject

  • 기존의 ObjectLibrary의 기능을 대체, 내부적으로 FStreamableManager를 감싸서 비동기 로딩 처리

  • Get()

    • 실제 에셋 매니저를 리턴하는 정적함수
    • IsValid()로 유효성 체크
  • ScanPathsForPrimaryAssets(Type,Paths,BaseClass)

    • 디스크나 쿠킹된 에셋 레지스트리를 스캔하여, 특정 타입의 프라이머리 에셋을 찾아 FAssetData로 분석
  • GetPrimaryAssetPath(PrimaryAssetId)

    • 프라이머리 에셋ID를 객체 경로를 반환
  • GetPrimaryAssetIdForPath(StringReference)

    • 문자열 경로를 프라이머리 에셋의 Type:Name 페어로 반환
  • 예시

    UAssetManager& AssetManager = UAssetManager::Get();
    
    FPrimaryAssetId ItemId(TEXT("Item"), TEXT("Sword"));
    FSoftObjectPath AssetPath = AssetManger.GetPrimaryAssetPath(ItemId);
    
    UObject* LoadedAsset = AssetManager.GetStreamableManager().LoadSynchronous(AssetPath);
  • 예시2
    // 싱글톤 접근
    UAssetManager& AssetManager = UAssetManager::Get();
    
    // 에셋 타입의 프라이머리 에셋 ID를 IDList에 담음
    AssetManager.GetPrimaryAssetIdList(WeaponItemType, WeaponIdList);
    
    // 에셋 ID에 대응되는 FAssetData(메타데이터) 반환
    AssetManager.GetPrimaryAssetData(WeaponIdList[0], AssetDataToParse);
    
    // 프라이머리 에셋 로드
    FPrimaryAssetId WeaponId = FPrimaryAssetId(WeaponItemType, WeaponName);
    AssetManager.LoadPrimaryAsset(WeaponId, CurrentLoadState, DelegateFunction);
    
    // 로드된 객체 가져오기
    UWeaponItem* Weapon = AssetManger.GetPrimaryAssetObject<UWeaponItem>(WeaponId);
    
    // 에셋 언로드
    AssetManager.UnloadPrimaryAsset(WeaponId);
    
    // 여러 에셋 미리 로드
    Handle = AssetManger.PreloadPrimaryAssets(ListOfPrimaryAssetIds, CurrentLoadState, false);

AssetBundle

  • 프라이머리 에셋과 관련된 특별한 에셋들의 묶음
  • AssetBundles 메타 태그를 가진 TSoftObjectPtr or FStringAssetReference 타입의 UObject로 생성
  • 복잡한 케이스에서 UpdateAssetBundleData를 오버라이드
    • 런타임에 에셋 번들에 대한 참조가 변경 될 수 있는 경우 직접 번들을 구성

    • 에디터나 런타임에서 에셋을 스캔할 때 자동으로 호출

      virtual void UpdateAssetBundleData(FAssetBundleData& AssetBundleData) override
      {
      		// "Model" 번들에 메쉬 추가
          AssetBundleData.AddBundleAsset("Model", WeaponMesh.ToSoftObjectPath());
          
          // "Sound" 번들에 사운드 추가
          AssetBundleData.AddBundleAsset("Sound", SwingSound.ToSoftObjectPath());
      
          // "UI" 번들에 텍스처 추가
          AssetBundleData.AddBundleAsset("UI", IconTexture.ToSoftObjectPath());
      }
  • ChangeBundleStateForPrimaryAssets
    • 특정 번들을 로드하거나 언로드할 때 사용

    • "Model", "Sound" 번들을 비동기로 로드하고, "UI" 번들은 해제

      TArray<FPrimaryAssetId> WeaponAssets = { SwordId, AxeId };
      TArray<FName> BundlesToLoad = { "Model", "Sound" };
      TArray<FName> BundlesToUnload = { "UI" };
      
      AssetManager.ChangeBundleStateForPrimaryAssets(
          WeaponAssets,
          BundlesToLoad,
          BundlesToUnload,
          false, // 비동기
          FStreamableDelegate::CreateUObject(this, &UMyGameInstance::OnBundlesLoaded)
      );
  • ChangeBundleStateForMatchingPrimaryAssets
    • 특정 타입으로 매칭된 모든 프라이머리 에셋에 대해 상태를 변경

    • 모든 “WeaponItem” 타입의 무기 에셋에서 사운드 번들만 한번에 로드

      AssetManager.ChangeBundleStateForMatchingPrimaryAssets(
          FPrimaryAssetType("WeaponItem"),
          { "Sound" },  // AddBundles
          {},           // RemoveBundles
          false,        // 비동기
          FStreamableDelegate()
      );
  • 예시 특정 프라이머리 에셋 타입에 속한 에셋들을 특정 번들(Game, Lobby) 기준으로 로드
    UCLASS()
    class UAssetBundlesCharacterData : public UPrimaryDataAsset
    {
    	GENERATED_BODY()
    public:
    	UPROPERTY(EditDefaultOnly, meta=(AssetBundles="Lobby"))
    	TSoftObjectPtr<UTexture2D> CharacterLobbyPortrait;
    	
    	UPROPERTY(EditDefaultsOnly)
    	FText CharacterName;
    	
    	UPROPERTY(EditDefaultsOnly, meta=(AssetBundles="Game"))
    	TSoftClassPtr<APawn> CharacterGamePawnClass;
    	
    }
    LoadPrimaryAssetWithType
    • 특정 프라이머리 에셋 타입에 속하면서 번들에 포함된 에셋을 모두 로드

      TArray<FName>& AssetBundles = { "Game", "Lobby" };
      
      UAssetManager& AssetManager = UAssetManager::Get();
      CharacterDataHandle = AssetManger.LoadPrimaryAssetWithType(
      	PrimaryAssetType,
      	AssetBundles,
      	FStreamableDelegate::CreateLambda([this]()
      	{
      		TArray<FPrimaryAssetId> AsssetIds;
      		UAssetManger::Get().GetPrimaryAssetIdList(UAssetBundlesCharacterData::StaticClass()->GetFName(), AssetIds);)
      		if (const UAssetBundlesCharacterData* LoadedAsset = Cast<UAssetBundlesCharacterData>(
      			UAssetManager::Get().GetPrimaryAssetObject(AssetIds[0])))
      		{
      			...
      		}
      	}
      
      );

에셋 매니저 실전 사용법

  • 동기 로드는 로딩씬 또는 입력에 대한 반응이 빠를 때만 사용
    • 로딩 화면 중 (플레이어 입력이 없고 로딩 바를 표시할 때)
    • 즉시 응답이 필요한 간단한 리소스 (예: 옵션 메뉴 아이콘, 미리보기용 작은 에셋)
  • 번들 상태를 변경하고 모드가 변경될 때 캐시를 클리어
    • 모드가 바뀔 때 에셋의 번들 상태를 전환
    • 이전에 사용하던 번들을 메모리에서 정리
  • 동기 로드 또는 메모리에 유지할 때 StreamableHandle을 사용
    • 에셋이 아직 메모리에 로드되지 않았는지, 로딩 중인지, 로딩 완료되었는지를 추적
    • 핸들이 살아있는 동안 참조가 유지, StreamableManager가 알아서 필요 없어진 에셋을 언로드
  • Load With Delegate vs Preload Plus Fallback
    • Delegate 방식: 비동기 로드 완료 후 람다 실행 (이벤트 기반)

    • Preload 방식: 미리 로드해두고, 나중에 즉시 접근 (데이터 캐시 기반)

      // Delegate 방식
      AssetManager.LoadPrimaryAsset(AssetId, Bundles, OnLoadedDelegate);
      
      // Preload 방식
      Handle = AssetManager.PreloadPrimaryAssets(AssetIds, Bundles, false);
  • 불안정한 람다함수를 딜리게이트에 절대 사용하면 안됨
    • 로드가 완료되기전 this가 파괴되면 크래시 발생
    • TWeakObjectPtr 사용

References

Asset Manager Explained | Inside Unreal

Unreal Engine - The Asset Manager, Primary Assets and Asset Bundles

Asset Manager - Gamedev Guide

profile
게임 AI 개발자 조정원입니다.

0개의 댓글