赞
踩
本文使用 UE 4.26,引擎自带 ARPG 项目,从代码和编辑器两个方面记录一下 UE4 中的 DataTable,也就是数据表的用法。
DataTable 就是数据表(以下简称 DT),也就是二维的,M 行 N 列的矩阵,如下图所示:
是一个 5 行(Row),三列(Col)的数据表。程序可以通过策划配置的数据表找到对应关系做相应的逻辑,对策划很友好。
在编辑器中,右键,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 中修改这一行的值(注意:代表行数的第一列,以及列名称是不能改的)
DT 的列在创建出来之后就固定了,不能随便加一列,删一列,如果需要自定义列的数据表,可以在 Content 里右键,创建一个 “Structure”(即结构):
在 Structure 中可以新增变量,调整变量位置(越靠上,DT 中就越靠左),Default Values
里可以设置每一列的默认值(注意:变量和字符串值,都可以是中文):
然后右键创建一个 DT,使用刚刚创建的自定义列结构,点击 Add,就能看到默认值:
在代码中可以仿照 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 里是跳转不过去的,如果是资源,能直接跳到结构资源):
在代码中可以直接通过资源路径和名称加载 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));
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);
选中一行之后,如果想复制其中的内容是分两种情况的:
如果是第一列,即 Key,那么选中一行之后按 F2
即可
如果是其他列,即 Value,那么选中一行之后在 Row Editor 中可以复制
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; });
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。