패키지

언리얼 오브젝트 패키지는 다수의 언리얼 오브젝트를 포장하는 단위로, 모든 언리얼 오브젝트는 패키지에 포함됨

언리얼 오브젝트 패키지의 언리얼 오브젝트를 에셋이라고 하며, 에디터에 이것이 노출됨

구조상 패키지는 다수의 언리얼 오브젝트를 소유할 수 있으나 일반적으로 하나의 에셋만 가지고, 에셋은 다수의 서브 오브젝트 보유 가능

 

패키지의 중의적 개념

언리얼 오브젝트를 감싼 포장 오브젝트

개발된 최종 컨텐츠를 정리해 프로그램으로 만드는 작업(게임 패키징)

DLC(DownLoadable Contents)와 같이 향후 확장 컨텐츠에 사용되는 별도의 데이터 묶음(패키지 파일)

 

// 프로젝트는 고유한 경로를 가짐
// Game은 게임에서 사용되는 에셋들을 모아놓은 대표 폴더
// Temp는 Saved 폴더
const FString UMyGameInstance::PackageName = TEXT("/Game/Student");

// 패키지 저장
void UMyGameInstance::SaveStudentPackage() const
{
	// 기존 패키지가 있는 경우 안전하게 하기 위함
	TObjectPtr<UPackage> StudentPackage = ::LoadPackage(nullptr, *PackageName, LOAD_None);
	if (StudentPackage)
	{
		StudentPackage->FullyLoad();
	}
	else
	{
		StudentPackage = CreatePackage(*PackageName);
	}

	EObjectFlags ObjectFlag = RF_Public | RF_Standalone;

	// 에셋 설정
	TObjectPtr<UStudent> TopStudent = NewObject<UStudent>(StudentPackage, UStudent::StaticClass(), *AssetName, ObjectFlag);
	TopStudent->SetName(TEXT("이득우"));
	TopStudent->SetOrder(36);

	// 서브 오브젝트 설정
	const uint8 NumOfSubSize = 10;
	for (uint8 Index = 1; Index <= NumOfSubSize; ++Index)
	{
		FString SubObjectName = FString::Printf(TEXT("Stduent%d"), Index);
		TObjectPtr<UStudent> SubStudent = NewObject<UStudent>(TopStudent, UStudent::StaticClass(), *SubObjectName, ObjectFlag);
		SubStudent->SetName(FString::Printf(TEXT("학생%d"), Index));
		SubStudent->SetOrder(Index);
	}

	// 패키지 저장
	const FString PackageFileName = FPackageName::LongPackageNameToFilename(PackageName, FPackageName::GetAssetPackageExtension());
	FSavePackageArgs SaveArgs;
	SaveArgs.TopLevelFlags = ObjectFlag;

	if (UPackage::SavePackage(StudentPackage, nullptr, *PackageFileName, SaveArgs))
	{
		UE_LOG(LogTemp, Log, TEXT("패키지 저장"));
	}
}

// 패키지 로드
void UMyGameInstance::LoadStudentPackage() const
{
	TObjectPtr<UPackage> StudentPackage = ::LoadPackage(nullptr, *PackageName, LOAD_None);
	if (StudentPackage == nullptr)
	{
		UE_LOG(LogTemp, Log, TEXT("패키지 없음"));
		return;
	}

	StudentPackage->FullyLoad();

	TObjectPtr<UStudent> TopStudent = FindObject<UStudent>(StudentPackage, *AssetName);
	PrintStudentInfo(TopStudent, TEXT("FindObject Asset"));
}

 

에셋 저장/로딩 전략

패키지를 불러 할당하는 작업은 부하가 크기 때문에 오브젝트의 경로로 대체해 사용

경로는 프로젝트 내에 유일함을 보장하기 때문에 에셋의 키로 사용

패키지 내 데이터를 모두 로드하지 않고 오브젝트 경로를 사용해 필요한 에셋만 로드가능 : {에셋클래스정보}'{패키지이름}.{에셋이름}' 또는 {패키지이름}.{에셋이름}

 

프로젝트에서 에셋이 반드시 필요한 경우 생성자 코드에서 미리 로딩

런타임에서 필요할 때 로딩하는 경우는 런타임 로직에서 관리자를 사용해 비동기 로딩

 

에셋 참조

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/referencing-assets-in-unreal-engine?application_version=5.1

 

강참조

직접 프로퍼티 참조

언리얼 오브젝트를 선언할 때 해당 타입을 명시적으로 지정한 것

 

생성 시간 참조

생성자 코드에서 해당 오브젝트가 가리키고 있는 에셋을 생성

엔진이 초기화 될 때 생성자가 실행되므로 게임이 실행되기 전에 해당 에셋이 로딩

 

약참조

간접 프로퍼티 참조 : TSoftObjectPtr 사용하여 필요할 때 로딩

오브젝트 검색/로드 : 로드되어 있지 않은 오브젝트는 LoadObject<>()를 사용

 

에셋 스트리밍 관리자

에셋의 비동기 로딩을 지원하는 객체

컨텐츠 제작과 무관한 싱글톤 객체(GameInstance 등)에 FStreamableManager를 선언

다수의 오브젝트 경로를 입력해 다수의 에셋을 로딩하는 것도 가능

// 에셋 비동기 로드
const FString TopSoftObjectPath = FString::Printf(TEXT("%s.%s"), *PackageName, *AssetName);
StreamableHandle = StreamableManager.RequestAsyncLoad(TopSoftObjectPath,
	[&]()
	{
		if (StreamableHandle.IsValid() && StreamableHandle->HasLoadCompleted())
		{
			TObjectPtr<UStudent> TopStudent = Cast<UStudent>(StreamableHandle->GetLoadedAsset());
			if (TopStudent)
			{
				PrintStudentInfo(TopStudent, TEXT("AsyncLoad"));

				StreamableHandle->ReleaseHandle();
				StreamableHandle.Reset();
			}
		}
	}
);

+ Recent posts

목차