如果你只是单纯的想读取Json文件,拿到里面的数据来使用的话,可以到虚幻商城-插件搜索JsonBlueprint(免费的),然后按照对应的要求做就可以拿到数据并在UE4中使用了,放一个大佬写的使用说明:https://blog.csdn.net/ljason1993/article/details/105747884
我这里的所有操作就是将结构体转为Json字符串然后保存起来,然后在通过读取文件拿到Json字符串再转回我的结构体,从而达到保存数据的目的。(因为暂时不能用UE4自带的SaveGame的方式,所以只能先用这个办法了)
UE4 C++中有自带的将Json格式转换为UE4中的数据类型。但是如果将数据写入文件的时候的格式和Json格式不同,但是凭肉眼可能看不出来(就是写入的格式并不是UE4能够准确读取的格式),这个时候编译是能通过的,
写入也是可以写入的(这里在C++里面的判定就是,{},[].只要你的代码中能有最基础的架构,网上都能找到。那么编译就能通过,写入也可以成功,但是当你将你写入的数据转换回来的时候,你就会发现读不到你想要的值了。)
所以,为了能更加符合格式的写入,可以先在UE4 的编辑器中创建出你想要的数据结构,比如这次我需要的就是一个结构体(S_AllBuildingData),次级结构是一个Finpoint的结构体(MapSize)+一个自定义的结构体数组(S_BuildingData)如下图:
S_AllBuildingData:
S_BuildingData:
E_Orinetation:
S_Coord
现在根据最终结构(S_AllBuildingData)在UE4中创建一个数据表(DataTable)DT_AllBuildingData,然后随便添入一些数据(数组的地方最好添上2项,这样等下看格式的时候更加清晰)
这里我随便填入了一些数据,接下来将这个数据表导出为json格式的文件。
然后打开该json文件(我是通过txt打开的)
这里要注意的地方是,将数据表导出成json格式的话他会自动把刚刚在数据表中的列的名字添作新的一个Name数组,所以在数据中的第一项如果是Name的可以改一下名字(因为做这个其实只是需要看一下自己等会写成什么样的格式能够方便用UE4 C++ 中Json自带的转换。)将最外层的[]以及第一项的Name删除,就能得到我所需要的结构格式了,如图:
PS:这里可以清楚的看到Vector2D和Vector的三个值是需要用{}包起来的(我之前用的时候就是在C++中直接将Vector转成了String,然后Location这个数据始终读出来的都是0,0,0,然后我在同事的提醒下才想到上面的办法确保我的格式正确)
接下来就可以开始C++部分了(我在蓝图函数库里写的)
首先需要在"项目名".Build.cs模块中加入"Json"和"JsonUtilities",如图:
首先我得在C++ 中创建出我需要转换的结构体FBuilding,和FAllBuilding以及自定义的枚举E_Orinetation
/* 建筑朝向 */
UENUM(BlueprintType)
enum class EOrient : uint8
{
North,
East,
South,
West,
};
/* 建筑信息 */
USTRUCT()
struct FABuildingDta
{
GENERATED_USTRUCT_BODY()
public:
/* 所占坐标 */
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
TArray<FIntPoint> Coords;
/* 建筑类型,数据表中的RowName */
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
FName TypeName;
/* 世界位置 */
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
FVector Location;
/* 建筑朝向 */
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
EOrient Orientation;
};
/* 地图大小和所有建筑信息 */
USTRUCT()
struct FAllBuildingDta
{
GENERATED_USTRUCT_BODY()
public:
/* 地图大小 */
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
FIntPoint MapSize;
/* 所有的建筑信息 */
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
TArray<FABuildingDta> BuildingData;
};
接下来创建两个函数。保存和读取
public:
/* 保存数据 */
UFUNCTION(BlueprintCallable, Category = "SaveData")
static bool SaveData(const FString& Filename,FAllBuildingData AllBuildingData);
/* 读取数据 */
UFUNCTION(BlueprintCallable, Category = "LoadData")
static bool LoadData(const FString& Filename, FAllBuildingData& AllBuildingData);
接下来需要实现两个函数
.cpp
/* 将输入的数据拆分开来 塞进Json字符串里面 根据刚刚生成的Json格式文件来写 /
/ 所有括号中的值都不用加冒号,且在符号前的名都只用输入Start中即可 /
/ JsonWriter->WriteObjectStart(),就相当于数据中的"{",括号里面填"{“前面的值;JsonWriter->WriteObjectEnd();就相当于数据中的”}" /
/ JsonWriter->WriteArrayStart(),就相当于数据中的"[",括号里面填"[“前面的值;JsonWriter->WriteArrayEnd();就相当于数据中的”]" /
/ 上面这两个都是一对的,的对应起来不然编译不过,所以可以先把结构建立起来,再往里面填 */
bool UUe4DataToJason::SaveData(const FString& Filename, FAllBuildingData AllBuildingData)
{
/* 创建一个空的字符串 */
FString JsonStr = "";
/* 拆分一下获得的数据 */
FIntPoint MapSize = AllBuildingData.MapSize;
TArray<FBuildingData> BuildingData = AllBuildingData.BuildingData;
/* 将数据用Json格式写入文件 */
/* 创建Json字符串 */
TSharedRef< TJsonWriter<>> JsonWriter = TJsonWriterFactory<>::Create(&JsonStr);
/* 最开始的"{" */
JsonWriter->WriteObjectStart();
/* 地图信息 */
JsonWriter->WriteObjectStart(TEXT("MapSize"));
JsonWriter->WriteValue(TEXT("X"), MapSize.X);
JsonWriter->WriteValue(TEXT("Y"), MapSize.Y);
JsonWriter->WriteObjectEnd();
/* 所有建筑信息 */
JsonWriter->WriteArrayStart(TEXT("BuildingData"));
for (auto& ABuildingData : BuildingData)
{
JsonWriter->WriteObjectStart();
/* 单个建筑坐标信息 */
JsonWriter->WriteArrayStart(TEXT("Coords"));
for (auto& Coord : ABuildingData.Coords)
{
JsonWriter->WriteObjectStart();
JsonWriter->WriteValue(TEXT("X"), Coord.X);
JsonWriter->WriteValue(TEXT("Y"), Coord.Y);
JsonWriter->WriteObjectEnd();
}
JsonWriter->WriteArrayEnd();
/* 建筑类型名字 */
JsonWriter->WriteValue(TEXT("TypeName"), ABuildingData.TypeName.ToString());
/* 建筑位置 */
JsonWriter->WriteObjectStart(TEXT("Location"));
JsonWriter->WriteValue(TEXT("X"), ABuildingData.Location.X);
JsonWriter->WriteValue(TEXT("Y"), ABuildingData.Location.Y);
JsonWriter->WriteValue(TEXT("Z"), ABuildingData.Location.Z);
JsonWriter->WriteObjectEnd();
/* 建筑朝向 */
UEnum* EnumPtr = FindObject<UEnum>(ANY_PACKAGE, TEXT("EOrient"), true);
JsonWriter->WriteValue(TEXT("Orientation"), EnumPtr->GetNameByValue((int)ABuildingData.Orientation).ToString());
JsonWriter->WriteObjectEnd();
}
JsonWriter->WriteArrayEnd();
JsonWriter->WriteObjectEnd();
JsonWriter->Close();
/* 保存文件 */
FString FinalStr = FPaths::ProjectDir() + "SaveFile/" + Filename/* + ".json"*/;
return FFileHelper::SaveStringToFile(JsonStr, *FinalStr);
}
读取:
bool UUe4DataToJason::LoadData(const FString& Filename, FAllBuildingData& AllBuildingData)
{
/* 创建空的Json字符串 */
FString JsonStr = "";
/* 判断文件是否存在 */
if (!Filename.IsEmpty())
{
/* 读取路径 */
FString AbsoPath = FPaths::ProjectDir() + "SaveFile/" + Filename;
/* 读取是否成功 */
if (FFileHelper::LoadFileToString(JsonStr, *AbsoPath))
{
/* 加载文件并将拿到的Json字符串转为对应结构体 */
FJsonObjectConverter::JsonObjectStringToUStruct<FAllBuildingData>(
JsonStr,
&AllBuildingData,
0, 0);
}
}
return false;
}
测试:
这里我测试的是打印各个建筑类型名的位置信息也就是Name + Location的值
原数据
测试结果:
这个AllBuildingData在C++中也就可以当正常的结构体使用了,比如:
另外这个里面其实还有将JsonArray/JsonArrayString转为结构体的方法
FJsonObjectConverter::
完整代码:
.h
#pragma once
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "Ue4DataToJason.generated.h"
/**
*
*/
/* 建筑朝向 */
UENUM(BlueprintType)
enum class EOrient : uint8
{
North,
East,
South,
West,
};
/****************/
/**-- 结构体 --**/
/****************/
/* 建筑信息 */
USTRUCT(BlueprintType)
struct FBuildingData
{
GENERATED_USTRUCT_BODY()
public:
/* 所占坐标 */
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
TArray<FIntPoint> Coords;
/* 建筑类型,数据表中的RowName */
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
FName TypeName;
/* 世界位置 */
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
FVector Location;
/* 建筑朝向 */
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
EOrient Orientation;
};
/* 地图大小和所有建筑信息 */
USTRUCT(BlueprintType)
struct FAllBuildingData
{
GENERATED_USTRUCT_BODY()
public:
/* 地图大小 */
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
FIntPoint MapSize;
/* 所有的建筑信息 */
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
TArray<FBuildingData> BuildingData;
};
UCLASS()
class UE4DATATOJSON_API UUe4DataToJason : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
/* 保存数据 */
UFUNCTION(BlueprintCallable, Category = "SaveData")
static bool SaveData(const FString& Filename,FAllBuildingData AllBuildingData);
/* 读取数据 */
UFUNCTION(BlueprintCallable, Category = "LoadData")
static bool LoadData(const FString& Filename, FAllBuildingData& AllBuildingData);
};
.cpp
#include "Ue4DataToJason.h"
#include "Json.h"
#include "JsonObjectConverter.h"
//保存数据
bool UUe4DataToJason::SaveData(const FString& Filename, FAllBuildingData AllBuildingData)
{
/* 将输入的数据拆分开来 塞进Json字符串里面 根据刚刚生成的Json格式文件来写 */
/* 所有括号中的值都不用加冒号,且在符号前的名都只用输入Start中即可 */
/* JsonWriter->WriteObjectStart(),就相当于数据中的"{",括号里面填"{"前面的值;JsonWriter->WriteObjectEnd();就相当于数据中的"}" */
/* JsonWriter->WriteArrayStart(),就相当于数据中的"[",括号里面填"["前面的值;JsonWriter->WriteArrayEnd();就相当于数据中的"]" */
/* 上面这两个都是一对的,的对应起来不然编译不过,所以可以先把两个都写出来,再往里面填 */
/* 创建一个空的字符串 */
FString JsonStr = "";
/* 拆分一下获得的数据 */
FIntPoint MapSize = AllBuildingData.MapSize;
TArray<FBuildingData> BuildingData = AllBuildingData.BuildingData;
/* 将数据用Json格式写入文件 */
/* 创建Json字符串 */
TSharedRef< TJsonWriter<>> JsonWriter = TJsonWriterFactory<>::Create(&JsonStr);
/* 最开始的"{" */
JsonWriter->WriteObjectStart();
/* 地图信息 */
JsonWriter->WriteObjectStart(TEXT("MapSize"));
JsonWriter->WriteValue(TEXT("X"), MapSize.X);
JsonWriter->WriteValue(TEXT("Y"), MapSize.Y);
JsonWriter->WriteObjectEnd();
/* 所有建筑信息 */
JsonWriter->WriteArrayStart(TEXT("BuildingData"));
for (auto& ABuildingData : BuildingData)
{
JsonWriter->WriteObjectStart();
/* 单个建筑坐标信息 */
JsonWriter->WriteArrayStart(TEXT("Coords"));
for (auto& Coord : ABuildingData.Coords)
{
JsonWriter->WriteObjectStart();
JsonWriter->WriteValue(TEXT("X"), Coord.X);
JsonWriter->WriteValue(TEXT("Y"), Coord.Y);
JsonWriter->WriteObjectEnd();
}
JsonWriter->WriteArrayEnd();
/* 建筑类型名字 */
JsonWriter->WriteValue(TEXT("TypeName"), ABuildingData.TypeName.ToString());
/* 建筑位置 */
JsonWriter->WriteObjectStart(TEXT("Location"));
JsonWriter->WriteValue(TEXT("X"), ABuildingData.Location.X);
JsonWriter->WriteValue(TEXT("Y"), ABuildingData.Location.Y);
JsonWriter->WriteValue(TEXT("Z"), ABuildingData.Location.Z);
JsonWriter->WriteObjectEnd();
/* 建筑朝向 */
UEnum* EnumPtr = FindObject<UEnum>(ANY_PACKAGE, TEXT("EOrient"), true);
JsonWriter->WriteValue(TEXT("Orientation"), EnumPtr->GetNameByValue((int)ABuildingData.Orientation).ToString());
JsonWriter->WriteObjectEnd();
}
JsonWriter->WriteArrayEnd();
JsonWriter->WriteObjectEnd();
JsonWriter->Close();
/* 保存文件 */
FString FinalStr = FPaths::ProjectDir() + "SaveFile/" + Filename/* + ".json"*/;
return FFileHelper::SaveStringToFile(JsonStr, *FinalStr);
}
//读取数据
bool UUe4DataToJason::LoadData(const FString& Filename, FAllBuildingData& AllBuildingData)
{
/* 创建空的Json字符串 */
FString JsonStr = "";
/* 判断文件是否存在 */
if (!Filename.IsEmpty())
{
/* 读取路径 */
FString AbsoPath = FPaths::ProjectDir() + "SaveFile/" + Filename;
if (FFileHelper::LoadFileToString(JsonStr, *AbsoPath))
{
/* 加载文件并将拿到的Json字符串转为对应结构体 */
FJsonObjectConverter::JsonObjectStringToUStruct<FAllBuildingData>(
JsonStr,
&AllBuildingData,
0, 0);
FIntPoint TestMapSize = AllBuildingData.MapSize;
}
}
return false;
}
-----------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------
11.11更新:
直接定义好结构体
然后可以用
FJsonObjectConverter::UStructToJsonObjectString(结构体, Json字符串, 0, 0);
直接完成结构体转换为Json字符串
然后想转回去直接
FJsonObjectConverter::JsonObjectStringToUStruct<结构体>
参考链接:
UE4 的json读写方式 https://www.cnblogs.com/sevenyuan/p/7116686.html
C++ Nested JSON in Unreal Engine 4 https://stackoverflow.com/questions/30342465/c-nested-json-in-unreal-engine-4