본문 바로가기

Unreal

UE C++ 직렬화

 

 

서론

언리얼 엔진(UE)에서 C++을 사용해 게임을 개발할 때, 데이터의 저장과 불러오기를 효율적으로 처리하기 위해 직렬화(Serialization)가 필요합니다. 직렬화는 객체의 상태를 저장하여 나중에 복원할 수 있도록 하는 과정입니다. 이 포스트에서는 언리얼 엔진에서 사용하는 직렬화에 대해서 알아보려고 합니다.

 

 

직렬화

우선 언리얼 엔진의 직렬화를 알아보기에 앞서서 직렬화에 대한 개념을 간단하게 짚고 넘어가려고 합니다.

직렬화는 객체의 상태를 바이트 스트림으로 변환하여 저장하거나 전송할 수 있도록 하는 과정입니다.

이를 통해 데이터를 파일에 저장하거나 네트워크를 통해 전송한 후 동일한 객체로 복원할 수 있습니다.

  • 데이터 -> 바이트 스트림 변환 과정을 Serialization,
  • 바이트 스트림 -> 데이터 변환 과정을 DeSerialization 이라고 정의합니다.
  • 보통 Serialization과 DeSerialization을 모두 포함해서 직렬화라고 부릅니다.

 

직렬화 구현 시 고려할 점.

  • 데이터 레이아웃 : 오브젝트가 소유한 다양한 데이터를 변환하는 방법
  • 이식성 : 서로 다른 시스템에 전송해도 이식할 수 있는 방법
  • 버전 관리 : 새로운 기능이 추가될 때 기능을 어떻게 확장하고 처리할 것인지에 대한 방법
  • 성능 : 네트워크 비용을 줄이기 위해 어떤 데이터 형식을 사용할 것인지에 대한 방법
  • 보안 : 데이터를 어떻게 안전하게 보호할 것인지에 대한 방법
  • 에러 처리 : 전송 과정에서 문제가 발생할 경우 이를 어떻게 인식하고 처리할 것인지에 대한 방법

 

언리얼 엔진의 직렬화 시스템

  • 언리얼 엔진은 이러한 상황을 모두 고려한 직렬화 시스템을 자체적으로 제공합니다.
  • 직렬화 시스템을 위해서 제공하는 클래스 FArchive, 연산자가 존재합니다.
    • Archive class : FArchive
    • Shift(<<) operator
  • 다양한 아카이브 클래스의 제공합니다.
    • 메모리 아카이브 (FMemoryReader, FMemoryWriter)
    • 파일 아카이브(FArchiveFileReaderGeneric, FArchiveFileWriterGeneric)
    • 기타 언리얼 오브젝트와 관련된 아카이브 클래스(FArchiveUObject)
  • Json 직렬화 기능 : 별도의 라이브러리를 통해 제공합니다.
    • 사용하기 위해서는 Json, JsonUtilites 라이브러리를 활용합니다.

 

Json 직렬화 추가

<ProjectName>.Build.cs 파일에서 PublicDependencyModuleNames.AddRange에 Json, JsonUtilites를 추가합니다.

// 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", "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
	}
}

 

 

Json 데이터 유형

  • 오브젝트 : {}
    • 오브젝트 내 데이터는 키, 밸류 조합으로 구성됩니다. 예) {”key”:10}
  • 배열 : []
    • 배열 내 데이터는 밸류로만 구성됩니다.. 예) [”value1”, “value2”, “value3”]
  • 이외 데이터
    • 문자열 (”string”), 숫자 (10 또는 3.14), 불리언 ( true 또는 false ), 널 ( null ) 로 구성됩니다.

 

실습

 

데이터 구조체에 직렬화 방식 정의


// MyGameInstance.h
struct FMyData
{

public:
    // 직렬화 함수
    friend FArchive& operator<<(FArchive& Ar, MyStruct& InMyData)
    {
        Ar << InMyData.MyInt;
        Ar << InMyData.MyString;
        return Ar;
    }
    
private:
    int32 MyInt;    
    FString MyString;
};

// ... 생략

// MyData.h
// ... 생략

UCLASS()
class UNREALSERIALIZATION_API UMyData : public UObject
{
	GENERATED_BODY()
	
public:
	UStudent();

	int32 GetMyInt() const { return MyInt; }
	void SetMyInt(int32 InMyInt) { MyInt = InMyInt; }

	const FString& GetMyString() const { return MyString; }
	void SetMyString(const FString& InMyString) { MyString = InMyString; }

	virtual void Serialize(FArchive& Ar) override;


private:
	UPROPERTY()
	int32 MyInt;

	UPROPERTY()
	FString MyString;
};

FMyData 구조체에 FArchive의 operator <<를 정의함으로서 FMyData 구조체에 직렬화 방식을 정의합니다.

 

 

파일에 데이터 쓰기 후 읽기

// Fill out your copyright notice in the Description page of Project Settings.


#include "MyGameInstance.h"
#include "MyData.h"

UMyGameInstance::UMyGameInstance()
{
}

void UMyGameInstance::Init()
{
	Super::Init();

	FStudentData RawDataSrc(16, TEXT("Test"));

	const FString SavedDir = FPaths::Combine(FPlatformMisc::ProjectDir(), TEXT("Saved"));
	{
		const FString RawDataFileName(TEXT("RawData.bin"));
		FString RawDataAbsolutePath = FPaths::Combine(*SavedDir, *RawDataFileName);
		FPaths::MakeStandardFilename(RawDataAbsolutePath);

		FArchive* RawFileWriterAr = IFileManager::Get().CreateFileWriter(*RawDataAbsolutePath);

		if (nullptr != RawFileWriterAr )
		{
			*RawFileWriterAr << RawDataSrc;
			RawFileWriterAr->Close();
			delete RawFileWriterAr;
			RawFileWriterAr = nullptr;
		}

		FMyData RawDataDest;
		FArchive* RawFileReaderAr = IFileManager::Get().CreateFileReader(*RawDataAbsolutePath);
		if (nullptr != RawFileReaderAr)
		{
			*RawFileReaderAr << RawDataDest;
			RawFileReaderAr->Close();
			delete RawFileReaderAr;
			RawFileReaderAr = nullptr;
		}
	}
}

 

메모리에 파일 내용 불러오기

// Fill out your copyright notice in the Description page of Project Settings.


#include "MyGameInstance.h"
#include "MyData.h"


UMyGameInstance::UMyGameInstance()
{
}

void UMyGameInstance::Init()
{
	Super::Init();
	
	MyDataSrc = NewObject<UMyData>();
	MyDataSrc->SetMyString(TEXT("데이터"));
	MyDataSrc->SetMyInt(59);

	{
		const FString ObjectDataFileName(TEXT("ObjectData.bin"));
		FString ObjectDataAbsolutePath = FPaths::Combine(*SavedDir, *ObjectDataFileName);
		FPaths::MakeStandardFilename(ObjectDataAbsolutePath);
		
        // 객체 직렬화 (Serialization)
		TArray<uint8> BufferArray;
		FMemoryWriter MemoryWriterAr(BufferArray);
		MyDataSrc->Serialize(MemoryWriterAr);
		
        // BufferArray -> 파일로 내용 전달
		if (TUniquePtr<FArchive> FileWriterAr = TUniquePtr<FArchive>(IFileManager::Get().CreateFileWriter(*ObjectDataAbsolutePath)))
		{
			*FileWriterAr << BufferArray;
			FileWriterAr->Close();
		}

		// FileReaderAr로 내용을 읽은 뒤 BufferArrayFromFile 로 내용 전달
		TArray<uint8> BufferArrayFromFile;
		if (TUniquePtr<FArchive> FileReaderAr = TUniquePtr<FArchive>(IFileManager::Get().CreateFileReader(*ObjectDataAbsolutePath)))
		{
			*FileReaderAr << BufferArrayFromFile; 
			FileReaderAr->Close();
		}

		// MemoryReader를 통해 Buffer의 내용을 이용하여 Object 생성. (DeSerialization)
		FMemoryReader MemoryReaderAr(BufferArrayFromFile);
		UMyData* MyDataDest = NewObject<UMyData>();
		MyDataDest->Serialize(MemoryReaderAr);
	}
}

 

참조

이 글은 이득우의 언리얼 프로그래밍 Part1 - 언리얼 C++를 수강하고 작성한 글입니다.

 

이득우의 언리얼 프로그래밍 Part1 - 언리얼 C++의 이해 | 이득우 - 인프런

이득우 | 대기업 현업자들이 수강하는 언리얼 C++ 프로그래밍 전문 과정입니다. 언리얼 엔진 프로그래머라면 게임 개발전에 반드시 알아야 하는 언리얼 C++ 기초에 대해 알려드립니다., [사진] 언

www.inflearn.com

 

 

 

 

'Unreal' 카테고리의 다른 글

UE 액터 컴포넌트  (1) 2024.06.08
UE C++ 빌드 시스템  (0) 2024.06.01
UE C++ 메모리 관리  (0) 2024.05.19
UE C++ Design - Interface  (0) 2024.04.27
UE C++ 오브젝트  (1) 2024.04.13