총 세 가지의 클래스를 만든다.
- GameInstanceSubsystem → ItemManagerSubsystem
- PrimaryDataAsset → ItemDataAsset
- 플레이하고 아이템 장착 할 액터 → ItemManagerTestCharacter

#include "Engine/AssetManager.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnItemLoaded, FPrimaryAssetId, AssetId);
public:
// 서브시스템 초기화 시 호출
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
// 아이템 비동기 로드 요청
UFUNCTION(BlueprintCallable, Category = "ItemManager")
void RequestAsyncLoadItem(FPrimaryAssetId AssetId);
// 로드된 아이템 가져오기 (이미 로드된 경우만 반환)
UFUNCTION(BlueprintCallable, Category = "ItemManager")
class UMyItemDataAsset* GetItemData(FPrimaryAssetId AssetId);
// 아이템 메모리 해제
UFUNCTION(BlueprintCallable, Category = "ItemManager")
void UnloadItem(FPrimaryAssetId AssetId);
//델리게이트
UPROPERTY(BlueprintAssignable, Category = "ItemManager")
FOnItemLoaded OnItemLoaded;
private:
void OnLoadCompleted(FPrimaryAssetId AssetId);
// 로드된 핸들을 보관하여 가비지 컬렉션을 방지
TMap<FPrimaryAssetId, TSharedPtr<FStreamableHandle>> LoadingHandles;
void UItemManagerSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
UE_LOG(LogTemp, Log, TEXT("Item Manager Subsystem Initialized!"));
}
void UItemManagerSubsystem::RequestAsyncLoadItem(FPrimaryAssetId AssetId)
{
if (!AssetId.IsValid()) return;
// 이미 로드 중이거나 로드 완료되었는지 확인
if (LoadingHandles.Contains(AssetId) && LoadingHandles[AssetId]->IsActive())
{
return;
}
UAssetManager& Manager = UAssetManager::Get();
// 비동기 로드 시작
TSharedPtr<FStreamableHandle> Handle = Manager.LoadPrimaryAsset(
AssetId,
TArray<FName>(),
FStreamableDelegate::CreateUObject(this, &UItemManagerSubsystem::OnLoadCompleted, AssetId)
);
LoadingHandles.Add(AssetId, Handle);
}
UMyItemDataAsset* UItemManagerSubsystem::GetItemData(FPrimaryAssetId AssetId)
{
// 에셋 매니저를 통해 메모리에 올라온 객체 포인터를 즉시 반환
return UAssetManager::Get().GetPrimaryAssetObject<UMyItemDataAsset>(AssetId);
}
void UItemManagerSubsystem::UnloadItem(FPrimaryAssetId AssetId)
{
if (LoadingHandles.Contains(AssetId))
{
// 핸들을 해제하여 에셋 매니저가 참조를 끊게 함 (GC 대상이 됨)
LoadingHandles.Remove(AssetId);
UAssetManager::Get().UnloadPrimaryAsset(AssetId);
UE_LOG(LogTemp, Log, TEXT("Item Unloaded: %s"), *AssetId.ToString());
}
}
void UItemManagerSubsystem::OnLoadCompleted(FPrimaryAssetId AssetId)
{
UE_LOG(LogTemp, Log, TEXT("Item Loaded via Subsystem: %s"), *AssetId.ToString());
// 로드 완료 알림
if (OnItemLoaded.IsBound())
{
OnItemLoaded.Broadcast(AssetId);
}
}
Primary Data Asset

public:
// 에셋 매니저에서 식별할 타입 이름 (예: "Weapon")
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Item")
FPrimaryAssetType ItemType;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Stats")
float AttackDamage;
// 무거운 에셋은 SoftObjectPtr로 선언해서 로드 시점을 제어합니다.
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Visual")
TSoftObjectPtr<USkeletalMesh> ItemMesh;
// ID를 반환하는 함수 오버라이드 (Asset Manager가 식별할 때 사용)
virtual FPrimaryAssetId GetPrimaryAssetId() const override
{
return FPrimaryAssetId(ItemType, GetFName());
}
'게임 개발 > 학습 일지' 카테고리의 다른 글
| 언리얼 내배캠 - 언리얼 멀티플레이 기초 NetMode, NetConnection, NetDriver (0) | 2026.06.17 |
|---|---|
| 언리얼 내배캠 - 언리얼 멀티플레이 기초 실습 환경 설정 (0) | 2026.06.16 |
| 언리얼 내배캠 마스터반 수업 - Subsystem과 수명 관리 (0) | 2026.06.10 |
| 언리얼 내배캠 - 언리얼 멀티플레이 기초 채팅 프로젝트 생성 (0) | 2026.06.09 |
| 언리얼 내배캠 마스터반 - 소프트 레퍼런스 (0) | 2026.06.08 |