일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- dtft
- 유스케이스
- CTF
- ret2libc
- sampling theory
- frequency-domain spectrum analysis
- MLFQ
- 게임 개발
- 운영체제
- RBAC
- Double free
- 언리얼엔진
- stride
- 유니티
- 배경 그림
- 메카님
- DSP
- 게임개발
- MAC
- Unity #Indie Game
- TSet
- Race condition
- Rr
- STCF
- linear difference equation
- AINCAA
- DP
- Security
- dirty cow
- pdlc
- Today
- Total
다양한 기록
[UE] Serialization (직렬화) 본문
오브젝트나 연결된 오브젝트의 묶음(오브젝트 그래프)을 바이트 스트림으로 변환하는 과정
- 복잡한 데이터를 일렬로 -> 직렬
Serialization : 오브젝트 그래프 -> 바이트 스트림
Deserialization : 바이트 스트림 -> 오브젝트 그래프
장점
- 현재 프로그램 상태를 저장 및 복원 가능 (게임 저장)
- 현재 객체 정보를 클립보드에 복사 -> 다른 프로그램에 전송 가능
- 네트워크를 통해 현재 프로그램의 상태를 다른 컴퓨터에 복원 가능 (멀티게임)
- 데이터 압축, 암호화를 통해 데이터를 효율적이고 안전하게 보관할 수도 있음
직접 구현?
- 데이터 레이아웃 (오브젝트가 소유한 다양한 데이터를 변환?)
- 이식성 (서로 다른 시스템 간)
- 버전 관리
- 성능
- 보안
- 에러 처리
쉽지 않음
그냥 언리얼 엔진이 제공하는 거 쓰면 됨
직렬화 시스템
- FArchive
- << 연산자
다양한 아카이브 클래스
- FMemoryReader, FMemoryWriter
- FArchiveFileReaderGeneric, FArchiveFileWriterGeneric
- FArchiveUObject
Json 직렬화 기능
구조체 직렬화
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Engine/GameInstance.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:
virtual void Init() override;
};
그냥 Ar << Student.Order 이런 식으로 넣을 수도 있는데,
그냥 << 새로 정의해두는게 편함
// Fill out your copyright notice in the Description page of Project Settings.
#include "MyGameInstance.h"
#include "Student.h"
void UMyGameInstance::Init()
{
Super::Init();
FStudentData RawDataSrc( 16, TEXT("라구넹") );
const FString SavedDir = FPaths::Combine(FPlatformMisc::ProjectDir(), TEXT("Saved"));
UE_LOG(LogTemp, Log, TEXT("저장할 파일 폴더 %s"), *SavedDir);
{
const FString RawDataFileName(TEXT("RawData.bin") );
FString RawDataAbsolutePath = FPaths::Combine(*SavedDir, *RawDataFileName);
UE_LOG(LogTemp, Log, TEXT("저장할 파일 전체 경로 %s"), *RawDataAbsolutePath);
FPaths::MakeStandardFilename(RawDataAbsolutePath);
UE_LOG(LogTemp, Log, TEXT("표준화된 파일 전체 경로 %s"), *RawDataAbsolutePath);
FArchive* RawDataFileWriterAr = IFileManager::Get().CreateFileWriter(*RawDataAbsolutePath);
if ( nullptr != RawDataFileWriterAr)
{
*RawDataFileWriterAr << RawDataSrc;
RawDataFileWriterAr->Close();
delete RawDataFileWriterAr;
RawDataFileWriterAr = nullptr;
}
FStudentData RawDataDst;
FArchive* RawDataFileReaderAr = IFileManager::Get().CreateFileReader(*RawDataAbsolutePath);
if ( nullptr != RawDataFileReaderAr)
{
*RawDataFileReaderAr << RawDataDst;
RawDataFileReaderAr->Close();
delete RawDataFileReaderAr;
RawDataFileReaderAr = nullptr;
UE_LOG(LogTemp, Log, TEXT("[RawData] 이름 %s 순번 %d"), *RawDataDst.Name, RawDataDst.Order);
}
}
}
ProjectDir() : 프로젝트 경로
FPaths::Combine(~, ~) 경로 합치기 .. Saved라는 폴더랑 합침
FPaths::MakeStandardFileName(~) : 경로 표준화하기
파일 열고 쓰기는 파일 라이터 아카이브랑 파일 리더 아카이브로 이루어짐
UObject 직렬화
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Engine/GameInstance.h"
#include "MyGameInstance.generated.h"
/**
*
*/
UCLASS()
class UNREALSERIALIZATION_API UMyGameInstance : public UGameInstance
{
GENERATED_BODY()
public:
virtual void Init() override;
private:
TObjectPtr<class UStudent> StudentSrc;
};
// Fill out your copyright notice in the Description page of Project Settings.
#include "MyGameInstance.h"
#include "Student.h"
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"));
UE_LOG(LogTemp, Log, TEXT("저장할 파일 폴더 %s"), *SavedDir);
StudentSrc = NewObject<UStudent>();
StudentSrc->SetName(TEXT("라구넹"));
StudentSrc->SetOrder(31);
{
const FString ObjectDataFileName(TEXT("ObjectData.bin"));
FString ObjectDataAbsolutePath = FPaths::Combine(*SavedDir, ObjectDataFileName);
FPaths::MakeStandardFilename(ObjectDataAbsolutePath);
// Buffer
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* StudentDst = NewObject<UStudent>();
StudentDst->Serialize(MemoryReaderAr);
PrintStudentInfo(StudentDst, TEXT("ObjectData"));
}
}
게임인스턴스
메모리 리더, 라이터를 사용하여 버퍼와 연결함
파일 불러오기, 가져오기는 구조체랑 비슷하게 하는데, 버퍼에 일단 담아야 함 (구조체의 경우 구조체에 바로 담으면 됨)
그리고 직렬화, 역직렬화 시 해당 메모리 리더, 라이터를 이용해서 버퍼에서 꺼내서 오브젝트에 담음
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "Student.generated.h"
/**
*
*/
UCLASS()
class UNREALSERIALIZATION_API UStudent : public UObject
{
GENERATED_BODY()
public:
UStudent();
int32 GetOrder() const { return Order; }
void SetOrder(const int32 InOrder) { Order = InOrder; }
const FString& GetName() const { return Name; }
void SetName(const FString& InName) { Name = InName; }
virtual void Serialize(FArchive& Ar) override;
private:
UPROPERTY()
int32 Order;
UPROPERTY()
FString Name;
};
// Fill out your copyright notice in the Description page of Project Settings.
#include "Student.h"
UStudent::UStudent()
{
Order = -1;
Name = TEXT("홍길동");
}
void UStudent::Serialize(FArchive& Ar)
{
Super::Serialize(Ar);
Ar << Order;
Ar << Name;
}
Serialize는 해당 오브젝트에서 따로 구현 필요
Json 직렬화
웹에서 많이 쓰는 서버-클라이언트 데이터 교환 포맷
장점
- 데이터 크기 가벼움, 읽기 편함
단점
- 지원하는 타입이 적음 ( 문자, 숫자, 불리언, 널, 배열, 오브젝트 )
- 텍스트 형식으로만 사용 가능
언리얼 엔진의 Json, JsonUtilities 라이브러리 활용
Json 데이터 유형
- 오브젝트 : {}
오브젝트 내 데이터는 키 밸류 조합으로 구성 .. { "key" : 10 }
- 배열 : []
배열 내 데이터는 밸류로만 구성 .. { "v1", "v2", "v3" }
- 이외
문자열, 숫자, 불리언, 널로 구성
언리얼 스마트 포인터 라이브러리
TUniquePtr
- 지정한 곳에서만 메모리를 관리
- 특정 오브젝트에 명확하게 해지 권한을 주고 싶은 경우
- delete 없이 함수 실행 후 자동 소멸
TSharedPtr
- 더이상 사용되지 않으면 자동으로 메모리 해지
- 여러 로직에서 할당된 오브젝트가 공유해서 사용
- 다른 함수로부터 할당된 오브젝트를 Out으로 받는 경우
- 널일 수 있음
TSharedRef
- 공유 포인터와 동일하지만 유효한 객체를 항상 보장
- Not Null
이런 것들을 사용해서 Json 직렬화함
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
public class UnrealSerialization : ModuleRules
{
public UnrealSerialization(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "EnhancedInput", "Json", "JsonUtilities" });
PrivateDependencyModuleNames.AddRange(new string[] { });
// Uncomment if you are using Slate UI
// PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });
// Uncomment if you are using online features
// PrivateDependencyModuleNames.Add("OnlineSubsystem");
// To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true
}
}
UnrealSerialization.Build.cs에 Json이랑 JsonUtilities 추가 필요
// Fill out your copyright notice in the Description page of Project Settings.
#include "MyGameInstance.h"
#include "Student.h"
#include "JsonObjectConverter.h"
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"));
UE_LOG(LogTemp, Log, TEXT("저장할 파일 폴더 %s"), *SavedDir);
StudentSrc = NewObject<UStudent>();
StudentSrc->SetName(TEXT("라구넹"));
StudentSrc->SetOrder(31);
{
const FString JsonFileName(TEXT("StudentJsonData.txt"));
FString JsonAbsoluteFilePath = FPaths::Combine(*SavedDir, *JsonFileName);
FPaths::MakeStandardFilename(JsonAbsoluteFilePath);
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, *JsonAbsoluteFilePath);
}
FString JsonInString;
FFileHelper::LoadFileToString(JsonInString, *JsonAbsoluteFilePath);
TSharedRef< TJsonReader<TCHAR> > JsonReaderAr = TJsonReaderFactory<TCHAR>::Create(JsonInString);
// 널이 들어갈 수 있어서 포인터로 선언
TSharedPtr< FJsonObject > JsonObjectDst;
if ( FJsonSerializer::Deserialize( JsonReaderAr, JsonObjectDst ) )
{
UStudent* JsonStudentDst = NewObject<UStudent>();
if (FJsonObjectConverter::JsonObjectToUStruct(JsonObjectDst.ToSharedRef(), JsonStudentDst->GetClass(), JsonStudentDst))
{
PrintStudentInfo(JsonStudentDst, "JsonData");
}
}
}
}
언리얼 오브젝트 -> Json 오브젝트로 변환
Json 라이터 받고 Json 시리얼라이저 이용해서 직렬화하고 저장
역직렬화도 비슷한 과정
텍스트파일까지 잘 저장된 걸 확인 가능
* Json은 텍스트로 저장됨
'언리얼 엔진 > Unreal C++' 카테고리의 다른 글
[UE] Unreal Build System (0) | 2025.01.02 |
---|---|
[UE] Package (0) | 2025.01.01 |
[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 |