본문 바로가기

공부/이득우의 언리얼 프로그래밍

[Study] Part 1 - 언리얼 오브젝트 관리 I - 직렬화 (13/15)

728x90
반응형

 

  • 정리
    • 언리얼 오브젝트 직렬화
      1. 언리얼 엔진이 제공하는 직렬화 시스템에 대한 이해
      2. FArchive클래스를 활용한 메모리 아카이브와 파일 아카이브의 활용
      3. JSonSerializer를 사용한 JSON 형식의 직렬화 기능의 활용
      4. 일반 C++ 객체 관리를 위한 언리얼 스마트 포인터 라이브러리의 활용

 

 

 

  • 직렬화(Serialization)란?
    • 오브젝트나 연결된 오브젝트의 묶음(오브젝트 그래프)을 바이트 스트림으로 변환하는 과정
      • 복잡한 데이터를 일렬로 세우기 때문에 직렬화
    • 거꾸로 복구시키는 과정도 포함해서 의미
      • 시리얼라이제이션(Serialization)
        • 오브젝트 그래프에서 바이트 스트림으로
      • 디시리얼라이제이션(Deserialization)
        • 바이트 스트림에서 오브젝트 그래프로
    • 직렬화가 가지는 장점
      • 현재 프로그램의 상태를 저장하고 필요한 때 복원할 수 있음 (게임의 저장…)
      • 현재 객체의 정보를 클립보드에 복사해서 다른 프로그램에 전송할 수 있다.
      • 네트워크를 통해 현재 프로그램의 상태를 다른 컴퓨터에 복원할 수 있다. (멀티플레이어 게임)
      • 데이터 압축, 암호화를 통해 데이터를 효율적이고 안전하게 보관할 수도 있음

 

 

  • 직렬화 구현시 고려할 점
    • 데이터 레이아웃
      • 오브젝트가 소유한 다양한 데이터를 변환할 것인가?
    • 이식성
      • 서로 다른 시스템에 전송해도 이식될 수 있는가?
    • 버전 관리
      • 새로운 기능이 추가될 때 이를 어떻게 확장하고 처리할 것인가?
    • 성능
      • 네트웍 비용을 줄이기 위해 어떤 데이터 형식을 사용할 것인가?
    • 보안
      • 데이터를 어떻게 안전하게 보호할 것인가?
    • 에러 처리
      • 전송 과정에서 문제가 발생할 경우 이를 어떻게 인식하고 처리할 것인가?
  • 언리얼 엔진의 직렬화 시스템
    • 언리얼 엔진은 직렬화 시스템을 자체적으로 제공하고 있음
    • 직렬화 시스템을 위해 제공하는 클래스 FArchive와 연산자
      • 아카이브 클래스 (FArchive)
      • Shift (<<) operator
    • 다양한 아카이브 클래스의 제공
      • 메모리 아카이브 (FMemoryReader, FMemoryWriter)
      • 파일 아카이브 (FArchiveFileReaderGeneric, FArchiveFileWriterGeneric)
      • 기타 언리얼 오브젝트와 관련된 아카이브 클래스 (FArchiveUObject)
    • Json 직렬화 기능
      • 별도의 라이브러리를 통해 제공하고 있음

 

 

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("홍길동");
};
  • MyGameInstance.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 RawDataAbosolutePath = FPaths::Combine(*SavedDir, *RawDataFileName);
		UE_LOG(LogTemp, Log, TEXT("저장할 파일 전체 경로 : %s"), *RawDataAbosolutePath);
		FPaths::MakeStandardFilename(RawDataAbosolutePath);  // 숨겨진 파일 전체 경로를 볼 수 있도록 변경해줌
		UE_LOG(LogTemp, Log, TEXT("변경할 파일 전체 경로 : %s"), *RawDataAbosolutePath);

		FArchive* RawFileWriterAr = IFileManager::Get().CreateFileWriter(*RawDataAbosolutePath);  // Writer
		if (nullptr != RawFileWriterAr)
		{
			*RawFileWriterAr << RawDataSrc;  // 헤더파일에서 << 오퍼레이션을 통해 손쉽게 데이터를 가져오기 가능
			RawFileWriterAr->Close();        // 파일을 열었으면 닫아주고
			delete RawFileWriterAr;          // 해제해주고
			RawFileWriterAr = nullptr;       // 삭제까지 해준다.
		}

		FStudentData RawDataDest;
		FArchive* RawFileReaderAr = IFileManager::Get().CreateFileReader(*RawDataAbosolutePath);  // Reader
		if (nullptr != RawFileReaderAr)
		{
			*RawFileReaderAr << RawDataDest;
			RawFileReaderAr->Close();
			delete RawFileReaderAr;
			RawFileReaderAr = nullptr;

			UE_LOG(LogTemp, Log, TEXT("[RawData] 이름 %s 순번 %d"), *RawDataDest.Name, RawDataDest.Order);  // 정상적으로 읽고, 써졌는지 확인하기
		}
	}
}
  • MyGameInstance.cpp
  • 언제나 파일의 내용을 읽어올때, 멤버 변수를 지정해줄 수 없으니 Shift (<<) operator를 통해 사용하면 된다.

 

 

 

  • Json 직렬화
    • Json(JavaScript Object Notation)의 약자
    • 엡 환경에서 서버와 클라이언트 사이에 데이터를 주고받을 때 사용하는 텍스트 기반 데이터 포맷
    • Json 장점
      • 텍스트임에도 데이터 크기가 가벼움
      • 읽기 편해서 데이터를 보고 이해할 수 있음
      • 사실 상 웹 통신의 표준으로 널리 사용됨
    • Json의 단점
      • 지원하는 타입이 몇 가지 안됨 (문자, 숫자, 불리언, 널, 배열, 오브젝트만 사용 가능)
      • 텍스트 형식으로만 사용할 수 있음

 

 

  • 언리얼 엔진의 Json, JsonUtilities 라이브러리 활용
  • Json 데이터 예시
    • Json 데이터 유형
      • 오브젝트 : {}
        • 오브젝트 내 데이터는 키, 밸류 조합으로 구성됨. 예) {”key” : 10}
      • 배열 : []
        • 배열 내 데이터는 밸류로만 구성됨
      • 이외 데이터
        • 문자열 (”string”), 숫자 (10 또는 3.14 …), 불리언 (true 또는 false), 널 (null)로 구성

 

 

  • 언리얼이 제공하는 Json 라이브러리를 사용하려면 언리얼 엔진이 제공하는 스마트 포인터 라이브러리를 알아두면 좋다.
  • 언리얼 스마트 포인터 라이브러리 개요
    • 일반 C++ 오브젝트의 포인터 문제를 해결해주는 언리얼 엔진의 라이브러리
    • TUniquePtr(유니크포인터) : 지정한 곳에서만 메모리를 관리하는 포인터
      • 특정 오브젝트에게 명확하게 포인터 해지 권한을 주고 싶은 경우
      • delete 구문 없이 함수 실행 후 자동으로 소멸시키고 싶을 때
    • TSharedPtr(공유포인터) : 더 이상 사용되지 않으면 자동으로 메모리를 해지하는 포인터
      • 여러 로직에서 할당된 오브젝트가 공유해서 사용되는 경우
      • 다른 함수로부터 할당된 오브젝트를 Out으로 받는 경우
      • Null 일 수 있음
    • TSharedRef(공유레퍼런스) : 공유포인터와 동일하지만, 유효한 객체를 항상 보장받는 레퍼런스
      • 여러 로직에서 할당된 오브젝트가 공유해서 사용되는 경우
      • Not Null을 보장받으며 오브젝트를 편리하게 사용하고 싶은 경우

 

 

 

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" });		// Json과 관련된 모듈 선언 및 추가

		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.Bulid.cs 에 Json에 관련된 모듈 추가하기

 

{
		const FString JsonDataFileName(TEXT("StudentJsonData.txt"));
		FString JsonDataAbsolutePath = FPaths::Combine(*SavedDir, *JsonDataFileName);
		FPaths::MakeStandardFilename(JsonDataAbsolutePath);

		TSharedRef<FJsonObject> JsonObjectSrc = MakeShared<FJsonObject>();		// TSharedRef를 통해 Not Null을 보장받음
		FJsonObjectConverter::UStructToJsonObject(StudentSrc->GetClass(), StudentSrc, JsonObjectSrc);

		FString JsonOutString;		// Json으로 내보낼
		TSharedRef<TJsonWriter<TCHAR>> JsonWriterAr = TJsonWriterFactory<TCHAR>::Create(&JsonOutString);		// Json으로 써주는
		if (FJsonSerializer::Serialize(JsonObjectSrc, JsonWriterAr))		// 직렬화 진행이 성공했다면
		{
			FFileHelper::SaveStringToFile(JsonOutString, *JsonDataAbsolutePath);		// 파일에 Json을 쓰기
		}

		FString JsonInString;		// Json으로 불러드릴
		FFileHelper::LoadFileToString(JsonInString, *JsonDataAbsolutePath);

		TSharedRef<TJsonReader<TCHAR>> JsonReaderAr = TJsonReaderFactory<TCHAR>::Create(JsonInString);

		TSharedPtr<FJsonObject> JsonObjectDest;		// Null이 들어갈 수도 있으니 Ptr로 선언
		if (FJsonSerializer::Deserialize(JsonReaderAr, JsonObjectDest))
		{
			UStudent* JsonStudentDest = NewObject<UStudent>();
			if (FJsonObjectConverter::JsonObjectToUStruct(JsonObjectDest.ToSharedRef(), JsonStudentDest->GetClass(), JsonStudentDest))		// Json 오브젝트를 언리얼 오브젝트로 변환
			{
				PrintStudentInfo(JsonStudentDest, TEXT("JsonData"));
			}
		}
	}
  • MyGameInstance.cpp / Json 오브젝트를 내보내거나, 가져와 사용하는 방법

 

 

 

 

 

 

 

해당 포스트는 인프런의 <이득우의 언리얼 프로그래밍 Part1 - 언리얼 C++의 이해>
강의를 수강하고 정리한 내용입니다.
 

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

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

www.inflearn.com

 

728x90