일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- JavaScript
- 2023 게이밍 인 구글 클라우드
- 산토리 하이볼
- 스즈메의 문단속
- 리팩터링 3장
- 112일선
- GenAI
- 작계훈련
- 주식
- 224일선
- 리팩터링
- 언리얼 5
- 448일선
- shader
- 이득우의 언리얼 프로그래밍1
- 2023 게이밍
- URP
- 전주비빔 라이스 버거
- 이득우의 언리얼 프로그래밍 1
- 스포일러 주의
- 리팩터링 4장
- 공부
- 2023 구글 클라우드
- 상계9동
- 2023 Gaming
- 언리얼5
- 구글 컨퍼런스
- 주식단테
- 1일차
- unity
- Today
- Total
개발 이야기 안하는 개발자
언리얼5_1_4 : 언리얼 프로젝트의 에셋 본문
직렬화
오브젝트나 연결된 오브젝트의 묶음을 바이트 스트림으로 변환하는 과정
복잡한 데이터를 일렬로 세우기 때문.
데이터 압축 , 암호화를 통해 데이터를 효율적이고 안전하게 보관할 수 있다.
파일경로 알아오기 (Saved 파일까지)
const FString SaveDir = FPaths::Combine(FPlatformMisc::ProjectDir(), TEXT("Saved"));
FArchive라고 하는 파일 경로 끝에 데이터를 담을 수 있다.
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;
FString Name;
};
...
const FString RawDataFileName(TEXT("RawData.bin"));
FString RawDataAbsolutePath = FPaths::Combine(*SaveDir, *RawDataFileName); //전체 파일 경로
FPaths::MakeStandardFilename(RawDataAbsolutePath); //변경할 파일 폴더
FArchive* RawFileWriterAr = IFileManager::Get().CreateFileWriter(*RawDataAbsolutePath);
if (nullptr != RawFileWriterAr)
{
*RawFileWriterAr << RawDataSrc; //직렬화 담기
RawFileWriterAr->Close();
delete RawFileWriterAr;
RawFileWriterAr = nullptr;
}
FStudentData RawDataDest;
FArchive* RawFileReaderAR = IFileManager::Get().CreateFileReader(*RawDataAbsolutePath);
if (nullptr != RawFileReaderAR)
{
*RawFileReaderAR << RawDataDest;
RawFileReaderAR->Close();
delete RawFileReaderAR;
RawFileReaderAR = nullptr;
}
언리얼에서 오브젝트 직렬화
언리얼 오브젝트는 Serialize 가 이미 구현이 되어있기 때문에 오버라이드 해서 사용하면 된다.
virtual void Serialize(FArchive& Ar) override;
언리얼 스마트 포인터(TUniquePtr<"targetClass"> )를 사용해서 쓰고 읽기
//경로 설정
const FString ObjectDataFileName(TEXT("ObjectData.bin"));
FString ObjectDataAbsolutePath = FPaths::Combine(*SaveDir, *ObjectDataFileName);
FPaths::MakeStandardFilename(ObjectDataAbsolutePath);
//담을 데이터 직렬화
TArray<uint8> BufferArray;
FMemoryWriter MemoryWriterAr(BufferArray);
StudentSrc->Serialize(MemoryWriterAr);
//쓰기
if (TUniquePtr<FArchive> FileWriterAr = TUniquePtr<FArchive>(IFileManager::Get().CreateFileWriter(*ObjectDataAbsolutePath)))
{
*FileWriterAr << BufferArray;
FileWriterAr->Close();
}
//읽기
TArray<uint8> BufferArrayFromFile;
if (TUniquePtr<FArchive> FileReaderAr = TUniquePtr<FArchive>(IFileManager::Get().CreateFileReader(*ObjectDataAbsolutePath)))
{
*FileReaderAr << BufferArrayFromFile;
FileReaderAr->Close();
}
FMemoryReader MemoryReaderAr(BufferArrayFromFile);
UStudent* StudentDest = NewObject<UStudent>();
StudentDest->Serialize(MemoryReaderAr);
Json 직렬화
언리얼에서도 지원하는 Json 직렬화가 있다.
주의할 점은 프로젝트 Build.cs에서 플러그인을 추가해주어야 한다. Json , JsonUtilites
//쓸 파일의 경로
FString JsonDataFileName(TEXT("StudentJsonData.txt"));
FString JsonDataAbsolutePath = FPaths::Combine(*SaveDir, *JsonDataFileName);
FPaths::MakeStandardFilename(JsonDataAbsolutePath);
//Json을 사용하기 위한 객체 생성
TSharedRef<FJsonObject> JsonObjectSrc = MakeShared<FJsonObject>();
FJsonObjectConverter::UStructToJsonObject(StudentSrc->GetClass(), StudentSrc, JsonObjectSrc);
//직결화 진행 및 파일에 쓰기
FString JsonOutString;
TSharedRef<TJsonWriter<TCHAR>> JsonWritedAr = TJsonWriterFactory<TCHAR>::Create(&JsonOutString);
if (FJsonSerializer::Serialize(JsonObjectSrc, JsonWritedAr))
{
FFileHelper::SaveStringToFile(JsonOutString, *JsonDataAbsolutePath);
}
//파일에서 데이터 읽어오기
FString JsonInString;
FFileHelper::LoadFileToString(JsonInString, *JsonDataAbsolutePath);
//Json을 사용하기 위한 객체 생성
TSharedPtr<FJsonObject> JsonObjectDest;
TSharedRef<TJsonReader<TCHAR>> JsonReaderAr = TJsonReaderFactory<TCHAR>::Create(JsonInString);
//역직렬화 진행 및 데이터 가져오기
if (FJsonSerializer::Deserialize(JsonReaderAr, JsonObjectDest))
{
UStudent* JsonStudentDest = NewObject<UStudent>();
if(FJsonObjectConverter::JsonObjectToUStruct(JsonObjectDest.ToSharedRef(), JsonStudentDest->GetClass(), JsonStudentDest))
{
...
}
}
패키지
언리얼 오브젝트를 감싼 포장 오브젝트.
DLC와 같은 확장 컨텐츠에 사용되는 별도의 데이터 묶음.
언리얼 오브젝트 패키지는 보통 하나의 에셋을 갖는다. (다중이여도 상관 없음)
이 하나의 에셋에는 다양한 서브오브젝트들을 갖는다.
//PackageName이라고 하는 패키지 생성
StudentPackage = CreatePackage(*PackageName);
EObjectFlags ObjectFlag = RF_Public | RF_Standalone;
//에셋을 제작할 거고, 이 에셋은 패키지하위에 들어감)
UStudent* TopStudent = NewObject<UStudent>(StudentPackage, UStudent::StaticClass(), *AssetName, ObjectFlag);
TopStudent->SetName(TEXT("MyPackage"));
TopStudent->SetOrder(36);
//총 10개의 다른 서브오브젝트를 제작, 각각 에셋하위에 들어감
const int32 NumofSubs = 10;
for (int32 ix = 1; ix <= NumofSubs; ix++)
{
FString SubObjectName = FString::Printf(TEXT("Student%d"), ix);
UStudent* SubStudent = NewObject<UStudent>(TopStudent, UStudent::StaticClass(), *SubObjectName, ObjectFlag);
SubStudent->SetName(FString::Printf(TEXT("Student%d"), ix));
SubStudent->SetOrder(ix);
}
//패키지를 경로에 저장
const FString PackageFileName = FPackageName::LongPackageNameToFilename(PackageName, FPackageName::GetAssetPackageExtension());
FSavePackageArgs SaveArgs;
SaveArgs.TopLevelFlags = ObjectFlag;
if (UPackage::SavePackage(StudentPackage, nullptr, *PackageFileName, SaveArgs))
{
...
}
적혀있는 Student가 Package의 이름.
TopStudent가 에셋이다.
패키지 로드
//패키지 찾기
UPackage* StudentPackage = ::LoadPackage(nullptr, *PackageName, LOAD_None);
if (nullptr == StudentPackage)
{
//못찾은 경우
return;
}
//찾은 패키지 로드
StudentPackage->FullyLoad();
//패키지내에 있는 에셋 가져오기
UStudent* TopStudent = FindObject<UStudent>(StudentPackage, *AssetName);
에셋 로드
게임 제작 단계에선 에셋 간의 연결 작업을 위해 직접 패키지를 불러 할당하는 작업은 부하가 크다.
그렇기 때문에 꼭 필요한 에셋인 경우에 생성자 코드에서 미리 로딩하는 것이 좋다.
단, 런타임에서 로딩해야 하는 경우라면 관리자를 사용해서 비동기로 로딩하는것이 좋다.
//생성자
const FString TopSoftObjectPath = FString::Printf(TEXT("%s.%s"), *PackageName, *AssetName);
static ConstructorHelpers::FObjectFinder<UStudent> UASSET_TopStudent(*TopSoftObjectPath);
if (UASSET_TopStudent.Succeeded())
{
//성공 로드. UASSET_TopStudent.Object로 접근 가능
}
...
//보통 메소드
const FString TopSoftObjectPath = FString::Printf(TEXT("%s.%s"), *PackageName, *AssetName);
UStudent* TopStudent = LoadObject<UStudent>(nullptr, *TopSoftObjectPath);
관리자를 활용한 비동기 로드
FStreamableManager를 활용한다.
FStreamableManager StreamableManager;
TSharedPtr<FStreamableHandle> Handle;
...
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)
{
//성공적 로드
Handle->ReleaseHandle();
Handle = nullptr;
}
}
}
);
'Unreal > 이득우의 언리얼 프로그래밍' 카테고리의 다른 글
언리얼 5_4_1 게임플레이 어빌리티 시스템 캐릭터 제작 기초 (0) | 2024.03.24 |
---|---|
언리얼5_2_1 : 게임 컨텐츠의 기본 구조 (3) | 2024.03.06 |
언리얼5_1_3 : 언리얼 엔진의 자료구조와 메모리 관리 (1) | 2024.02.16 |
언리얼5_1_2 : 언리얼 C++ 모던객체지향 설계 (0) | 2024.02.16 |
언리얼5_1_1 : 언리얼 오브젝트의 이해 (1) | 2024.02.16 |