일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- Double free
- 메카님
- ret2libc
- stride
- Rr
- AINCAA
- RBAC
- 유스케이스
- 게임 개발
- 운영체제
- dirty cow
- frequency-domain spectrum analysis
- Unity #Indie Game
- dtft
- Race condition
- pdlc
- CTF
- MLFQ
- 언리얼엔진
- MAC
- TSet
- STCF
- 배경 그림
- linear difference equation
- Security
- DSP
- sampling theory
- 게임개발
- 유니티
- DP
- Today
- Total
다양한 기록
[UE] Package 본문
언리얼 오브젝트 패키지
복잡한 계층 구조를 가진 언리얼 오브젝트를 효과적으로 저장, 로드하는 방법을 통일해야 함
=> UPackage 단위로 언리얼 오브젝트를 관리
* 패키지는 여러 뜻을 가짐
- 언리얼 오브젝트를 감싼 포장 오브젝트
- 개발된 최종 컨텐츠를 정리해 프로그램으로 만드는 작업 (게임 패키징)
- 향후 확장 컨텐츠에 사용되는 별도의 데이터 묶음 (pkg 파일)
패키지와 애셋
- 언리얼 패키지 오브젝트 : 다수의 언리얼 오브젝트를 포장하는데 사용하는 언리얼 오브젝트
- 모든 언리얼 오브젝트는 패키지에 속함 (Transitent Package, 임시 패키)
- 언리얼 오브젝트 패키지의 서브 오브젝트를 애셋이라고 하며 에디터에 노출
- 패키지는 다수의 언리얼 오브젝트를 소유할 수 있으나 일반적으로는 하나의 애셋만 가짐
- 애셋은 다시 다수의 서브오브젝트를 가질 수 있으며, 모두 언리얼 오브젝트 패키지에 포함됨
- 하지만 에디터에 노출되지 않음
// Fill out your copyright notice in the Description page of Project Settings.
#include "MyGameInstance.h"
#include "Student.h"
#include "JsonObjectConverter.h"
#include "UObject/SavePackage.h"
// /Game은 게임에서 사용하는 애셋들을 모아놓은 대표 폴더
const FString UMyGameInstance::PackageName = TEXT("/Game/Student");
const FString UMyGameInstance::AssetName = TEXT("TopStudent");
void PrintStudentInfo(const UStudent* InStudent, const FString& InTag)
{
UE_LOG(LogTemp, Log, TEXT("[%s] 이름 %s 순번 %d"), *InTag, *InStudent->GetName(), InStudent->GetOrder());
}
void UMyGameInstance::Init()
{
Super::Init();
const FString SavedDir = FPaths::Combine(FPlatformMisc::ProjectDir(), TEXT("Saved"));
StudentSrc = NewObject<UStudent>();
StudentSrc->SetName(TEXT("라구넹"));
StudentSrc->SetOrder(31);
SaveStudentPackage();
LoadStudentPackage();
}
void UMyGameInstance::SaveStudentPackage() const
{
UPackage* StudentPackage = ::LoadPackage(nullptr, *PackageName, LOAD_None);
if ( StudentPackage )
{
StudentPackage->FullyLoad();
}
StudentPackage = CreatePackage(*PackageName);
EObjectFlags ObjectFlag = RF_Public | RF_Standalone;
// 인자 없으면 임시 패키지 안에 저장됨
UStudent* TopStudent = NewObject<UStudent>(
StudentPackage, UStudent::StaticClass(), *AssetName, ObjectFlag);
TopStudent->SetName(TEXT("라구넹"));
TopStudent->SetOrder(72);
const int32 NumofSubs = 10;
for (int32 i = 1; i <= NumofSubs; ++i)
{
FString SubObjectName = FString::Printf(TEXT("Student%d"), i);
UStudent* SubStudent = NewObject<UStudent>(
TopStudent, UStudent::StaticClass(), *SubObjectName, ObjectFlag);
SubStudent->SetName(FString::Printf(TEXT("학생%d"), i));
SubStudent->SetOrder(i);
}
const FString PackageFileName =
FPackageName::LongPackageNameToFilename(PackageName, FPackageName::GetAssetPackageExtension());
FSavePackageArgs SavePackageArgs;
SavePackageArgs.TopLevelFlags = ObjectFlag;
if ( UPackage::SavePackage(StudentPackage, nullptr, *PackageFileName, SavePackageArgs))
{
UE_LOG(LogTemp, Log, TEXT("패키지가 성공적으로 저장되었습니다."));
}
}
void UMyGameInstance::LoadStudentPackage() const
{
UPackage* StudentPackage = ::LoadPackage(nullptr, *PackageName, LOAD_None);
if ( nullptr == StudentPackage )
{
UE_LOG(LogTemp, Log, TEXT("텍스트를 찾을 수 없습니다."));
return;
}
StudentPackage->FullyLoad();
UStudent* TopStudent = FindObject<UStudent>(StudentPackage, *AssetName);
PrintStudentInfo(TopStudent, TEXT("Find Object Asset"));
}
패키지를 저장하고 로드
단, 컨텐츠 브라우저에 보이지는 않음
언리얼 5.4 이후부터 Factory가 아니면 애셋 생성이 안됨
저것도 실습은 되는데, 껐다 키고 다시 플레이하면 SavePackage 함수에서 크래시 발생함
애셋 정보의 저장과 로딩
애셋 간 연결을 위해 매번 패키지 부르기 -> 부하가 큼
- 애셋 로딩 대신 패키지와 오브젝트를 지정한 문자열을 대체해 사용, 오브젝트 경로 라고 함
- 프로젝트 내의 오브젝트 경로는 유니크함
애셋의 로딩 전략
- 프로젝트에서 애셋이 반드시 필요 -> 생성자 코드에서 미리 로딩
- 런타임에서 필요할 때 바로 로딩하는 경우 -> 런타임 로직에서 정적 로딩
- 런타임에서 비동기적으로 로딩하는 경우 -> 런터임 로직에서 관리자를 사용해 비동기 로딩
오브젝트 경로(Object Path)
- 패키지 이름과 애셋 이름을 한 데 묶은 문자열
- 애셋 클래스 정보는 생략 가능
- 패키지 내 데이터를 모두 로드하지 않고 오브젝트 경로를 사용해 필요한 애셋만 로드할 수 있음
{애셋클래스정보}'{패키지이름}.{애셋이름}'
또는
{패키지이름}.{애셋이름}
애셋 참조
강참조와 약참조 존재
약참조
- 오브젝트 경로같은 문자열 형태를 사
강참조
직접 프로퍼티 참조
- 언리얼 오브젝트의 헤더에서 언리얼 오브젝트를 선언할 때, 해당 타입을 명시적으로 지정한 것'
생성 시간 참조
- 해당 오브젝트가 가리키고 있는 애셋을 생성자 코드에서 생성해주는 것을 의미
- 생성자는 엔진 초기화 시 불러지는데, 게임 실행 전에 해당 애셋이 로딩됨
간접 프로퍼티 참조
- TSoftObjectPath 을 사용해 LoadObject, StatucLoadObject, FStreamingManager를 사용해서 로딩 가능
오브젝트 검색/로드
UPROPERTY 기반으로 레퍼런스를 구할 때,
생성, 로드된 오브젝트만 사용하려는 경우 FindObject<>()가 맞음 (패키지가 이미 메모리에 로드된 경우)
로드되어 있지 않다면 LoadObject로 원하는 패키지 로딩 가능
애셋 스트리밍 관리자 (Streamable Manager)
- 애셋의 비동기 로딩을 지원하는 관리자 객체
- 컨첸츠 제작과 무관한 싱글턴 클래스에 FStreamableManager 선언 시 좋음 * GameInstance도 좋음
- 애셋의 동기/비동기 로딩을 관리 가능하며, 다수의 오브젝트 경로를 입력해 다수의 애셋 로딩도 가능
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Engine/GameInstance.h"
#include "Engine/StreamableManager.h"
#include "MyGameInstance.generated.h"
struct FStudentData
{
FStudentData() {}
FStudentData(int32 InOrder, const FString& InName) : Order(InOrder), Name(InName) {}
friend FArchive& operator<<(FArchive& Ar, FStudentData& InStudentData)
{
Ar << InStudentData.Order;
Ar << InStudentData.Name;
return Ar;
}
int32 Order = -1;
FString Name = TEXT("홍길동");
};
/**
*
*/
UCLASS()
class UNREALSERIALIZATION_API UMyGameInstance : public UGameInstance
{
GENERATED_BODY()
public:
UMyGameInstance();
virtual void Init() override;
void SaveStudentPackage() const;
void LoadStudentPackage() const;
void LoadStudentObject() const;
private:
static const FString PackageName;
static const FString AssetName;
UPROPERTY()
TObjectPtr<class UStudent> StudentSrc;
FStreamableManager StreamableManager;
TSharedPtr<FStreamableHandle> Handle;
};
// Fill out your copyright notice in the Description page of Project Settings.
#include "MyGameInstance.h"
#include "Student.h"
#include "JsonObjectConverter.h"
#include "UObject/SavePackage.h"
// /Game은 게임에서 사용하는 애셋들을 모아놓은 대표 폴더
const FString UMyGameInstance::PackageName = TEXT("/Game/Student");
const FString UMyGameInstance::AssetName = TEXT("TopStudent");
void PrintStudentInfo(const UStudent* InStudent, const FString& InTag)
{
UE_LOG(LogTemp, Log, TEXT("[%s] 이름 %s 순번 %d"), *InTag, *InStudent->GetName(), InStudent->GetOrder());
}
UMyGameInstance::UMyGameInstance()
{
const FString TopSoftObjectPath = FString::Printf(TEXT("$s.%s"), *PackageName, *AssetName);
static ConstructorHelpers::FObjectFinder<UStudent> UASSET_TOPSTUDENT(*TopSoftObjectPath);
if ( UASSET_TOPSTUDENT.Succeeded() )
{
PrintStudentInfo(UASSET_TOPSTUDENT.Object, TEXT("Constructor"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("Load Failed .."));
}
}
void UMyGameInstance::Init()
{
Super::Init();
const FString SavedDir = FPaths::Combine(FPlatformMisc::ProjectDir(), TEXT("Saved"));
StudentSrc = NewObject<UStudent>();
StudentSrc->SetName(TEXT("라구넹"));
StudentSrc->SetOrder(31);
//SaveStudentPackage();
//LoadStudentPackage();
//LoadStudentObject();
const FString TopSoftObjectPath = FString::Printf(TEXT("$s.%s"), *PackageName, *AssetName);
Handle = StreamableManager.RequestAsyncLoad(TopSoftObjectPath,
[&]()
{
if ( Handle.IsValid() && Handle->HasLoadCompleted() )
{
UStudent* TopStudent = Cast<UStudent>(Handle->GetLoadedAsset());
if ( TopStudent )
{
PrintStudentInfo(TopStudent, TEXT("AsyncLoad"));
Handle->ReleaseHandle();
Handle.Reset();
}
}
}
);
}
void UMyGameInstance::SaveStudentPackage() const
{
UPackage* StudentPackage = ::LoadPackage(nullptr, *PackageName, LOAD_None);
if ( StudentPackage )
{
StudentPackage->FullyLoad();
}
StudentPackage = CreatePackage(*PackageName);
EObjectFlags ObjectFlag = RF_Public | RF_Standalone;
// 인자 없으면 임시 패키지 안에 저장됨
UStudent* TopStudent = NewObject<UStudent>(
StudentPackage, UStudent::StaticClass(), *AssetName, ObjectFlag);
TopStudent->SetName(TEXT("라구넹"));
TopStudent->SetOrder(72);
const int32 NumofSubs = 10;
for (int32 i = 1; i <= NumofSubs; ++i)
{
FString SubObjectName = FString::Printf(TEXT("Student%d"), i);
UStudent* SubStudent = NewObject<UStudent>(
TopStudent, UStudent::StaticClass(), *SubObjectName, ObjectFlag);
SubStudent->SetName(FString::Printf(TEXT("학생%d"), i));
SubStudent->SetOrder(i);
}
const FString PackageFileName =
FPackageName::LongPackageNameToFilename(PackageName, FPackageName::GetAssetPackageExtension());
FSavePackageArgs SavePackageArgs;
SavePackageArgs.TopLevelFlags = ObjectFlag;
if ( UPackage::SavePackage(StudentPackage, nullptr, *PackageFileName, SavePackageArgs))
{
UE_LOG(LogTemp, Log, TEXT("패키지가 성공적으로 저장되었습니다."));
}
}
void UMyGameInstance::LoadStudentPackage() const
{
UPackage* StudentPackage = ::LoadPackage(nullptr, *PackageName, LOAD_None);
if ( nullptr == StudentPackage )
{
UE_LOG(LogTemp, Log, TEXT("텍스트를 찾을 수 없습니다."));
return;
}
StudentPackage->FullyLoad();
UStudent* TopStudent = FindObject<UStudent>(StudentPackage, *AssetName);
PrintStudentInfo(TopStudent, TEXT("Find Object Asset"));
}
void UMyGameInstance::LoadStudentObject() const
{
const FString TopSoftObjectPath = FString::Printf(TEXT("$s.%s"), *PackageName, *AssetName);
UStudent* TopStudent = LoadObject<UStudent>(nullptr, *TopSoftObjectPath);
PrintStudentInfo(TopStudent, TEXT("Loaf Object Asset"));
}
패키지째로 로드하고, 오브젝트 경로로 런타임, 생성자, 비동기 등등의 방법이 있긴 한데
위 코드는 정상적으로 작동하지 않음
언리얼 5.4 이후로 애셋 생성이 Factory를 통해서만 가능하도록 바뀌었음
일단 그냥 생성자, 런타임에 정적으로, 런타임에 비동기적으로 처리가 가능하다 정도만 학습
'언리얼 엔진 > Unreal C++' 카테고리의 다른 글
[UE] Unreal Build System (0) | 2025.01.02 |
---|---|
[UE] Serialization (직렬화) (0) | 2024.12.30 |
[UE] Memory Management (0) | 2024.12.29 |
[UE] Unreal Container Library / Struct, Map (0) | 2024.12.28 |
[UE] Unreal Container Library / TArray, TSet (0) | 2024.12.28 |