일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 언리얼뮤지컬
- 스터디
- 데이터베이스
- 디자드
- 미니프로젝트
- node
- 언리얼
- 정글사관학교
- 프메
- flask
- JWT
- 스마일게이트
- 카렌
- 파이썬서버
- Unseen
- 언리얼프로그래머
- 프린세스메이커
- 게임개발
- EnhancedInput
- VUE
- Jinja2
- 알고풀자
- 마인크래프트뮤지컬
- 레베카
- Express
- Bootstrap4
- R
- Enhanced Input System
- Ajax
- 으
- Today
- Total
Showing
[Unreal] 언리얼 Json 직렬화 및 역직렬화 본문
*본 포스팅은 언리얼에서 Json 직렬화하는 방법을 작성하였습니다. 직렬화 개념 자체에 대해서 알고 싶으신 분께는 해당 포스팅을 추천드립니다.
많은 경우, 아이템 목록이 Json으로 작성되어 있을 가능성이 높고, 이를 직렬화할 줄 알아야 할 것이다.
1. 모듈 추가
source 폴더 안의 build.cs를 건들지 않았다면 코드는 아래와 같이 작성되어 있을 것이다.
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
public class TracesoftheFox : ModuleRules
{
public TracesoftheFox(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" });
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
}
}
아래와 같이 수정해준다.
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "Json", "JsonUtilities" });
2. 코드 작성
USGameInstance에서 json 직렬화 코드 예제를 작성해보도록 한다.
우선, 아래와 같이 2개의 헤더파일이 필요하다.
#include "JsonObjectConverter.h"
#include "UObject/SavePackage.h"
Json Object vs Json value
Json 파일 포맷에다가 무언가를 저장할 때 Json Object가 있고 Json value가 있다.
Json Object는 new object를 통해 만든 것을 저장할 때 혹은 읽어올 때 쓰는 구조체이다.
그냥 값만 쓰거나 primitive type만 json에다 저장하거나, 혹은 읽어올 때도
only '값' 즉 primitive type만 긁어온다고 한다면 Json value를 쓰면 된다.
// Json
const FString JsonDataFileName(TEXT("StudyJsonFile.txt"));
FString AbsolutePathForJsonData = FPaths::Combine(*SavedDir, *JsonDataFileName);
FPaths::MakeStandardFilename(AbsolutePathForJsonData);
TSharedRef<FJsonObject> SrcJsonObject = MakeShared<FJsonObject>();
FJsonObjectConverter::UStructToJsonObject(SerializedPigeon->GetClass(), SerializedPigeon, SrcJsonObject);
FString JsonOutString;
TSharedRef<TJsonWriter<TCHAR>> JsonWriterAr = TJsonWriterFactory<TCHAR>::Create(&JsonOutString);
if (true == FJsonSerializer::Serialize(SrcJsonObject, JsonWriterAr)) {
FFileHelper::SaveStringToFile(JsonOutString, *AbsolutePathForJsonData);
}
FString JsonInString;
FFileHelper::LoadFileToString(JsonInString, *AbsolutePathForJsonData);
TSharedRef<TJsonReader<TCHAR>> JsonReaderAr = TJsonReaderFactory<TCHAR>::Create(JsonInString);
TSharedPtr<FJsonObject> DstJsonObject;
if (true == FJsonSerializer::Deserialize(JsonReaderAr, DstJsonObject)) {
USPigeon* Pigeon78 = NewObject<USPigeon>();
if (true == FJsonObjectConverter::JsonObjectToUStruct(DstJsonObject.ToSharedRef(), Pigeon78-> GetClass(), Pigeon78)) {
UE_LOG(LogTemp, Log, TEXT("[Pigeon78] Name: %s, ID: %d"), *Pigeon78->GetName(), Pigeon78->GetID());
}
}
3. 코드 설명
// Json
저장 로직
Json 파일 위치를 정해준다.
const FString JsonDataFileName(TEXT("StudyJsonFile.txt"));
경로 만들어준다.
FString AbsolutePathForJsonData = FPaths::Combine(*SavedDir, *JsonDataFileName);
FPaths::MakeStandardFilename(AbsolutePathForJsonData);
저장이 될 아이를 만든다. SrcJsonObject! MakeShared를 통해 JsonObject를 만들어준다.
TSharedRef<FJsonObject> SrcJsonObject = MakeShared<FJsonObject>();
FJsonObjectConverter::UStructToJsonObject(SerializedPigeon->GetClass(), SerializedPigeon, SrcJsonObject);
FString JsonOutString;
FJsonObject는 안전하게 공유해야 하므로 TSharedRef를 사용한다.
TSharedRef<TJsonWriter<TCHAR>> JsonWriterAr = TJsonWriterFactory<TCHAR>::Create(&JsonOutString);
if (true == FJsonSerializer::Serialize(SrcJsonObject, JsonWriterAr)) {
JsonWriterAr가 있으면 save 한다.
FFileHelper::SaveStringToFile(JsonOutString, *AbsolutePathForJsonData);
}
읽어오기 로직
위에서 세이브된 스트링을 바로 읽어오도록 한다.
FString JsonInString;
FFileHelper::LoadFileToString(JsonInString, *AbsolutePathForJsonData);
JsonReaderAr를 만든다.
TSharedPtr<TJsonReader<TCHAR>> JsonReaderAr = TJsonReaderFactory<TCHAR>::Create(JsonInString);
읽어오기 위해 쓸 변수 DstJsonObject를 만든다.
TSharedPtr<FJsonObject> DstJsonObject;
저장되어 있던 것을 Deserialize해줌으로써 Destination json object에 담아줄 수 있도록 한다.
if (true == FJsonSerializer::Deserialize(JsonReaderAr, DstJsonObject)) {
Deserialize가 성공한다면 Pigeon78 객체를 하나 만들어서 Deserialize내용을 적는다.
USPigeon* Pigeon78 = NewObject<USPigeon>();
if (true == FJsonObjectConverter::JsonObjectToUStruct(DstJsonObject.ToSharedRef(), Pigeon78-> GetClass(), Pigeon78)) {
UE_LOG(LogTemp, Log, TEXT("[Pigeon78] Name: %s, ID: %d"), *Pigeon78->GetName(), Pigeon78->GetID());
}
}
4. 전체 코드
// 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"
#include "JsonObjectConverter.h"
#include "UObject/SavePackage.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());
// Json
const FString JsonDataFileName(TEXT("StudyJsonFile.txt"));
FString AbsolutePathForJsonData = FPaths::Combine(*SavedDir, *JsonDataFileName);
FPaths::MakeStandardFilename(AbsolutePathForJsonData);
TSharedRef<FJsonObject> SrcJsonObject = MakeShared<FJsonObject>();
FJsonObjectConverter::UStructToJsonObject(SerializedPigeon->GetClass(), SerializedPigeon, SrcJsonObject);
FString JsonOutString;
TSharedRef<TJsonWriter<TCHAR>> JsonWriterAr = TJsonWriterFactory<TCHAR>::Create(&JsonOutString);
if (true == FJsonSerializer::Serialize(SrcJsonObject, JsonWriterAr)) {
FFileHelper::SaveStringToFile(JsonOutString, *AbsolutePathForJsonData);
}
FString JsonInString;
FFileHelper::LoadFileToString(JsonInString, *AbsolutePathForJsonData);
TSharedRef<TJsonReader<TCHAR>> JsonReaderAr = TJsonReaderFactory<TCHAR>::Create(JsonInString);
TSharedPtr<FJsonObject> DstJsonObject;
if (true == FJsonSerializer::Deserialize(JsonReaderAr, DstJsonObject)) {
USPigeon* Pigeon78 = NewObject<USPigeon>();
if (true == FJsonObjectConverter::JsonObjectToUStruct(DstJsonObject.ToSharedRef(), Pigeon78-> GetClass(), Pigeon78)) {
UE_LOG(LogTemp, Log, TEXT("[Pigeon78] Name: %s, ID: %d"), *Pigeon78->GetName(), Pigeon78->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")); // 월드 객체를 가져올 수 있는 액터들 같은 경우 가능
}
'Unreal' 카테고리의 다른 글
[Unreal] 블루프린트를 이용한 캐릭터 hp/mp 상태바 위젯 구현 (1) | 2023.11.24 |
---|---|
[Unreal] Line Trace 블루프린트와 C++로 구현 (1) | 2023.11.24 |
[Unreal] 언리얼 직렬화, Serialize (1) | 2023.11.23 |
[Unreal] singleton UGameInstance와 interface의 활용 예시 (0) | 2023.11.23 |
[Unreal] Maximo fbx 애니메이팅과 무기 socket 적용을 위한 animNotifyState (0) | 2023.11.07 |