저번 포스팅에서는 언리얼 엔진이 메모리를 관리하는 방법 중 하나인 가비지 컬렉터에 대해 알아보았습니다.
이번 포스팅에서는
언리얼 엔진에서 메모리, 그 중에서도 오브젝트를 관리하는 방법 중 하나인 직렬화에 대해 알아보겠습니다.
1) 직렬화(Serialization)란?
직렬화는 오브젝트나 연결된 오브젝트의 묶음을 바이트 스트림으로 변환하는 과정을 의미합니다.
이때 직렬화는 바이트 스트림에서 오브젝트로 변환하는 과정도 포함해서 의미합니다.
직렬화로 얻을 수 있는 장점은 다음과 같습니다.
1) 현재 프로세스의 상태를 저장하고 필요한 경우에 복원이 가능합니다.
2) 현재 인스턴스의 정보를 복사하여 다른 프로그램에 전송이 가능합니다.
3) 네트워크를 통해 현재 프로세스의 상태를 다른 컴퓨터에서 복원이 가능합니다.
4) 데이터를 압축하고 암호화하여 효율적이고 안전한 데이터의 보관이 가능합니다.
직렬화를 통해 위와 같은 장점을 얻을 수 있습니다.
그렇다면 언리얼 엔진에서는 직렬화를 어떻게 구현하는지 알아보겠습니다.
2) 언리얼 엔진의 직렬화 시스템
기본적으로 언리얼 엔진은 직렬화를 위해 FArchive 클래스와 << 연산자를 제공합니다.
위의 << 연산자는 직렬화를 위해 읽기와 쓰기의 역할을 모두 수행합니다.
무엇보다 언리얼 엔진의 직렬화 시스템의 장점은 다양한 아카이브 클래스를 제공한다는 것입니다.
메모리 형태로 데이터를 읽고 쓰고 싶다면,
FMemoryReader와 FMemoryWriter 클래스를 사용합니다.
파일의 형태로 데이터를 읽고 쓰고 싶다면,
FArchiveFileReaderGeneric과 FArchiveFileWriterGeneric 클래스를 사용합니다.
위의 2개뿐만이 아니라 더 많은 아카이브가 존재하고, 특히 JSON 형태로 데이터를 저장할 수도 있습니다.
JSON 형태로 저장하기 위해서는 별도의 모듈인 Json, JsonUtilies를 프로젝트에 추가해야 하고
부가적으로, 언리얼 스마트 포인터를 사용하는 것이 권장됩니다.
더 복잡해 보일 수 있지만, JSON 파일이 갖는 장점이 훨씬 더 크다고 생각합니다.
따라서 이번 포스트에서는
파일의 형태로 데이터를 저장하는 방법과 JSON 형태로 데이터를 저장하는 2가지 방법에 대해 알려드리겠습니다.
3) 언리얼 스마트 포인터
기존 C++에서는 포인터의 위험성 때문에 가급적 스마트 포인터를 사용할 것을 권장합니다.
이것은 언리얼 엔진도 마찬가지로서, 필요한 경우에 스마트 포인터를 사용해야 합니다.
1) TUniquePtr
(1) 지정한 곳에서만 메모리를 관리하는 포인터입니다.
(2) 특정 오브젝트에 한해 명확하게 포인터 해지 권한을 주고 싶은 경우에 사용합니다.
(3) delete 구문 없이 함수 실행 후 자동으로 소멸시키고 싶을 때 사용합니다.
2) TSharedPtr
(1) 더 이상 사용되지 않으면 자동으로 메모리를 해지하는 포인터입니다.
(2) 여러 로직에서 할당된 오브젝트를 공유해서 사용하는 경우 사용이 권장됩니다.
(3) 다른 함수로부터 할당된 오브젝트를 Outer(=나를 소유한 언리얼 오브젝트)으로 받는 경우에 사용합니다.
(4) 주의할 것은 TSharedPtr이 가리키는 오브젝트는 null일 수 있다는 것입니다.
3) TSharedRef
(1) 위의 TSharedPtr과 동일하지만, 항상 유효한 객체를 보장받는 레퍼런스입니다.
(2) 마찬가지로, 여러 로직에서 할당된 오브젝트를 공유해서 사용하는 경우 사용할 수 있습니다.
(3) 항상 유효하므로 null일 수 없고 오브젝트를 편하게 사용할 수 있습니다.
위와 같이 대표적인 언리얼 스마트 포인터 라이브러리에 대해 알아보았습니다.
이제 직렬화의 사용방법을 예제를 통해 알아보겠습니다.
사용예시는 파일 형태의 직렬화와 JSON 형태의 직렬화 2가지로 구성되어있습니다.
4) 직렬화 - 언리얼 오브젝트 데이터를 파일 형태로 읽고, 쓰기
구성은 다음과 같습니다.
Member라는 언리얼 오브젝트를 대상으로 파일에 쓰고, 파일을 읽어들일 예정입니다.
코드 작성의 편의를 위해 헤더 파일에 몰아넣어서 작업했음을 양해바랍니다.
이제 MyGameInstance로 가보겠습니다.
MyGameInstance의 헤더 파일은 위와 같습니다.
MeberSrc 포인터로 언리얼 오브젝트를 가리키게 할 것입니다.
cpp 파일의 경우, 코드가 길어져 2분할 하게 되었습니다.
구성은 위와 같이 이뤄져있습니다.
이제 로그 실행결과와 프로젝트 경로를 확인해보겠습니다.
로그와 파일 역시 제대로 실행되고 생성되었음을 확인할 수 있습니다.
이제 JSON 형태로 저장하는 방법을 알아보겠습니다.
언리얼 오브젝트에는 따로 변화가 없지만, 필요한 모듈을 추가하고 GameInstance의 로직이 변경됩니다.
5) 직렬화 - 언리얼 오브젝트 데이터를 JSON 파일 형태로 읽고, 쓰기
모듈을 다음과 같이 추가합니다.
GameInstance.cpp 파일을 다음과 같이 수정합니다.
실행결과와 생성된 파일은 다음과 같습니다.
이상으로 직렬화에 대한 포스팅을 마치겠습니다.
직렬화를 통해 우리는 게임의 데이터를 외부에 쉽게 저장하거나 네트워크를 통해 다른 프로세스에 전송할 수 있습니다.
특히 게임의 데이터 변경이 빈번하게 이뤄지는 온라임 게임에서 직렬화는 매우 도움이 될 것입니다.
'언리얼 > 언리얼 C++' 카테고리의 다른 글
언리얼 구형 입력 시스템 (0) | 2024.09.21 |
---|---|
언리얼 엔진의 메모리 관리 (3) ~ 패키지 (0) | 2024.08.18 |
언리얼 엔진의 메모리 관리 (1) ~ 가비지 컬렉션 시스템 (0) | 2024.08.03 |
언리얼 C++ - 구조체 (0) | 2024.07.27 |
언리얼 C++ - 자료 구조 ~ TMap (0) | 2024.07.20 |