【UE4】DataTable - 数据表

本文使用 UE 4.26,引擎自带 ARPG 项目,从代码和编辑器两个方面记录一下 UE4 中的 DataTable,也就是数据表的用法。

一、DataTable 是什么

  DataTable 就是数据表(以下简称 DT),也就是二维的,M 行 N 列的矩阵,如下图所示:

在这里插入图片描述

  是一个 5 行(Row),三列(Col)的数据表。程序可以通过策划配置的数据表找到对应关系做相应的逻辑,对策划很友好。

二、编辑器中使用

2.1 创建一个数据表

  在编辑器中,右键,Miscellaneous -> DataTable 即可创建一个 DT:

在这里插入图片描述

  需要选择 Row (即列)的数据结构,比如选了 GameplayTagTableRow,就会创建一个如下所示的 DT:
在这里插入图片描述
在这里插入图片描述
  新创建的 DT 默认是空的, 点击 Add 按钮可以创建一个默认行,如果 DT 的列结构是代码里的,则默认值在代码中设置;如果 DT 的列结构是资源,则在资源中设置。这个例子中 GameplayTagTableRow 是代码里写的,所以默认值在代码中设置(没有设置默认值,所以 Tag 默认是 None,DevComment 默认是空):

/** Simple struct for a table row in the gameplay tag table and element in the ini list */
USTRUCT()
struct FGameplayTagTableRow : public FTableRowBase
{
    
    
	GENERATED_USTRUCT_BODY()

	/** Tag specified in the table */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=GameplayTag)
	FName Tag;

	/** Developer comment clarifying the usage of a particular tag, not user facing */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=GameplayTag)
	FString DevComment;

	/** Constructors */
	FGameplayTagTableRow() {
    
    }
	FGameplayTagTableRow(FName InTag, const FString& InDevComment = TEXT("")) : Tag(InTag), DevComment(InDevComment) {
    
    }
	GAMEPLAYTAGS_API FGameplayTagTableRow(FGameplayTagTableRow const& Other);

	/** Assignment/Equality operators */
	GAMEPLAYTAGS_API FGameplayTagTableRow& operator=(FGameplayTagTableRow const& Other);
	GAMEPLAYTAGS_API bool operator==(FGameplayTagTableRow const& Other) const;
	GAMEPLAYTAGS_API bool operator!=(FGameplayTagTableRow const& Other) const;
	GAMEPLAYTAGS_API bool operator<(FGameplayTagTableRow const& Other) const;
};

  点击 DT 中的一行,这一行会高亮,且可以在 Row Editor 中修改这一行的值(注意:代表行数的第一列,以及列名称是不能改的)
在这里插入图片描述

2.2 自定义数据表的列

  DT 的列在创建出来之后就固定了,不能随便加一列,删一列,如果需要自定义列的数据表,可以在 Content 里右键,创建一个 “Structure”(即结构):
在这里插入图片描述

  在 Structure 中可以新增变量,调整变量位置(越靠上,DT 中就越靠左),Default Values 里可以设置每一列的默认值(注意:变量和字符串值,都可以是中文):

在这里插入图片描述

扫描二维码关注公众号,回复: 16963489 查看本文章

  然后右键创建一个 DT,使用刚刚创建的自定义列结构,点击 Add,就能看到默认值:

在这里插入图片描述

三、代码中使用

3.1 使用代码创建一个列结构

  在代码中可以仿照 FGameplayTagTableRow 写一个:

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

public:

	FTableRowTest() {
    
    }

	FTableRowTest(bool InLoop, int32 InCurrentCount, int32 InMaxNum, float InLifeTime)
		: bLoop(InLoop)
	    , CurrentCount(InCurrentCount)
		, MaxNum(InMaxNum)
		, LifeTime(InLifeTime)
	{
    
    
	}

	UPROPERTY(EditAnywhere, BlueprintReadOnly, DisplayName = "是否循环")
	bool		bLoop = false;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, DisplayName = "当前数量")
	int64		CurrentCount = 0;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, DisplayName = "最大数量")
	int64		MaxNum = 0;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, DisplayName = "生命周期")
	float		LifeTime = 0.f;
};

  这样就可以在编辑器中创建一个这个列结构的 DT 了(且由于是代码创建的列结构,在 Row Structure 里是跳转不过去的,如果是资源,能直接跳到结构资源):
在这里插入图片描述

3.2 通过代码读/写 DT

  在代码中可以直接通过资源路径和名称加载 DT,比如在 Content/TestForDT 目录下的 “MyTestDT”,可以这样加载:

UDataTable* const TestTable = LoadObject<UDataTable>(nullptr, TEXT("/Game/TestForDT/MyTestDT.MyTestDT"));

  然后就可以通过 void UDataTable::AddRow(FName RowName, const FTableRowBase& RowData) 来新增行了。比如:

TestTable->AddRow(FName("Bob"), FTableRowTest(true, 3, 8, 2.7f));

3.3 CSV、JSON

  DT 创建之后可以右键,导出成 CSV 或者 JSON 文件:
在这里插入图片描述

  编辑器中可以通过 Reimport 通过 CSV 或者 JSON 导入,具体格式试一试就知道了:
在这里插入图片描述

  在代码中(或者蓝图中)也可以直接通过 CSV 或者 JSON 创建一个 DT:

/** 
*	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);

四、注意事项

4.1 复制内容

  选中一行之后,如果想复制其中的内容是分两种情况的:

  1. 如果是第一列,即 Key,那么选中一行之后按 F2 即可
    在这里插入图片描述

  2. 如果是其他列,即 Value,那么选中一行之后在 Row Editor 中可以复制

4.2 排序

  DT 是可以按照某一列的值排序的,但是很扯淡的是:不管是什么类型,都是 TEXT 转成 FString 然后比较,源码如下(在 FDataTableEditor::OnColumnSortModeChanged 函数中):

if (InSortMode == EColumnSortMode::Ascending)
{
    
    
	VisibleRows.Sort([ColumnIndex](
		const FDataTableEditorRowListViewDataPtr& first, 
		const FDataTableEditorRowListViewDataPtr& second)
	{
    
    
		// 返回值:大于 0 表示 A > B; 0 表示相等; 小于 0 表示 A < B		
		int32 Result = (first->CellData[ColumnIndex].ToString()).Compare(second->CellData[ColumnIndex].ToString());
		
		if (!Result)
		{
    
    
			return first->RowNum < second->RowNum;
		}

		return Result < 0;
	});
}

  而 FString 的比较,是 字典排序,所以 99 > 963

在这里插入图片描述

  以及 2.87 > 11.09
在这里插入图片描述

  我自己的改法是直接把引擎改了,如果是 Numeric,用数值类型排序,而不是字符串:

VisibleRows.Sort([ColumnIndex](
	const FDataTableEditorRowListViewDataPtr& first, 
	const FDataTableEditorRowListViewDataPtr& second)
{
    
    					
	int32 Result = (first->CellData[ColumnIndex].ToString()).Compare(second->CellData[ColumnIndex].ToString());

	FString const FirstColumnStr = first->CellData[ColumnIndex].ToString();
    FString const SecondColumnStr = second->CellData[ColumnIndex].ToString();
    bool const bNumeric = FCString::IsNumeric(*FirstColumnStr) && FCString::IsNumeric(*SecondColumnStr);
    if (bNumeric)
    {
    
    
    	double const FirstNum = FCString::Atod(*FirstColumnStr);
        double const SecondNum = FCString::Atod(*SecondColumnStr);
        Result = (FirstNum > SecondNum) ? 1 : ((FirstNum < SecondNum) ? -1 : 0);
    }
				
	if (!Result)
	{
    
    
		return first->RowNum < second->RowNum;
	}

	return Result < 0;
});

五、参考资料

  1. UDataTable - Unrael Engine Document

猜你喜欢

转载自blog.csdn.net/Bob__yuan/article/details/117457482