UE基础篇三:数据驱动_udevelopersettings-程序员宅基地

技术标签: ue5  ue4  Unreal 必学系列  unreal engine  

初级:

Gameplay数据

  • C++ 类:原生类构造函数设置默认值并支持数据继承。数据也可以硬编码到函数局部变量中,但很难跟踪。
  • 配置文件:Ini文件和控制台变量支持覆盖在C++构造函数中声明的数据,也可以直接查询。
  • 蓝图类:蓝图类默认值与C++类构造函数的作用相同,支持数据继承。数据也可以在函数局部变量或引脚文字值中安全地设置。
  • 数据资源:对于无法实例化且不需要数据继承的对象,独立数据资源比蓝图默认值更易于使用。
  • 表:数据可以作为数据表、曲线表导入,也可以在运行时读取。
  • 放置实例:数据可以存储在关卡或其他资源内设置的蓝图或C++类实例中,并会覆盖类默认值。
  • 自定义系统:与逻辑一样,可以使用多种自定义方法来存储数据。
    保存游戏:运行时保存游戏文件可用于覆盖或修改上述数据类型。

1、结构体

USTRUCT(BlueprintType)
struct DATADRIVEN_API FShopItem
{
	GENERATED_BODY();
public:
	UPROPERTY(BlueprintReadWrite,EditAnywhere)
		FText Name;
	UPROPERTY(BlueprintReadWrite, EditAnywhere)
		UTexture2D* Icon;
	UPROPERTY(BlueprintReadWrite, EditAnywhere)
		float Price;
};

2、ConstructorHelpers

ConstructorHelpers只能在构造函数中使用。如果在非构造函数中使用的话,则会引起Crash(原因不明),在其代码内部检查了是否在构造函数中调用:CheckIfIsInConstructor(ObjectToFind);

如下 在构造函数中查找UMG 并且设置WidgetClass

ConstructorHelpers::FClassFinder<UUserWidget> classFinder(TEXT("/Game/Blueprint/CPP/UMG_Shop_CPP"));
	Widget= CreateDefaultSubobject<UWidgetComponent>("Widget");
	Widget->SetupAttachment(RootComponent);
	Widget->SetRelativeLocation(FVector(0.f, 0.f, 110.f));
	Widget->SetRelativeScale3D(FVector(0.3f, 0.3f, 0.3f));
	Widget->SetDrawAtDesiredSize(true);
	Widget->SetManuallyRedraw(true);

	if (classFinder.Succeeded())
	{
		Widget->SetWidgetClass(classFinder.Class);
	}

3、Class Reference

中级:

4、Datatable

4.1 蓝图创建如下UMG,每个Item显示图片,名字,价格

数据结构设计:

USTRUCT(BlueprintType)
struct FShopItem : public FTableRowBase
{
	GENERATED_BODY()
	
	UPROPERTY(BlueprintReadWrite,EditAnywhere)
	FText Name;
	UPROPERTY(BlueprintReadWrite, EditAnywhere)
	UTexture2D* Icon;
	UPROPERTY(BlueprintReadWrite, EditAnywhere)
	float Price;
};

基于ShopItem创建DataTable, 可以在UE引擎里 点击添加进行操作,也可以导出成Json文件或CSV文件 进行修改,再点重新导入

创建单个UMG_Item

在图表里,添加ShopItem变量,并且公开,去更新图片,价格,名字

创建一个UMG_Shop,用来显示数据表里的Item

蓝图里获取DataTable的值

添加UMG_Item

4.2 C++中调用Datatable,LoadObject

// ShopConfig.h
struct FShopItem;

UCLASS(Blueprintable, Category = "DataDriven")
class UShopConfig : public UObject
{
	GENERATED_BODY()
public:
	virtual bool Initialize();
public:
	UFUNCTION(BlueprintPure, DisplayName = "ShopConfig")
		static UShopConfig* Get();

public:
	void GetAllItems(TArray<FShopItem*>& outItems);
protected:
	UPROPERTY()
		UDataTable* mItemsDataTable;
};

//ShopConfig.cpp

bool UShopConfig::Initialize()
{

	mItemsDataTable = LoadObject<UDataTable>(nullptr, TEXT("/Game/Blueprint/DataTable/DT_ShopItems.DT_ShopItems"));

	return mItemsDataTable!=nullptr;
}

UShopConfig* UShopConfig::Get()
{
//获取类的可变默认对象,GetDefault不可变
	return GetMutableDefault<UShopConfig>();
}

void UShopConfig::GetAllItems(TArray<FShopItem*>& outItems)
{
	static const FString ContextString(TEXT("GENERAL"));
	mItemsDataTable->GetAllRows(ContextString,outItems);
}

使用方法:

void UBaseShopWdget::NativePreConstruct()
{
	Super::NativePreConstruct();

	if (ItemWidgetClass == nullptr)
	{
		return;
	}

	ItemsUniformGridPanel->ClearChildren();
	TArray<FShopItem*> outItems;
	UShopConfig::Get()->GetAllItems(outItems);

	for (int32 i = 0; i < outItems.Num(); ++i)
	{
		UBaseItemWdget* itemWidget = CreateWidget<UBaseItemWdget>(ItemsUniformGridPanel, ItemWidgetClass);
		itemWidget->Item = *outItems[i];

		ItemsUniformGridPanel->AddChildToUniformGrid(itemWidget, i / 5, i % 5);
	}
}

4.3 Composite Datatable : 复合数据表,可以堆叠多个DataTable,

当出现相同的数据索引时,以最后面的表为第一优先级

5、CurveTable: 用于一些比较轻量的数值

外部CSV 编辑好表后,直接拖入场景,选择CurveTable

6、DataAsset

继承自UPrimaryDataAsset(方便管理数据对象的加载释放) ,也可以继承UDataAsset

UCLASS(BlueprintType)
class UShopItemAsset :public UPrimaryDataAsset
{
	GENERATED_BODY()
public:
	UPROPERTY(BlueprintReadWrite,EditAnywhere)
		FText Name;
	UPROPERTY(BlueprintReadWrite, EditAnywhere)
		UTexture2D* Icon;
	UPROPERTY(BlueprintReadWrite, EditAnywhere)
		float Price;
};

资产管理器中添加路径,可以动态加载

加载方式:

1.

2.通过Key动态加载

张乂卓:UE4动作游戏实例RPG Action解析五:GameInstance加载UPrimaryDataAsset(武器资源)并加载默认武器资源

多个DataAsset 可以批量编辑

6.2 全局数据

UCLASS(BlueprintType)
class UShopGlobalSettings : public UDataAsset
{
	GENERATED_BODY()
public:
	UShopGlobalSettings();
public:
	UFUNCTION(BlueprintPure, DisplayName = "ShopGlobalSettings")
		static UShopGlobalSettings* Get();

	UPROPERTY(BlueprintReadWrite, EditAnywhere)
		float PlayerStartMoney = 100.f;	
};

//cpp

UShopGlobalSettings::UShopGlobalSettings()
{
	
}

UShopGlobalSettings* UShopGlobalSettings::Get()
{
	static UShopGlobalSettings* obj=nullptr;
	if (obj==nullptr)
	{
		obj = LoadObject<UShopGlobalSettings>(nullptr, TEXT("/Game/Blueprint/DataAsset/DA_ShopGlobalSettings"));
		obj->AddToRoot();
	}
	return obj;
}

7、WorldSetting : 设置自己的WorldSetting

使用方法:

UCLASS(Abstract)
class UPlayerWidget : public UUserWidget
{
	GENERATED_BODY()
public:
public:
	virtual void NativePreConstruct()override;
	virtual void NativeConstruct()override;
public:
	UPROPERTY(meta = (BindWidget))
		class UTextBlock* DifficultyTextBlock;
};

void UPlayerWidget::NativePreConstruct()
{
	Super::NativePreConstruct();

	
}

void UPlayerWidget::NativeConstruct()
{
	Super::NativeConstruct();

	AWorldSettings* settings= UGameplayStatics::GetGameMode(this)->GetWorld()->GetWorldSettings();
	AShopWorldSettings* shopSettings=Cast<AShopWorldSettings>(settings);
	if (shopSettings!=nullptr)
	{
		DifficultyTextBlock->SetText(UKismetTextLibrary::Conv_FloatToText(shopSettings->LevelDifficulty, ERoundingMode::HalfToEven));
	}

}

高级:

8 Settings.ini

8.1 UDeveloperSetting

创建一个C++文件继承DeveloperSettings

UCLASS(config = DataDrivenProjectSettings, defaultconfig)
class UDataDrivenProjectSettings :public UDeveloperSettings
{
	GENERATED_BODY()
public:
	/** Gets the settings container name for the settings, either Project or Editor */
	virtual FName GetContainerName() const override { return TEXT("Project"); }
	/** Gets the category for the settings, some high level grouping like, Editor, Engine, Game...etc. */
	virtual FName GetCategoryName() const override { return TEXT("DataDriven"); }
	/** The unique name for your section of settings, uses the class's FName. */
	virtual FName GetSectionName() const override { return TEXT("DataDriven"); }
public:

	UFUNCTION(BlueprintPure, DisplayName = "DataDrivenProjectSettings")
		static UDataDrivenProjectSettings* Get() { return GetMutableDefault<UDataDrivenProjectSettings>(); }
public:
	UPROPERTY(Config, EditAnywhere, BlueprintReadWrite, Category = Shop)
	float PriceOff = 50.f;

	UPROPERTY(Config, EditAnywhere, BlueprintReadWrite, Category = Player)
	float PlayerStartLevel = 10.f;

};

重新编译之后,在项目设置里面会出现如下信息

修改其中的值,在项目Config目录里会出现新的文件

蓝图调用:

8.2 对类配置

调用父类方法

UCLASS(config = CustomConfig)
class ACustomConfigActor :public AActor
{
	GENERATED_BODY()
public:
	ACustomConfigActor();
public:
	UPROPERTY(Config, EditAnywhere, BlueprintReadWrite, Category = MyCustom)
	float MaxLife = 100.f;

	UPROPERTY(transient, EditAnywhere, BlueprintReadWrite, Category = MyCustom)
	float CurrentLife = 50.f;
public:
	virtual void BeginPlay()override;

	UFUNCTION(BlueprintCallable)
	void AddMaxLifeAndSave(float val);


};

//cpp
ACustomConfigActor::ACustomConfigActor()
{
}

void ACustomConfigActor::BeginPlay()
{
	Super::BeginPlay();
	LoadConfig();
}

void ACustomConfigActor::AddMaxLifeAndSave(float val)
{
	MaxLife+=val;
	SaveConfig();
}

9、ConsoleVariables: 方便用于调试

dd.ShopEnabled 接数值

UCLASS(BlueprintType,Blueprintable)
class AConsoleVariableActor :public AActor
{
	GENERATED_BODY()
public:
	AConsoleVariableActor();
public:
	virtual void BeginPlay()override;
	virtual void Tick(float DeltaSeconds)override;
};

static TAutoConsoleVariable<bool> CVarDDShopEnabled(
	TEXT("dd.ShopEnabled"),
	false,
	TEXT("The switch to enable shop or not."),
	ECVF_Default);

static TAutoConsoleVariable<int> CVarDDShopCount(
	TEXT("dd.ShopCount"),
	3,
	TEXT("The count of shop."),
	ECVF_Default);

static TAutoConsoleVariable<FString> CVarDDShopName(
	TEXT("dd.ShopName"),
	TEXT("Jack"),
	TEXT("The name of shop."),
	ECVF_Default);

AConsoleVariableActor::AConsoleVariableActor()
{
	PrimaryActorTick.bCanEverTick=true;
}

void AConsoleVariableActor::BeginPlay()
{
	Super::BeginPlay();
}

void AConsoleVariableActor::Tick(float DeltaSeconds)
{
	Super::Tick(DeltaSeconds);

	FString str=FString::Printf(TEXT("dd.ShopEnabled: %s"), CVarDDShopEnabled.GetValueOnGameThread()?TEXT("true"):TEXT("false"));
	str += FString::Printf(TEXT("\ndd.ShopCount: %d"), CVarDDShopCount.GetValueOnGameThread());
	str += FString::Printf(TEXT("\ndd.ShopName: %s"), *CVarDDShopName.GetValueOnGameThread());
	
	GEngine->AddOnScreenDebugMessage(0,5.f,FColor::Red, str);

}

10、原始数据

10.1 txt

FString ARawDataActor::ReadTxtFile(FString path)
{
	FString realPath = FPaths::ProjectDir() / path;
	FString content;
	FFileHelper::LoadFileToString(content, *realPath);
	
	return content;
}

10.2 Load Csv/Json as Table

bool ARawDataActor::LoadCsvAsTable(FString path, TArray<FShopItem>& Items)
{
	UDataTable* DataTable = NewObject<UDataTable>(GetTransientPackage(), FName(TEXT("TempDataTable")));
	DataTable->RowStruct = FShopItem::StaticStruct();

	FString realPath = FPaths::ProjectDir() / path;
	FString content;

	if (!FFileHelper::LoadFileToString(content, *realPath))
	{
		return false;
	}

	DataTable->CreateTableFromCSVString(content);	//CreateTableFromJSONString

	static const FString ContextString(TEXT("GENERAL"));
	DataTable->ForeachRow<FShopItem>(ContextString, [&Items](const FName& key, const FShopItem& value)
		{
			Items.Add(value);
		});

	return true;
}

bool ARawDataActor::LoadJsonAsTable(FString path, TArray<FShopItem>& Items)
{
	FString realPath = FPaths::ProjectDir() / path;
	FString content;
	FFileHelper::LoadFileToString(content, *realPath);

	TSharedPtr<FJsonObject> jsonRoot = MakeShareable(new FJsonObject());
	TSharedRef<FJsonStringReader> jsonReader= FJsonStringReader::Create(content);

	TArray<TSharedPtr<FJsonValue>> OutArray;

	if (FJsonSerializer::Deserialize<TCHAR>(jsonReader, OutArray))
	{
		for (TSharedPtr<FJsonValue> itemJson: OutArray)
		{
			const TSharedPtr<FJsonObject>& obj=itemJson->AsObject();
			FShopItem& newItem= Items.AddDefaulted_GetRef();
			newItem.Name=FText::FromString(obj->GetStringField(TEXT("Name")));
			newItem.Icon=LoadObject<UTexture2D>(nullptr,*obj->GetStringField(TEXT("Icon")));
			newItem.Price = obj->GetNumberField(TEXT("Price"));
		}
		return true;
	}
	return false;
}

10.3 Excel DirectExcel

bool UExcelWorkbook::StartWatch(const UObject* WorldContextObject, FExcelWorkbookChangedDelegate onChanged, bool autoReload /*= true*/, float rate/*=1.f*/)
{
#ifdef WIN32

	//wchar_t watchDirectory[512] = { 0 };
	//MultiByteToWideChar(CP_ACP, 0, (char*)*mPath, strlen((char*)*mPath), watchDirectory, sizeof(watchDirectory) / sizeof(wchar_t));
	FString path = FPaths::GetPath(mPath);
	const wchar_t* p = *path;

	mFindHandle = ::FindFirstChangeNotification(p, 0, FILE_NOTIFY_CHANGE_LAST_WRITE);
	if (INVALID_HANDLE_VALUE == mFindHandle)
	{
		DWORD errorCode = GetLastError();
		UE_LOG(LogDirectExcel, Error, L"%d", errorCode);
		return false;
	}

	UWorld* world = GEngine->GetWorldFromContextObjectChecked(WorldContextObject);
	world->GetTimerManager().SetTimer(mWatchTimer, [this, WorldContextObject, onChanged, autoReload]
	{
		DWORD dwWait = ::WaitForSingleObject(mFindHandle, 0);
		UE_LOG(LogDirectExcel, Warning, TEXT("Watch result:%d."), dwWait);
		if (dwWait == WAIT_FAILED)
		{
			DWORD errorCode = GetLastError();
			UE_LOG(LogDirectExcel, Error, L"%d", errorCode);
			StopWatch(WorldContextObject);
			return;
		}

		if (WAIT_OBJECT_0 == dwWait)
		{
			bool isOpen = true;
			if (autoReload)
			{
				isOpen = Reload();
			}

			if (isOpen)
			{
				onChanged.ExecuteIfBound(this);
			}
			else
			{
				onChanged.ExecuteIfBound(nullptr);
			}

			if (!::FindNextChangeNotification(mFindHandle))
			{
				::FindCloseChangeNotification(mFindHandle);
				mFindHandle = NULL;
				UE_LOG(LogDirectExcel, Warning, TEXT("Lost watch."));
			}
		}

	}, rate, true);
#endif

	return true;

}

10.4 SQL

UCLASS(BlueprintType,Blueprintable)
class ASqlDataActor :public AActor
{
	GENERATED_BODY()
public:
	ASqlDataActor();
public:
	virtual void BeginPlay()override;
public:
	UFUNCTION(BlueprintCallable)
		bool LoadSqlAsTable(FString path,TArray<FShopItem>& Items);
};

//CPP
ASqlDataActor::ASqlDataActor()
{
}

void ASqlDataActor::BeginPlay()
{
	Super::BeginPlay();
}

bool ASqlDataActor::LoadSqlAsTable(FString path, TArray<FShopItem>& Items)
{
	FSQLiteDatabaseConnection db;
	if (!db.Open(*path, nullptr, nullptr))
	{
		return false;
	}

	FString query = FString::Printf(TEXT("SELECT Name,Icon,Price FROM ShopItems"));

	FDataBaseRecordSet* outRecords = nullptr;
	if (!db.Execute(*query, outRecords))
	{
		delete outRecords;
		return false;
	}

	int count = outRecords->GetRecordCount();
	if (count == 0)
	{
		delete outRecords;
		return false;
	}

	int result = -1;
	for (FDataBaseRecordSet::TIterator i(outRecords); i; ++i)
	{
		FShopItem& newItem = Items.AddDefaulted_GetRef();
		for (FDatabaseColumnInfo column : i->GetColumnNames())
		{
			if (column.ColumnName == TEXT("Name"))
			{
				newItem.Name = FText::FromString(i->GetString(*column.ColumnName));

			}
			else if (column.ColumnName == TEXT("Icon"))
			{
				newItem.Icon = LoadObject<UTexture2D>(nullptr, *i->GetString(*column.ColumnName));
			}
			else if (column.ColumnName == TEXT("Price"))
			{
				newItem.Price = i->GetFloat(*column.ColumnName);
			}

		}

	}
	delete outRecords;
	db.Close();
	
	return true;
}

11、工具:

12、提取数据保存到表格

使用插件:

其中有一些函数是无法直接弄出来的,所以要在C++中添加一些东西,将函数暴露在蓝图界面中:

UCLASS(Blueprintable, Category = "DataDriven")
class UMyDataTableFunctionLibrary : public UBlueprintFunctionLibrary

	GENERATED_BODY()
public:
#if WITH_EDITOR
	UFUNCTION(BlueprintCallable, Category = "Editor Scripting | DataTable", DisplayName = "AddRowToDataTable")
		static void AddVolumeToDataTable(UDataTable* DataTable, FName rowName,const FVolumeItem& row);

	UFUNCTION(BlueprintCallable, Category = "Editor Scripting | DataTable", DisplayName = "ClearDataTable")
		static void ClearDataTable(UDataTable* DataTable);

	UFUNCTION(BlueprintCallable, Category = "Editor Scripting | DataTable", DisplayName = "DataTable_RemoveRow")
		static void DataTable_RemoveRow(UDataTable* DataTable, FName rowName);
#endif
;

void UMyDataTableFunctionLibrary::AddVolumeToDataTable(UDataTable* DataTable, FName rowName, const FVolumeItem& row)

	if (DataTable == nullptr)
	
		return;
	
	DataTable->AddRow(rowName, row);
	DataTable->GetOutermost()->MarkPackageDirty();


void UMyDataTableFunctionLibrary::ClearDataTable(UDataTable* DataTable)

	if (DataTable == nullptr)
	
		return;
	
	DataTable->EmptyTable();
	DataTable->GetOutermost()->MarkPackageDirty();


void UMyDataTableFunctionLibrary::DataTable_RemoveRow(UDataTable* DataTable, FName rowName)

	if (DataTable == nullptr)
	
		return;
	
	DataTable->RemoveRow(rowName);
	DataTable->GetOutermost()->MarkPackageDirty();

DataTable->GetOutermost()->MarkPackageDirty();

这个代码就让小星星出现,代表已经对他进行了修改。

13、关于无法编译成功的问题

将启用状态改成false。


DataDriven.Build.cs中添加PublicDependencyModuleNames.Add("DeveloperSettings");

然后就可以成功编译运行了!

在名为Watch代码的文件夹中有部分插件中的代码,可以借鉴参考一下。

14、CSV写入DataTable

原文链接:童年的琴:第6期 UE4 动态读写DataTable数据表

//.h
// Declare General Log Category, header file .h
DECLARE_LOG_CATEGORY_EXTERN(LogUtiliesNode, Log, All);

/**
 * 
 */
UCLASS()
class DATADERIVEDEXCISE_API UDataTableBFLibrary : public UBlueprintFunctionLibrary
{
	GENERATED_BODY()

	/**
	* Empty and fill a Data Table from CSV string.
	* @param	CSVString	The Data that representing the contents of a CSV file.
	* @return	True if the operation succeeds, check the log for errors if it didn't succeed.
	*/
UFUNCTION(BlueprintCallable, DisplayName = "Fill Data Table from CSV String", Category = "DataTable")
	static bool FillDataTableFromCSVString(UDataTable* DataTable, const FString& CSVString);

/**
	* Empty and fill a Data Table from CSV file.
	* @param	CSVFilePath	The file path of the CSV file.
	* @return	True if the operation succeeds, check the log for errors if it didn't succeed.
	*/
UFUNCTION(BlueprintCallable, DisplayName = "Fill Data Table from CSV File", Category = "DataTable")
	static bool FillDataTableFromCSVFile(UDataTable* DataTable, const FString& CSVFilePath);

/**
	* Empty and fill a Data Table from JSON string.
	* @param	JSONString	The Data that representing the contents of a JSON file.
	* @return	True if the operation succeeds, check the log for errors if it didn't succeed.
	*/
UFUNCTION(BlueprintCallable, DisplayName = "Fill Data Table from JSON String", Category = "DataTable")
	static bool FillDataTableFromJSONString(UDataTable* DataTable, const FString& JSONString);

/**
	* Empty and fill a Data Table from JSON file.
	* @param	JSONFilePath	The file path of the JSON file.
	* @return	True if the operation succeeds, check the log for errors if it didn't succeed.
	*/
UFUNCTION(BlueprintCallable, DisplayName = "Fill Data Table from JSON File", Category = "DataTable")
	static bool FillDataTableFromJSONFile(UDataTable* DataTable, const FString& JSONFilePath);

/** Output entire contents of table as CSV string */
UFUNCTION(BlueprintCallable, DisplayName = "Get Table As CSV String", Category = "DataTable")
	static void GetDataTableAsCSVString(UDataTable* DataTable, FString& CSVString);

/** Output entire contents of table as CSV File */
UFUNCTION(BlueprintCallable, DisplayName = "Get Table As CSV File", Category = "DataTable")
	static void GetDataTableAsCSVFile(UDataTable* DataTable, const FString& CSVFilePath);
	
};

//cpp
//Declare General Log Category, source file .cpp
DEFINE_LOG_CATEGORY(LogUtiliesNode);

bool UDataTableBFLibrary::FillDataTableFromCSVString(UDataTable* DataTable, const FString& CSVString)
{
	if (!DataTable || (CSVString.Len() == 0))
	{
		UE_LOG(LogUtiliesNode, Warning, TEXT("FillDataTableFromCSVString -> Can't fill DataTable with CSVString: %."), *CSVString);
		return false;
	}
	// Call bulit-in function
	TArray<FString> Errors = DataTable->CreateTableFromCSVString(CSVString);
	if (Errors.Num())
	{
		// It has some error message
		for (const FString& Error : Errors)
		{
			UE_LOG(LogUtiliesNode, Warning, TEXT("%s"), *Error);
		}
		return false;
	}

	return true;
}

bool UDataTableBFLibrary::FillDataTableFromCSVFile(UDataTable* DataTable, const FString& CSVFilePath)
{
	FString CSVString;
	if (FPlatformFileManager::Get().GetPlatformFile().FileExists(*CSVFilePath))
	{
		// Supports all combination of ANSI/Unicode files and platforms.
		FFileHelper::LoadFileToString(CSVString, *CSVFilePath);
	}
	else
	{
		UE_LOG(LogUtiliesNode, Warning, TEXT("FillDataTableFromCSVFile -> Cannot find CSV file %s"), *CSVFilePath);
		return false;
	}
	return UDataTableBFLibrary::FillDataTableFromCSVString(DataTable, CSVString);
}

bool UDataTableBFLibrary::FillDataTableFromJSONString(UDataTable* DataTable, const FString& JSONString)
{
	if (!DataTable || (JSONString.Len() == 0))
	{
		UE_LOG(LogUtiliesNode, Warning, TEXT("FillDataTableFromJSONString -> Can't fill DataTable with JSONString: %."), *JSONString);
		return false;
	}
	// Call bulit-in function
	TArray<FString> Errors = DataTable->CreateTableFromJSONString(JSONString);

	if (Errors.Num())
	{
		// It has some error message
		for (const FString& Error : Errors)
		{
			UE_LOG(LogUtiliesNode, Warning, TEXT("%s"), *Error);
		}
		return false;
	}

	return true;
}

bool UDataTableBFLibrary::FillDataTableFromJSONFile(UDataTable* DataTable, const FString& JSONFilePath)
{
	FString JSONString;
	if (FPlatformFileManager::Get().GetPlatformFile().FileExists(*JSONFilePath))
	{
		// Supports all combination of ANSI/Unicode files and platforms.
		FFileHelper::LoadFileToString(JSONString, *JSONFilePath);
	}
	else
	{
		UE_LOG(LogUtiliesNode, Warning, TEXT("FillDataTableFromJSONFile -> Cannot find CSV file %s"), *JSONFilePath);
		return false;
	}
	return UDataTableBFLibrary::FillDataTableFromJSONString(DataTable, JSONString);
}

void UDataTableBFLibrary::GetDataTableAsCSVString(UDataTable* DataTable, FString& CSVString)
{
	CSVString = FString();

	if (!DataTable || (DataTable->RowStruct == nullptr))
	{
		UE_LOG(LogTemp, Warning, TEXT("UGenericMiscLibrary::GetTableAsCSV : Missing DataTable or RowStruct !"));
		return;
	}

	// First build array of properties
	TArray<FProperty*> StructProps;
	for (TFieldIterator<FProperty> It(DataTable->RowStruct); It; ++It)
	{
		FProperty* Prop = *It;
		check(Prop != nullptr);
		StructProps.Add(Prop);
	}

	// First row, column titles, taken from properties
	CSVString += TEXT("---");
	for (int32 PropIdx = 0; PropIdx < StructProps.Num(); PropIdx++)
	{
		CSVString += TEXT(",");
		CSVString += StructProps[PropIdx]->GetName();
	}
	CSVString += TEXT("\n");

	// Now iterate over rows
	for (auto RowIt = DataTable->GetRowMap().CreateConstIterator(); RowIt; ++RowIt)
	{
		FName RowName = RowIt.Key();
		CSVString += RowName.ToString();

		uint8* RowData = RowIt.Value();
		for (int32 PropIdx = 0; PropIdx < StructProps.Num(); PropIdx++)
		{
			CSVString += TEXT(",");
			CSVString += DataTableUtils::GetPropertyValueAsString(StructProps[PropIdx], RowData, EDataTableExportFlags::None);
		}
		CSVString += TEXT("\n");
	}
}



void UDataTableBFLibrary::GetDataTableAsCSVFile(UDataTable* DataTable, const FString& CSVFilePath)
{
	FString CSVString;
	UDataTableBFLibrary::GetDataTableAsCSVString(DataTable, CSVString);
	if (CSVString.Len() == 0)
	{
		return;
	}
	FFileHelper::SaveStringToFile(CSVString, *CSVFilePath, FFileHelper::EEncodingOptions::ForceUTF8);
}

蓝图调用:

15、Json读写

UE4包含Json和JsonUtilities这两个模块

原文链接:【UE4】UE4读写Json文件 - Goulandis

void UDataTableBFLibrary::CreateJson()
{
	FString filePath = FPaths::GetProjectFilePath() + TEXT("MysqlConfig/Connect.json");
	FString jsonStr;
	TSharedRef<TJsonWriter<>> jsonWriter = TJsonWriterFactory<>::Create(&jsonStr);
	jsonWriter->WriteObjectStart();
	jsonWriter->WriteValue(TEXT("server"), TEXT("127.0.0.1"));
	jsonWriter->WriteObjectEnd();
	jsonWriter->Close();
	FFileHelper::SaveStringToFile(jsonStr, *filePath);
}

序列化写入:

void AMyActor::Test()
{
	TSharedPtr<FJsonObject> rootObj = MakeShareable(new FJsonObject());
	rootObj->SetStringField("root", "1");
	TArray<TSharedPtr<FJsonValue>> arrValue;
	TSharedPtr<FJsonValueString> tmp = MakeShareable(new FJsonValueString("array"));
	arrValue.Add(tmp);
	rootObj->SetArrayField("array", arrValue);
	FString filePath = FPaths::GameContentDir() + TEXT("MysqlConfig/text.json");
	FString jsonStr;
	TSharedRef<TJsonWriter<TCHAR>> jsonWriter = TJsonWriterFactory<TCHAR>::Create(&jsonStr);
	FJsonSerializer::Serialize(rootObj.ToSharedRef(), jsonWriter);
	FFileHelper::SaveStringToFile(jsonStr, *filePath);
	UE_LOG(LogTemp, Error, TEXT("%s"),*filePath);
}

反序列化读取:

TArray<FName> AMysqlJsonCpp::ReadMysqlConnectConfig()
{
	FString filePath = FPaths::GameContentDir() + TEXT("MysqlConfig/Connect.json");
	if (FPaths::FileExists(filePath))
	{
		FString server;
		FString dbName;
		FString userId;
		FString passwd;
		TArray<FName> connectConfig;
		FString fileStr;
		FFileHelper::LoadFileToString(fileStr, *filePath);
		TSharedPtr<FJsonObject> rootObject = MakeShareable(new FJsonObject());
		TSharedRef<TJsonReader<>> jsonReader = TJsonReaderFactory<>::Create(fileStr);
		if (FJsonSerializer::Deserialize(jsonReader, rootObject))
		{
			server = rootObject->GetStringField("server");
			dbName = rootObject->GetStringField("dbName");
			userId = rootObject->GetStringField("userId");
			passwd = rootObject->GetStringField("passwd");
		}
		connectConfig.Add(FName(*server));
		connectConfig.Add(FName(*dbName));
		connectConfig.Add(FName(*userId));
		connectConfig.Add(FName(*passwd));
	}

	return TArray<FName>();
}
 

源码仓库:张乂卓从头学UE4/UE4Learn_DataDeriven

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/yuchenwuhen/article/details/134376619

智能推荐

sqlmap安装以及运用_kali安装sqlmap-程序员宅基地

文章浏览阅读1.7k次。sqlmap是一个开源的渗透测试工具,它可以自动化检测sql注入漏洞利用sql注入缺陷 接管数据库服务器。_kali安装sqlmap

【曼哈顿距离】第六届蓝桥杯省赛C++ B组 /JAVA A组C组《移动距离》(c++)_移动距离 蓝桥杯 c++-程序员宅基地

文章浏览阅读598次,点赞19次,收藏4次。本题来自第六届蓝桥杯省赛C++ B组 /JAVA A组C组《移动距离》_移动距离 蓝桥杯 c++

zram disksize 设置_use_dedup-程序员宅基地

文章浏览阅读3.4k次。zram disksize 设置小内存项目:1G,2G,3G RAMzram disksize设置.高通:高通的设置比较简单:相关代码:init.qcom.post_boot.shif [ -f /sys/block/zram0/disksize ]; thenif [ -f /sys/block/zram0/use_dedup ]; thenecho 1 > /sys/block/zram0/use_dedupfiif [ $MemTotal -le5242_use_dedup

学画画软件app推荐_在游戏中学习!化学app软件推荐!-程序员宅基地

文章浏览阅读281次。今天中学化学园给大家推荐几款超有趣的教育软件APP,大家可以自行搜索下载,又萌又有趣,在玩乐中还能学到知识!手机要有足够内存哦~~~!下面几款适用于苹果系统~~~1.神奇的化学元素简介:可以高效帮助您记忆有关元素的基本知识。适用对象:初高中学生2.烧杯简介:150多种药剂、300多种神奇的化学反应任你尝试。安全、有趣生动、随时随地做各种化学实验,生动直观,充满乐趣~适用对象:高中学生锂..._化学游戏软件

QMI8658A-EVB 评估板--产品简介_qmi8658a中文资料-程序员宅基地

文章浏览阅读618次。QMA8658A 是一款功能强大的6轴加速度传感器,其内置了3轴加速度计和3轴陀螺仪,能够同时测量三个方向的加速度和角速度。该传感器广泛应用于无人机、机器人、智能手机等领域。为了帮助开发人员快速评估和开发基于QMA8658A的解决方案,我们推出了QMA8658A-EVB全面的评估板。该评估板精心设计,预置了所有必需的硬件接口,兼容I2C和SPI接口,方便与任意MCU处理器进行连接和通信。此外,我们还提供了详细的驱动程序和使用指南,以便开发者能够轻松使用该评估板进行二次开发。4.1 I2C接口。_qmi8658a中文资料

iMeta | 宁波大学附属第一医院崔翰斌团队综述缺血性心脏病相关肠道微生物及菌群代谢物研究进展...-程序员宅基地

文章浏览阅读551次。点击蓝字 关注我们缺血性心脏病相关肠道微生物及菌群代谢物研究进展iMeta主页:http://www.imeta.science综 述●原文链接DOI: https://doi.org/10.1002/imt2.94● 2023年2月26日,宁波大学附属第一医院崔翰斌团队、浙江省动脉粥样硬化疾病精准医学研究重点实验室范勇团队在iMeta在线发表了题为“Microbiota-related ..._与急性心肌梗死有关的微生物

随便推点

渗透测试-安服面试点总结_安服题-程序员宅基地

文章浏览阅读1.5k次。渗透测试-安服面试点总结_安服题

C语言文件操作与调试技巧:编辑、运行和测试你的项目_c语言编辑-程序员宅基地

文章浏览阅读424次。错误类型判断 在C语言中,常见的错误类型包括语法错误、逻辑错误和运行时错误。逻辑错误是指程序的逻辑错误,导致程序的输出不符合预期。运行时错误是指在程序运行过程中发生的错误,例如除以零、访问不存在的内存等。通过本文的介绍,你已经了解了在C语言项目中打开文件、编辑、运行和测试程序的基本方法,以及常见的错误类型判断和调试技巧。同时,持续学习和实践是提高编程技能的关键,希望本文能为你在C语言编程之路上提供帮助和指导。此外,还将探讨常见的错误类型判断和程序测试方法,帮助你提高代码质量和开发效率。_c语言编辑

【代码优化】for-each代替普通的for循环或者while循环_c++中的while循环可有什么替代-程序员宅基地

文章浏览阅读2.8k次。对于集合的遍历首选方法是for-eachfor(Element e :c){ doSomething(e);}这是1.5版本之后的做法;java1.5之前使用的是Iterator迭代器。为了弄清楚为啥比普通的for循环或者whlie循环好,请看一下代码Iterator i=c.iterator();while(i.hasNext()){_c++中的while循环可有什么替代

微信公众号网页静默授权/非静默授权(uniapp版)_微信公众号静默授权-程序员宅基地

文章浏览阅读7.7k次,点赞5次,收藏33次。一、问题为什么要进行网页授权?首先我们进行网页授权的需求是,获取用户信息、最主要是获取openid唯一值,可以用于用户登录、支付等功能,这时候就需要进行网页授权获取用户的信息以及openid。二、静默授权/非静默授权在操作之前可以先提前看看网页授权官方文档静默授权snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid;用来获取进入页面的用户的openid的,并且自动跳转到回调页的。用户感知的就是直接进入了回调页(往往是业务页面)。非静默授权snsapi_user_微信公众号静默授权

A Key Volume Mining Deep Framework for Action Recognition-程序员宅基地

文章浏览阅读235次。A Key Volume Mining Deep Framework for Action Recognition_a key volume mining deep framework for action recognition

python创建窗体_python生成窗口-程序员宅基地

文章浏览阅读3.9k次。广告关闭腾讯云11.11云上盛惠 ,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高返5000元!2、python生成目录树上述 cmd 方式虽然可以生成目录树,但是并不美观,让我们用 python 实现。 2.1 标准库pathlib介绍python有一个标准文件路径处理库 os.path ,从 python3.4 开始,python 又加入了一个标准库 pathlib ,该库..._python创建一个窗口

推荐文章

热门文章

相关标签