일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 카렌
- 데이터베이스
- 게임개발
- 정글사관학교
- 언리얼
- 스마일게이트
- 언리얼프로그래머
- Ajax
- Jinja2
- EnhancedInput
- 디자드
- node
- flask
- Bootstrap4
- 파이썬서버
- Enhanced Input System
- Express
- 프메
- 레베카
- 스터디
- 미니프로젝트
- Unseen
- 프린세스메이커
- JWT
- 언리얼뮤지컬
- 알고풀자
- 으
- R
- VUE
- 마인크래프트뮤지컬
- Today
- Total
Showing
[Unreal] 언리얼 직렬화, Serialize 본문
*본 포스팅은 언리얼에서 직렬화하는 방법을 작성하였습니다. 직렬화 개념 자체에 대해서 알고 싶으신 분께는 해당 포스팅을 추천드립니다.
저장할 프로퍼티 결정 (ID, Name)
비둘기 클래스의 id와 이름을 저장해보도록 한다면 아래와 같이 진행할 수 있다.
일단 저장할 프로퍼티를 만들고 Get, Set 처리를 다 만들어준다.
Serialize를 반드시 오버라이드해둔다.
pigeon.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "SFlyable.h" // 상속하기 위해 가져옴
#include "SPigeon.generated.h"
/**
*
*/
UCLASS()
class TRACESOFTHEFOX_API USPigeon : public UObject, public ISFlyable // 인터페이스 상속
{
GENERATED_BODY()
public:
USPigeon(); // 생성자
virtual void Fly() override;
// name get, set
const FString& GetName() const { return Name; }
void SetName(const FString& InName) {
Name = InName;
}
// id get, set
int32 GetID() const { return ID; }
void SetID(int32 InID) {
ID = InID;
}
virtual void Serialize(FArchive& Ar) override;
private:
UPROPERTY()
FString Name;
UPROPERTY()
int32 ID;
};
pigeon.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "Examples/SPigeon.h"
USPigeon::USPigeon()
{
Name = TEXT("Pigeon");
}
void USPigeon::Fly()
{
UE_LOG(LogTemp, Log, TEXT("%s is now Flying"), *Name)
}
void USPigeon::Serialize(FArchive& Ar)
{
Super::Serialize(Ar);
Ar << Name;
Ar << ID;
}
Data struct 정의 for serialize
인터페이스에 저장 형식이 될 birdData struct를 넣어준다.
#pragma once
#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "SFlyable.generated.h"
USTRUCT()
struct FBirdData
{
GENERATED_BODY()
public:
FBirdData() {}
FBirdData(const FString& InName, int32 InID) : Name(InName), ID(InID) {}
friend FArchive& operator<<(FArchive& Ar, FBirdData& InBirdData) {
Ar << InBirdData.Name;
Ar << InBirdData.ID;
return Ar;
}
UPROPERTY()
FString Name = TEXT("DefaultName");
UPROPERTY()
int32 ID = 0;
};
UINTERFACE(MinimalAPI)
class USFlyable : public UInterface
{
GENERATED_BODY()
};
class TRACESOFTHEFOX_API ISFlyable
{
GENERATED_BODY()
public:
virtual void Fly() = 0;
// 이렇게 '순수 가상 함수'가 들어있는 클래스를 인터페이스라고 한다.
};
<< 연산자를 정의하여 serialize 될 수 있도록 한다.
생성자는 이름과 id를 받아올 수 있도록 하나 더 정의한다.
본격적인 serialize 예제
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Engine/GameInstance.h"
#include "SGameInstance.generated.h"
/**
*
*/
UCLASS()
class TRACESOFTHEFOX_API USGameInstance : public UGameInstance
{
GENERATED_BODY()
public:
USGameInstance();
virtual void Init() override;
virtual void Shutdown() override;
private:
UPROPERTY()
FString Name;
UPROPERTY()
TObjectPtr<class USPigeon> SerializedPigeon; // <class USPigeon> 전방 선언 효과
};
저장할 경로에 데이터 저장
미리보는전체코드.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "Game/SGameInstance.h"
#include "Kismet/KismetSystemLibrary.h"
#include "Examples/SFlyable.h"
#include "Examples/SPigeon.h"
USGameInstance::USGameInstance()
{
}
void USGameInstance::Init()
{
Super::Init();
// Step 1
FBirdData SrcRawData(TEXT("Pigeon17"), 17);
UE_LOG(LogTemp, Log, TEXT("[SrcRawData] Name : %s, ID : %d"), *SrcRawData.Name, SrcRawData.ID);
const FString SavedDir = FPaths::Combine(FPlatformMisc::ProjectDir(), TEXT("Saved"));
UE_LOG(LogTemp, Log, TEXT("*SavedDir : %s"), *SavedDir);
const FString RawDataFileName(TEXT("RawData.bin"));
FString AbsolutePathForRawData = FPaths::Combine(*SavedDir,*RawDataFileName);
UE_LOG(LogTemp, Log, TEXT("Relative path for saved file: %s"), *AbsolutePathForRawData);
FPaths::MakeStandardFilename(AbsolutePathForRawData);
UE_LOG(LogTemp, Log, TEXT("Absolute path for saved file: %s"), *AbsolutePathForRawData);
FArchive* RawFileWriterAr = IFileManager::Get().CreateFileWriter(*AbsolutePathForRawData);
if (nullptr != RawFileWriterAr) {
*RawFileWriterAr << SrcRawData;
RawFileWriterAr->Close();
delete RawFileWriterAr;
RawFileWriterAr = nullptr;
}
// Step 2
FBirdData DsRawData;
FArchive* RawFileReaderAr = IFileManager::Get().CreateFileReader(*AbsolutePathForRawData);
if (nullptr != RawFileReaderAr) {
*RawFileReaderAr << DsRawData;
RawFileReaderAr->Close();
delete RawFileReaderAr;
RawFileReaderAr = nullptr;
UE_LOG(LogTemp, Log, TEXT("[DsRawData] Name: %s, ID: %d"), *DsRawData.Name, DsRawData.ID);
}
// Save Object
SerializedPigeon = NewObject<USPigeon>();
SerializedPigeon->SetName("Pigeon76");
SerializedPigeon->SetID(76);
UE_LOG(LogTemp, Log, TEXT("[DsRawData] Name: %s, ID: %d"), *SerializedPigeon->GetName(), SerializedPigeon->GetID());
const FString ObjectDataFileName(TEXT("ObjectData.bin"));
FString AbsolutePathForObjectData = FPaths::Combine(*SavedDir, *ObjectDataFileName);
FPaths::MakeStandardFilename(AbsolutePathForObjectData);
TArray<uint8> BufferArray;
FMemoryWriter MemoryWriterAr(BufferArray);
SerializedPigeon->Serialize(MemoryWriterAr);
TUniquePtr<FArchive> ObjectDataFileWriterAr = TUniquePtr<FArchive>(IFileManager::Get().CreateFileWriter(*AbsolutePathForObjectData));
if (nullptr != ObjectDataFileWriterAr) {
*ObjectDataFileWriterAr << BufferArray;
ObjectDataFileWriterAr->Close();
ObjectDataFileWriterAr = nullptr;
}
TArray<uint8> BufferArrayFromObjectDataFile;
TUniquePtr<FArchive> ObjectDataFileReaderAr = TUniquePtr<FArchive>(IFileManager::Get().CreateFileReader(*AbsolutePathForObjectData));
if (nullptr != ObjectDataFileReaderAr) {
*ObjectDataFileReaderAr << BufferArrayFromObjectDataFile;
ObjectDataFileReaderAr->Close();
ObjectDataFileReaderAr = nullptr;
}
FMemoryReader MemoryReaderAr(BufferArrayFromObjectDataFile);
USPigeon* Pigeon77 = NewObject<USPigeon>();
Pigeon77->Serialize(MemoryReaderAr);
UE_LOG(LogTemp, Log, TEXT("[Pigeon77] Name : %s, ID : %d"), *Pigeon77->GetName(), Pigeon77->GetID());
/*
UE_LOG(LogTemp, Log, TEXT("USGameInstance Init"));
UKismetSystemLibrary::PrintString(this, TEXT("USGameInstance this Init"));
// this 대신 GetWorld
UKismetSystemLibrary::PrintString(GetWorld(), TEXT("USGameInstance GetWorld Init"));
USPigeon* pigeon1 = NewObject<USPigeon>();
ISFlyable* Bird1 = Cast<ISFlyable>(pigeon1);
//현업에서 인터페이스 개념은 대부분 이런 식으로 업캐스팅 하기 위함
if (nullptr != Bird1) {
Bird1->Fly();
}
*/
}
void USGameInstance::Shutdown()
{
Super::Shutdown();
UE_LOG(LogTemp, Log, TEXT("USGameInstance Shutdown"));
UKismetSystemLibrary::PrintString(this, TEXT("USGameInstance this Shutdown"));
// this 대신 GetWorld
UKismetSystemLibrary::PrintString(GetWorld(), TEXT("USGameInstance GetWorld Shutdown")); // 월드 객체를 가져올 수 있는 액터들 같은 경우 가능
}
순서대로 코드 설명
FBirdData SrcRawData(TEXT("Pigeon17"), 17);
Bird 데이터(raw data)를 만들어서 이름과 아이디를 넣는다.
const FString SavedDir = FPaths::Combine(FPlatformMisc::ProjectDir(), TEXT("Saved"));
saved dir은 프로젝트 디렉토리의 '경로'에 Saved 폴더를 붙인다.
const FString RawDataFileName(TEXT("RawData.bin"));
그 다음 'RawData.bin'이라는 데이터 파일에다가 저장을 하겠다라고 밝힌 후,
FString AbsolutePathForRawData = FPaths::Combine(*SavedDir,*RawDataFileName);
절대 경로를 만들어 준다(saved dir과 RawDataFileName 합체 : 파일의 전체 경로가 나옴
FPaths::MakeStandardFilename(AbsolutePathForRawData);
MakeStandardFilename() 함수를 사용하여 절대 경로를 언리얼에 맞게 포맷 조정해준 후,
이제 이 조정된 절대 경로에 저장을 준비해준다.
FArchive* RawFileWriterAr = IFileManager::Get().CreateFileWriter(*AbsolutePathForRawData);
파일라이터 아카이브를 만들어서 파일 매니저가 제공하는 파일라이터에 조정된 절대경로를 전달해준다.
if (nullptr != RawFileWriterAr) {
*RawFileWriterAr << SrcRawData;
RawFileWriterAr->Close();
이제 파일라이터 아카이브에서 처음에 만든 새데이터(RawData)를 넘겨주고 아카이브를 닫는다.
delete RawFileWriterAr;
RawFileWriterAr = nullptr;
그 다음에 만들어진 아카이브는 바로 지워주도록 한다.
}
저장된 정보 읽어오기
저장되어 있는 파일을 다시 한번 읽어 새 데이터에 저장
FBirdData DsRawData;
FArchive* RawFileReaderAr = IFileManager::Get().CreateFileReader(*AbsolutePathForRawData);
if (nullptr != RawFileReaderAr) {
*RawFileReaderAr << DsRawData;
RawFileReaderAr->Close();
delete RawFileReaderAr;
RawFileReaderAr = nullptr;
}
오브젝트 전체를 저장하고자 하는 경우
// Save Object
SerializedPigeon = NewObject<USPigeon>();
SerializedPigeon->SetName("Pigeon76");
SerializedPigeon->SetID(76);
UE_LOG(LogTemp, Log, TEXT("[DsRawData] Name: %s, ID: %d"), SerializedPigeon->GetName(), SerializedPigeon->GetID());
const FString ObjectDataFileName(TEXT("ObjectData.bin"));
FString AbsolutePathForObjectData = FPaths::Combine(*SavedDir, *ObjectDataFileName);
FPaths::MakeStandardFilename(AbsolutePathForObjectData);
TArray<uint8> BufferArray;
FMemoryWriter MemoryWriterAr(BufferArray);
SerializedPigeon->Serialize(MemoryWriterAr);
TUniquePtr<FArchive> ObjectDataFileWriterAr = TUniquePtr<FArchive>(IFileManager::Get().CreateFileWriter(*AbsolutePathForObjectData));
if (nullptr != ObjectDataFileWriterAr) {
*ObjectDataFileWriterAr << BufferArray;
ObjectDataFileWriterAr->Close();
ObjectDataFileWriterAr = nullptr;
}
TArray<uint8> BufferArrayFromObjectDataFile;
TUniquePtr<FArchive> ObjectDataFileReaderAr = TUniquePtr<FArchive>(IFileManager::Get().CreateFileReader(*AbsolutePathForObjectData));
if (nullptr != ObjectDataFileReaderAr) {
*ObjectDataFileReaderAr << BufferArrayFromObjectDataFile;
ObjectDataFileReaderAr->Close();
ObjectDataFileReaderAr = nullptr;
}
FMemoryReader MemoryReaderAr(BufferArrayFromObjectDataFile);
USPigeon* Pigeon77 = NewObject<USPigeon>();
Pigeon77->Serialize(MemoryReaderAr);
UE_LOG(LogTemp, Log, TEXT("[Pigeon77] Name : %s, ID : %d"), *Pigeon77->GetName(), Pigeon77->GetID());
BufferArray를 부분을 유심히 보면(위 코드 8라인) 자료형이 Uint8이다.
즉 한 칸이 1바이트짜리 씩인 TArray를 만들어 작성을 용이하게 해준다.
메모리라이터를 이용해 언리얼 오브젝트를 메모리에다 작성하기 위해 1바이트 짜리 어레이를 만들어 전달하는 것이다.
SerializedPigeon->Serialize(MemoryWriterAr)
상단 3줄로 만든 SerializedPigeon를 MemoryWriterAr에다 Serialize 해준다.
ObjectDataFileWriterAr가 있으면 버퍼에 적혀져 있는 내용을 전달해준다.
TUniquePtr<FArchive> ObjectDataFileWriterAr = TUniquePtr<FArchive>(IFileManager::Get().CreateFileWriter(*AbsolutePathForObjectData));
if (nullptr != ObjectDataFileWriterAr) {
*ObjectDataFileWriterAr << BufferArray;
ObjectDataFileWriterAr->Close();
ObjectDataFileWriterAr = nullptr;
}
저장된 것을 읽어올 버퍼 BufferArrayFromObjectDataFile
ObjectDataFileReaderAr가 있으면 BufferArrayFromObjectDataFile에다가 out(넣어줌)
TArray<uint8> BufferArrayFromObjectDataFile;
TUniquePtr<FArchive> ObjectDataFileReaderAr = TUniquePtr<FArchive>(IFileManager::Get().CreateFileReader(*AbsolutePathForObjectData));
if (nullptr != ObjectDataFileReaderAr) {
*ObjectDataFileReaderAr << BufferArrayFromObjectDataFile;
ObjectDataFileReaderAr->Close();
ObjectDataFileReaderAr = nullptr;
}
데이터가 잘 넣어졌는지 내용을 확인한다.
FMemoryReader MemoryReaderAr(BufferArrayFromObjectDataFile);
USPigeon* Pigeon77 = NewObject<USPigeon>();
Pigeon77->Serialize(MemoryReaderAr);
UE_LOG(LogTemp, Log, TEXT("[Pigeon77] Name : %s, ID : %d"), *Pigeon77->GetName(), Pigeon77->GetID());
실행결과
'Unreal' 카테고리의 다른 글
[Unreal] Line Trace 블루프린트와 C++로 구현 (1) | 2023.11.24 |
---|---|
[Unreal] 언리얼 Json 직렬화 및 역직렬화 (0) | 2023.11.23 |
[Unreal] singleton UGameInstance와 interface의 활용 예시 (0) | 2023.11.23 |
[Unreal] Maximo fbx 애니메이팅과 무기 socket 적용을 위한 animNotifyState (0) | 2023.11.07 |
[Unreal] 언리얼 VFX 에디터 나이아가라(Niagara) (0) | 2023.11.07 |