언리얼은 직렬화 시스템을 위해 자체적으로
FArchive
클래스와 연산자들을 제공하고 있다.
다양한 아카이브 클래스로써
FMemoryReader
FMemoryWriter
)FArchiveFileReaderGeneric
, FArchiveFileWriterGeneric
)FArchiveUObject
)등을 제공하고 있으며 Json
형태의 직렬화는 별도의 라이브러리를 통해 제공하고 있다.
//언리얼 오브젝트
class SERIALIZATIONTEST_API UStudent : public UObject
{
GENERATED_BODY()
public:
UStudent();
int32 GetOrder() const { return Order; }
void SetOrder(int32 InOrder) { Order = InOrder; }
const FString& GetName() const { return Name; }
void SetName(const FString& InName) { Name = InName; }
virtual void Serialize(FArchive& Ar) override;
private:
int32 Order;
FString Name;
};
UStudent::UStudent()
{
Order = -1;
Name = TEXT("학생이름 기본값");
}
void UStudent::Serialize(FArchive& Ar)
{
Super::Serialize(Ar);
Ar << Order;
Ar << Name;
}
//cpp 구조체
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("홍길동");
};
//GameInstance.cpp
void UMyGameInstance::Init()
{
Super::Init();
FStudentData RawDataSrc(16, TEXT("너굴"));
const FString SavedDir = FPaths::Combine(FPlatformMisc::ProjectDir(), TEXT("Saved"));
UE_LOG(LogTemp, Display, TEXT("저장할 파일 폴더 : %s"), *SavedDir);
const FString RawDataFileName(TEXT("RawData.bin"));
FString RawDataAbsolutePath = FPaths::Combine(*SavedDir, *RawDataFileName);
UE_LOG(LogTemp, Display, TEXT("저장할 파일 전체 경로 : %s"), *RawDataAbsolutePath);
FPaths::MakeStandardFilename(RawDataAbsolutePath);
UE_LOG(LogTemp, Display, TEXT("저장할 파일 전체 경로 : %s"), *RawDataAbsolutePath);
FArchive* RawFileWriterAr = IFileManager::Get().CreateFileWriter(*RawDataAbsolutePath);
if(RawFileWriterAr != nullptr)
{
*RawFileWriterAr << RawDataSrc;
RawFileWriterAr->Close();
delete RawFileWriterAr;
RawFileWriterAr = nullptr;
}
FStudentData RawDataDest;
FArchive* RawFileReaderAr = IFileManager::Get().CreateFileReader(*RawDataAbsolutePath);
if (RawFileReaderAr != nullptr)
{
*RawFileReaderAr << RawDataDest;
RawFileReaderAr->Close();
delete(RawFileReaderAr);
RawFileReaderAr = nullptr;
UE_LOG(LogTemp, Display, TEXT("[RawData] 이름 %s, 순번 %d"), *RawDataDest.Name, RawDataDest.Order);
}
StudentSrc = NewObject<UStudent>();
StudentSrc->SetName(TEXT("너굴"));
StudentSrc->SetOrder(14);
const FString ObjectDataFileName(TEXT("ObjectData.bin"));
FString ObjectDataAbsolutePath = FPaths::Combine(*SavedDir, *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);
PrintStudentInfo(StudentDest, TEXT("ObjectData"));
}
//결과
LogTemp: Display: 저장할 파일 폴더 : ../../../../UnrealProjects/SerializationTest/Saved
LogTemp: Display: 저장할 파일 전체 경로 : ../../../../UnrealProjects/SerializationTest/Saved/RawData.bin
LogTemp: Display: 저장할 파일 전체 경로 : C:/UnrealEngine/UnrealProjects/SerializationTest/Saved/RawData.bin
LogTemp: Display: 저장할 파일 폴더 : ../../../../UnrealProjects/SerializationTest/Saved
LogTemp: Display: 저장할 파일 전체 경로 : ../../../../UnrealProjects/SerializationTest/Saved/RawData.bin
LogTemp: Display: 저장할 파일 전체 경로 : C:/UnrealEngine/UnrealProjects/SerializationTest/Saved/RawData.bin
LogTemp: Display: [RawData] 이름 너굴, 순번 16
LogTemp: Display: [ObjectData] 이름 너굴 순번 14
이렇게 C++ 클래스 구조체와 언리얼 오브젝트를 각각 파일로 저장하고 불러올 수 있게 되었다!
StudentSrc = NewObject<UStudent>();
StudentSrc->SetName(TEXT("너굴"));
StudentSrc->SetOrder(14);
const FString JsonDataFileName(TEXT("StudentJsonData.txt"));
FString JsonDataAbsolutePath = FPaths::Combine(*SavedDir, *JsonDataFileName);
FPaths::MakeStandardFilename(JsonDataAbsolutePath);
TSharedRef<FJsonObject> JsonObjectSrc = MakeShared<FJsonObject>();
FJsonObjectConverter::UStructToJsonObject(StudentSrc->GetClass(), StudentSrc, JsonObjectSrc);
FString JsonOutString;
TSharedRef<TJsonWriter<TCHAR>> JsonWriterAr = TJsonWriterFactory<TCHAR>::Create(&JsonOutString);
if (FJsonSerializer::Serialize(JsonObjectSrc, JsonWriterAr))
{
FFileHelper::SaveStringToFile(JsonOutString, *JsonDataAbsolutePath);
}
FString JsonInString;
FFileHelper::LoadFileToString(JsonInString, *JsonDataAbsolutePath);
TSharedRef<TJsonReader<TCHAR>> JsonReaderAr = TJsonReaderFactory<TCHAR>::Create(JsonInString);
TSharedPtr<FJsonObject> JsonObjectDest;
if (FJsonSerializer::Deserialize(JsonReaderAr, JsonObjectDest))
{
UStudent* JsonStudentDest = NewObject<UStudent>();
if (FJsonObjectConverter::JsonObjectToUStruct(JsonObjectDest.ToSharedRef(), JsonStudentDest->GetClass(), JsonStudentDest))
{
PrintStudentInfo(JsonStudentDest, TEXT("JsonData"));
}
}
//결과
LogTemp: Display: [JsonData] 이름 너굴 순번 14
txt 파일로 잘 저장되고 불러와짐을 확인할 수 있다!
직렬화를 수행할 때 만약 언리얼 오브젝트를 직렬화 한다면
그 때 필요한 모든 프로퍼티들이 UPROPERTY 매크로가 잘 붙어 있는지 확인해야함에 주의하자!
안붙이면 언리얼이 인식을 못해버릴 수 있다.