본 코드는 Lyra 샘플 프로젝트의 LyraAssetManager기반으로 작성 됨


AssetManager 동작 방식

AssetManager 등록

DefaultEngine.ini에 등록해서 엔진이 기본 AssetManager 대신 사용

AssetManagerClassName=/Script/ProjectFT.FTAssetManager

PrimaryAssetTypesToScan 등록

  1. 패키징에서 빠질 수 있는 에셋을 명시적으로 Cook 대상에 포함시키기
  2. AssetManager가 타입/ID 기반으로 쉽게 찾고 로드할 수 있게 만들기
[/Script/Engine.AssetManagerSettings]
+PrimaryAssetTypesToScan=(PrimaryAssetType="FTGameData",AssetBaseClass="/Script/ProjectFT.FTGameDataAsset",bHasBlueprintClasses=False,bIsEditorOnly=False,Directories=((Path="/Game/Data/Game")),SpecificAssets=,Rules=(Priority=-1,ChunkId=-1,bApplyRecursively=True,CookRule=AlwaysCook))
+PrimaryAssetTypesToScan=(PrimaryAssetType="FTItem",AssetBaseClass="/Script/ProjectFT.FTItemDataAsset",bHasBlueprintClasses=False,bIsEditorOnly=False,Directories=((Path="/Game/Data/Items")),SpecificAssets=,Rules=(Priority=-1,ChunkId=-1,bApplyRecursively=True,CookRule=AlwaysCook))

.ini 설정이 되어 있으면 아래와 같이 호출 가능.

if (UFTAssetManager* AssetManager = Cast<UFTAssetManager>(GEngine->AssetManager))
{
    return *AssetManager;
}

UE_LOG(LogFTAsset, Fatal, TEXT("Invalid AssetManagerClassName. It must be set to FTAssetManager."));

GameDataPath

GameDataPath는 .ini에서 설정 가능한 소프트 레퍼런스

UPROPERTY(Config)
TSoftObjectPtr<UFTGameDataAsset> GameDataPath;

.ini에 설정

[/Script/ProjectFT.FTAssetManager]
GameDataPath=/Game/Data/DA_GameData.DA_GameData

GameData

로드된 게임 데이터 에셋을 캐싱

UPROPERTY(Transient) // 런타임에만 유지됨
TObjectPtr<UFTGameDataAsset> GameData = nullptr;

LoadedAssets

로드한 에셋을 보관하는 Set
LoadedAssets에 넣어서 AssetManager가 해당 에셋을 계속 참조하게 해서 GC 대상이 안되게 함

UPROPERTY()
TSet<TObjectPtr<const UObject>> LoadedAssets;

LoadedAssetsCritical

Mutex가 들어있는 FCriticalSection 사용
LoadedAssets 접근을 보호하기 위한 락

FCriticalSection LoadedAssetsCritical

AssetManager::GetAsset

1. Load Object

기존:

UTexture2D* Icon = LoadObject<UTexture2D>(
    nullptr,
    TEXT("/Game/UI/Texture/T_ItemIcon.T_ItemIcon")
);

AssetManager/SoftReference 사용:

UTexture2D* Icon = UFTAssetManager::GetAsset(ItemData.ItemIcon);

차이:

  • 경로 문자열이 코드에서 사라짐
  • 에셋 위치 변경에 강해짐
  • ItemIconTSoftObjectPtr<UTexture2D>라 타입이 명확함

2. ConstructorHelpers X

기존:

static ConstructorHelpers::FObjectFinder<UStaticMesh> MeshFinder(
    TEXT("/Game/Assets/SM_Item.SM_Item")
);

ItemMesh->SetStaticMesh(MeshFinder.Object);

AssetManager 사용:

UStaticMesh* Mesh = UFTAssetManager::GetAsset(ItemData.ItemMesh);
ItemMesh->SetStaticMesh(Mesh);

차이:

  • 생성자에서 강제 로드하지 않음
  • 필요한 시점에 로드 가능
  • 생성자 코드가 에셋 경로에 묶이지 않음

3. 동기 로드, 비동기 로드 선택 가능

작고 즉시 필요한 에셋:

UTexture2D* Icon = UFTAssetManager::GetAsset(ItemData.ItemIcon);

큰 에셋이나 화면 전환 중 비동기 로드:

UFTAssetManager::Get().LoadAssetAsync<UStaticMesh>(
    ItemData.ItemMesh,
    [this](UStaticMesh* LoadedMesh)
    {
        if (!LoadedMesh)
        {
            return;
        }

        ItemMesh->SetStaticMesh(LoadedMesh);
    });

차이:

  • 기존에는 LoadObject/TryLoad로 순간 끊김이 생김
  • 비동기 로드를 선택하면 UI나 로딩 화면과 연결 가능

4. Cast

기존:

UObject* LoadedObject = SoftPath.TryLoad();
UFTItemDataAsset* ItemData = Cast<UFTItemDataAsset>(LoadedObject);

AssetManager 사용:

UFTItemDataAsset* ItemData = UFTAssetManager::GetAsset(ItemDataRef);

차이:

  • 사용처에서 Cast 반복이 줄어듦
  • 타입이 TSoftObjectPtr<UFTItemDataAsset>에 이미 들어 있음

5. Widget 생성 코드

기존:

UClass* WidgetClass = LoadClass<UUserWidget>(
    nullptr,
    TEXT("/Game/UI/HUD/WBP_MainHUD.WBP_MainHUD_C")
);

UUserWidget* Widget = CreateWidget<UUserWidget>(PC, WidgetClass);

AssetManager 사용:

TSubclassOf<UFTMainHUDWidget> HUDClass =
    UFTAssetManager::GetSubclass(GameData.MainHUDWidgetClass);

UFTMainHUDWidget* HUD =
    CreateWidget<UFTMainHUDWidget>(PC, HUDClass);

차이:

  • _C 문자열을 직접 만들 필요 없음
  • WidgetClass를 DataAsset에서 지정 가능
  • UIManager가 어떤 위젯을 띄울지 데이터로 바꿀 수 있음

6. 아이템/퀘스트 데이터 접근 방식

기존:

UFTItemDataAsset* ItemData = LoadObject<UFTItemDataAsset>(
    nullptr,
    TEXT("/Game/Data/Items/DA_Milk.DA_Milk")
);

AssetManager 사용:

FPrimaryAssetId ItemAssetId(TEXT("FTItem"), TEXT("DA_Milk"));

UFTAssetManager& AssetManager = UFTAssetManager::Get();

AssetManager.LoadPrimaryAsset(
    ItemAssetId,
    {},
    FStreamableDelegate::CreateLambda([]()
    {
        // 로드 완료 후 사용
    }));

또는 이미 soft reference가 있으면:

UFTItemDataAsset* ItemData = UFTAssetManager::GetAsset(ItemDataRef);

차이:

  • 특정 에셋을 이름/타입으로 관리 가능
  • 모든 FTItem을 스캔해서 목록화가 가능

UFTAssetManager 장점

에셋 보관

기존:

UObject* Loaded = Path.TryLoad();
// 어디에서도 UPROPERTY로 잡지 않으면 GC 대상이 될 수 있음

AssetManager 사용:

UTexture2D* Icon = UFTAssetManager::GetAsset(ItemData.ItemIcon, true);

차이:

  • bKeepInMemory = true면 AssetManager가 LoadedAssets에 보관
  • 반복 사용 에셋은 다시 로드하지 않게 관리 가능

에셋 직접 로드 X

기존:

void AFTItemActor::BeginPlay()
{
    UFTItemDataAsset* Data = LoadObject<UFTItemDataAsset>(
        nullptr,
        TEXT("/Game/Data/Items/DA_Cleaner.DA_Cleaner")
    );

    ApplyItemData(Data);
}

개선:

void AFTItemActor::ApplyItemData(UFTItemDataAsset* ItemData)
{
    if (!ItemData)
    {
        return;
    }

    UStaticMesh* Mesh = UFTAssetManager::GetAsset(ItemData->ItemData.ItemMesh);
    ItemMeshComponent->SetStaticMesh(Mesh);
}

차이:

  • Actor는 “어디서 데이터를 찾을지”를 몰라도 됨
  • 스폰하는 쪽, 인벤토리, 상점, DataAsset이 참조를 넘겨줌
  • Actor 책임이 줄어듦

로딩 화면 생성 가능

PX 방식:

AssetManager가 LoadingWidget 생성
AssetManager가 에셋 로드
AssetManager가 OpenLevel

우리 방향:

GameFlowSubsystem이 로딩 요청
UIManager가 LoadingWidget 표시
AssetManager가 에셋 로드만 담당
GameFlowSubsystem이 레벨 전환

차이:

  • AssetManager가 UI/레벨 전환을 직접 하지 않음
  • 실패/탈출/레벨 전환 규칙과 충돌하지 않음

에셋 테스트/디버깅이 획일화 됨

기존에는 “경로가 맞나?”를 코드에서 봐야 합니다.

"/Game/UI/HUD/WBP_MainHUD.WBP_MainHUD_C"

AssetManager 사용 후에는:

  • DataAsset에 참조가 제대로 들어갔는지 확인
  • PrimaryAssetTypesToScan 설정이 맞는지 확인
  • AssetManager 로그로 로드 성공/실패 확인

예상 디버그 함수:

UFTAssetManager::DumpLoadedAssets();

코드 사용 요약

// 경로 문자열로 직접 로드
LoadObject / LoadClass / TryLoad / ConstructorHelpers

// 타입 있는 참조를 AssetManager로 로드
TSoftObjectPtr / TSoftClassPtr / PrimaryAssetId / GetAsset / GetSubclass / LoadAssetAsync

ActorUI 코드는 에셋 경로를 덜 알고, DataAssetManager가 참조와 로딩 책임을 가져감.

반응형

'Unreal Engine' 카테고리의 다른 글

UE Input 1  (0) 2026.06.23
GameplayTag Static vs GameplayTag Extern  (0) 2026.06.22
UE Game play Framework 2  (0) 2026.06.18
UE Game play Framework 1  (0) 2026.06.17
GAS 2  (0) 2026.06.16