[UE C++] Data Table的使用

[UE C++] Data Table

1. 创建结构体

USTRUCT(BlueprintType)
struct  FTableStruct :public FTableRowBase
{
	GENERATED_USTRUCT_BODY()

	UPROPERTY(EditAnywhere)
	FString TestString;

	UPROPERTY(EditAnywhere)
	float Damage;

};

结构体创建完成之后,回到Editor创建Data Table,比较简单,此处省略

UStruct 的用法可以参考这一篇文章: UE C++ Struct
DataTable内容

2. 加载Data Table

首先声明DataTable变量

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "DataTable")
UDataTable* TestDataTable;

2.1 ConstructorHelpers::FObjectFinder

注意必须在构造函数中实现

static ConstructorHelpers::FObjectFinder<UDataTable> TableType(TEXT("DataTable'/Game/Data/Da_Test.Da_Test'"));
if (TableType.Succeeded())
{
	TestDataTable = TableType.Object;
}

资源的 Path 可以通过,右键点击资源 Copy Reference 获得
在这里插入图片描述

2.2 StaticLoadObject

TestDataTable = Cast<UDataTable>(StaticLoadObject(UDataTable::StaticClass(), NULL, TEXT("DataTable'/Game/Data/Da_Test.Da_Test'")));

2.3 LoadObject

TestDataTable = LoadObject<UDataTable>(this, TEXT("DataTable'/Game/Data/Da_Test.Da_Test'"));

观察LoadObject代码,发现其实调用的就是StaticLoadObject

template< class T > 
inline T* LoadObject( UObject* Outer, const TCHAR* Name, const TCHAR* Filename=nullptr, uint32 LoadFlags=LOAD_None, UPackageMap* Sandbox=nullptr )
{
	return (T*)StaticLoadObject( T::StaticClass(), Outer, Name, Filename, LoadFlags, Sandbox );
}

DataTable也是一种资源,有很多种加载方式,资源的加载方式可以看这篇文章UE 资源加载

3. 读取数据

3.1 FindRow

if (TestDataTable)
{
    for (FName RowName : TestDataTable->GetRowNames())
	{
		FTableStruct* DataTableRowInfo = TestDataTable->FindRow<FTableStruct>(RowName, TEXT("TestDataTableContext"));
        //use row
	    GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Yellow, \
        FString::Printf(TEXT("TestString: %s \n Damage: %f"),*DataTableRowInfo->TestString, DataTableRowInfo->Damage));
	}
}

3.2 GetAllRows

if (TestDataTable)
{
    TArray<FTableStruct*> DataTableRowInfos;
    TestDataTable->GetAllRows<FTableStruct>(TEXT("TestDataTableContext"), DataTableRowInfos);
    for (FTableStruct* DataTableRowInfo : DataTableRowInfos)
    {
        //use row
        GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Yellow, \
        FString::Printf(TEXT("TestString: %s \n Damage: %f"), *DataTableRowInfo->TestString, DataTableRowInfo->Damage));
    }
}

3.3 GetRowMap

if (TestDataTable)
{
	for (auto iter : TestDataTable->GetRowMap())
	{
		FName RowName = iter.Key;
		FTableStruct* DataTableRowInfo = (FTableStruct*)iter.Value;
        //use row
		GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Yellow, \
			FString::Printf(TEXT("TestString: %s \n Damage: %f"), *DataTableRowInfo->TestString, DataTableRowInfo->Damage));
	}
}

4. 写入数据

AddRow
可以RunTime使用

if (TestDataTable)
{
	FTableStruct* DataTableRowInfo = new FTableStruct();
	DataTableRowInfo->Damage = 30.f;
	DataTableRowInfo->TestString = FString("Three");
	TestDataTable->AddRow(FName("Third"), *DataTableRowInfo);
}

在这里插入图片描述

4. CSV

4.1 CSV To DataTable

CSV内容如下

---,TestString,Damage
1,"OneOne",100
2,"TwoTwo",200
3,"ThreeThree",300

UE 会自动将第一列作为Row Name,第一列的名字可以是 --- ,也可以是 - ,或者其他中英文字符,甚至可以为空

,TestString,Damage
1,"OneOne",100
2,"TwoTwo",200
3,"ThreeThree",300

4.1.2填充现有的DataTable

声明函数 CallInEditor

UFUNCTION(CallInEditor)
void FillDataTable();

定义函数

void FillDataTable()
{
	UDataTable* TestDataTable = LoadObject<UDataTable>(this, TEXT("DataTable'/Game/Data/Test.Test'"));
	if (TestDataTable)
	{
		FString CSVPath{ FPaths::ProjectDir() + "DataImport/TestCSV.csv" };
		if (FPlatformFileManager::Get().GetPlatformFile().FileExists(*CSVPath))
		{
			UDataTableFunctionLibrary::FillDataTableFromCSVFile(TestDataTable, CSVPath);
		}
		else
		{
			UE_LOG(LogTemp, Warning, TEXT("CSV Failed"));
		}
	}
}

注意事项:

  • 确保要填充的DataTable文件为空,即里面不含内容,不然Editor会崩溃
  • 这个方法只能在Editor中使用,无法在Runtime使用 (使用Editor Utility),原因是FillDataTableFromCSVFile()为Editor Only
  • #include "Kismet/DataTableFunctionLibrary.h"
  • 如果你将Editor Only的函数声明在了Runtime的Actor中打包会产生问题

4.1.3生成新的DataTable

这个方法可以在Runtime使用

FString CSVPath{ FPaths::ProjectDir() + "DataImport/TestCSV.csv" };

if (FPlatformFileManager::Get().GetPlatformFile().FileExists(*CSVPath))
{
	//加载CSV文件
	FString CSVData;
	FFileHelper::LoadFileToString(CSVData, *CSVPath);

	//创建package
	FString DataTableName = "DT_CreateTest";
	FString PackagePath = "/Game/Data/";
	PackagePath += DataTableName;
	UPackage* MyPackage = CreatePackage(nullptr, *PackagePath);

	//创建DataTable
	UDataTable* DT_CreateTest = NewObject<UDataTable>(MyPackage, *DataTableName, EObjectFlags::RF_Public | RF_Standalone);

	//初始化DataTable
	DT_CreateTest->RowStruct = FTableStruct::StaticStruct();
	DT_CreateTest->CreateTableFromCSVString(CSVData);

	//结束创建
	FAssetRegistryModule::AssetCreated(DT_CreateTest);
	DT_CreateTest->GetOutermost()->MarkPackageDirty();

	//打印信息
	if (DT_CreateTest)
	{
		for (FName RowName : DT_CreateTest->GetRowNames())
		{
			FTableStruct* DataTableRowInfo = DT_CreateTest->FindRow<FTableStruct>(RowName, TEXT("TestDataTableContext"));
			UE_LOG(LogTemp, Warning, TEXT("%s : %s \t Damage: %f"), \
					* RowName.ToString(), *DataTableRowInfo->TestString, DataTableRowInfo->Damage);
		}
	}
}
else
{
	UE_LOG(LogTemp, Warning, TEXT("CSV Failed"));
}

在这里插入图片描述
在这里插入图片描述

如果给UDataTable* 开始时就指向一个Asset实例,就可以达到 Runtime 修改DataTable的效果了(和4.1.2相比较)

FString CSVPath{ FPaths::ProjectDir() + "DataImport/TestCSV.csv" };

if (FPlatformFileManager::Get().GetPlatformFile().FileExists(*CSVPath))
{
	FString CSVData;
	FFileHelper::LoadFileToString(CSVData, *CSVPath);

	UDataTable* TestDataTable = LoadObject<UDataTable>(this, TEXT("DataTable'/Game/Data/Test.Test'"));
	TestDataTable->CreateTableFromCSVString(CSVData);
}
else
{
	UE_LOG(LogTemp, Warning, TEXT("CSV Failed"));
}

注意事项: 这种修改方式不要求填充的DataTable为空,会直接覆盖掉原来的内容

4.2 DataTable To CSV

Editor Only

UDataTable* TestDataTable = LoadObject<UDataTable>(this, TEXT("DataTable'/Game/Data/Da_Test.Da_Test'"));
if (TestDataTable)
{
	FString CSVString = TestDataTable->GetTableAsCSV();
	FString CSVPath{ FPaths::ProjectDir() + "DataImport/DataTableToCSV.csv" };
	FFileHelper::SaveStringToFile(CSVString, *CSVPath, FFileHelper::EEncodingOptions::ForceUTF8);
}

DataTable内容
CSV内容

注意事项:

  • GetTableAsCSV()是Editor函数,但是如果你在Runtime调用了此函数也是可以 生效 的,但是 打包 会产生问题。所以可以利用这一特点,较为简单的为游戏在运行时生成大量的CSV文件来使用,然后打包阶段去除此函数
  • 如果想要在Runtime真正实现DataTable To CSV的效果,可以参考这篇文章 动态读写DataTable

5. JSON

JSON的操作和CSV十分的相似,就是相应的API发生了变化,这里就不重复介绍了

总结

  • CSV/JSON To DataTable 的Runtime方法很简单,UE提供了2种Runtime可供访问的API
    /** 
     *	Create table from CSV style comma-separated string. 
     *	RowStruct must be defined before calling this function. 
     *	@return	Set of problems encountered while processing input
     */
    ENGINE_API TArray<FString> CreateTableFromCSVString(const FString& InString);
    
    /** 
    *	Create table from JSON style string. 
    *	RowStruct must be defined before calling this function. 
    *	@return	Set of problems encountered while processing input
    */
    ENGINE_API TArray<FString> CreateTableFromJSONString(const FString& InString);
    
    再配合FFileHelper::LoadFileToString就可以完成转化
  • DataTable To CSV/JSON的Runtime方法较为复杂,可以参考如下文章:
    DataTable to CSV
    DataTable to JSON
  • 引擎对于DataTable的操作大多数局限在Editor中,所以在Editor中可以很方便的转化DataTable

参考链接

DataTable读写

DataTable排序

猜你喜欢

转载自blog.csdn.net/qq_52179126/article/details/129800615