DataTable优化方案

前言

image-20250114182453643

image-20250114182516281

image-20250114182535643

目标

  • 自动加载
  • 更简单的方式查询数据
  • 需要支持cpp和蓝图

CPP

创建一个Setting类存储所有需要加载的表格

image-20250114181143210

用一个引擎子系统加载和保存所有表格

image-20250114181232265

image-20250114181434501

核心其实是用数据类型的哈希作为键来保存到Map中,这样就可以用模板的方式查询

image-20250114181547907

这里是包到了一个函数库中去,其实也可以直接放外面

蓝图

蓝图需要实现一个通过数据查询的方法麻烦很多

我们需要实现一个运行时的泛型函数,一个编辑器模式下的K2Node(类似GetDataTableRow),因为需要动态的修改数据类型和Row的Pin,所有还需要一个Factory来定义这两个Pin

蓝图函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
UFUNCTION(BlueprintCallable,CustomThunk,meta = (CustomStructureParam = "OutRow", BlueprintInternalUseOnly="true"))
static bool TryGetTableRowData(UPARAM(meta=(GetOptions = "GetAllTableStructs"))FName StructName,FName RowName, FTableRowBase& OutRow){return false;};

static bool Generic_TryGetTableRowData(FName StructName, FName RowName, void* OutRowPtr);
DECLARE_FUNCTION(execTryGetTableRowData)
{
P_GET_PROPERTY(FNameProperty, StructName);
P_GET_PROPERTY(FNameProperty, RowName);

Stack.MostRecentProperty = nullptr;
Stack.StepCompiledIn<FStructProperty>(NULL);
void* OutRowPtr = Stack.MostRecentPropertyAddress;

P_FINISH;

bool bSuccess = false;

FStructProperty* StructProp = CastField<FStructProperty>(Stack.MostRecentProperty);

UDataTable* Table = UDataTableUtility::FindTableByStructName(StructName);
if (!Table)
{
FBlueprintExceptionInfo ExceptionInfo(
EBlueprintExceptionType::AccessViolation,
NSLOCTEXT("TryGetTableRowData", "MissingStructInput", "Failed to resolve the table input. Be sure the DataTable is valid.")
);
FBlueprintCoreDelegates::ThrowScriptException(P_THIS, Stack, ExceptionInfo);
}
else if(StructProp && OutRowPtr)
{
UScriptStruct* OutputType = StructProp->Struct;
const UScriptStruct* TableType = Table->GetRowStruct();

const bool bCompatible = (OutputType == TableType) ||
(OutputType->IsChildOf(TableType) && FStructUtils::TheSameLayout(OutputType, TableType));
if (bCompatible)
{
P_NATIVE_BEGIN;
bSuccess = Generic_TryGetTableRowData(StructName, RowName, OutRowPtr);
P_NATIVE_END;
}
else
{
FBlueprintExceptionInfo ExceptionInfo(
EBlueprintExceptionType::AccessViolation,
NSLOCTEXT("TryGetTableRowData", "IncompatibleProperty", "Incompatible output parameter; the data table's type is not the same as the return type.")
);
FBlueprintCoreDelegates::ThrowScriptException(P_THIS, Stack, ExceptionInfo);
}
}
else
{
FBlueprintExceptionInfo ExceptionInfo(
EBlueprintExceptionType::AccessViolation,
NSLOCTEXT("TryGetTableRowData", "MissingOutputProperty", "Failed to resolve the output parameter for GetDataTableRow.")
);
FBlueprintCoreDelegates::ThrowScriptException(P_THIS, Stack, ExceptionInfo);
}
*(bool*)RESULT_PARAM = bSuccess;
}

这个函数没有暴露给蓝图,即使暴露了也是不够的,因为数据结构是可以通过办法来制作下拉表,但是这个RowName没办法,所以只能靠K2Node

K2Node

可以先把K2Node_GetDataTableRow复制过来,因为区别就是他传递的是一个UObject,我们改成FName即可

,还需要一个工具函数通过Name去查询DataTable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
UDataTable* UDataTableUtility::FindTableByStructName(FName Name)
{


//删除所有空格
FString StrName = Name.ToString();
RemoveEmpty(StrName);
if (auto Setting = GetMutableDefault<UDataTableUtilitySettings>())
{
for (auto& Table : Setting->Tables)
{
if (!Table || Table.Get()->GetRowStruct() == nullptr)
{
continue;
}
FString StructName = Table.Get()->GetRowStruct()->GetName();
RemoveEmpty(StructName);
// FString DisplayName = Table.Get()->GetRowStruct()->GetName().ToString();
// RemoveEmpty(DisplayName);
if (Table.IsValid() && StructName == StrName /*|| DisplayName == StrName)*/)
{
return Table.Get();
}
}
}
return nullptr;
}

Factory

这个对象的目的就是修改2个pin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

TSharedPtr<SGraphPin> FDataTableUtilityPinFactory::CreatePin(UEdGraphPin* InPin) const
{
if (InPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Name )
{
UObject* Outer = InPin->GetOuter();
const UEdGraphPin* DataTablePin = nullptr;
if (Outer->IsA(UK2Node_GetTableData::StaticClass()))
{
//处理行号
if (InPin->PinName == GetTableData::RowNamePinName)
{
const UK2Node_GetTableData* GetDataTableRowNode = CastChecked<UK2Node_GetTableData>(Outer);
DataTablePin = GetDataTableRowNode->GetDataTablePin();
auto FilterPin = GetDataTableRowNode->GetFilterPin();
if (DataTablePin)
{
if (!DataTablePin->DefaultValue .IsEmpty() && DataTablePin->LinkedTo.Num() == 0)
{
if (auto DataTable = UDataTableUtility::FindTableByStructName(FName(DataTablePin->DefaultValue)))
{
TArray<FName> Names = DataTable->GetRowNames();
if (FilterPin)
{
// Filter the names
Names = Names.FilterByPredicate([FilterPin](FName Name)
{
return Name.ToString().Contains(FilterPin->DefaultValue);
});
}
TArray<TSharedPtr<FName>> RowNames;
for (auto Name:Names)
{
/** Create a simple array of the row names */
TSharedPtr<FName> RowNameItem = MakeShareable(new FName(Name));
RowNames.Add(RowNameItem);
}
return SNew(SGraphPinNameList, InPin, RowNames);
}
}
}
}
else if (InPin->PinName == GetTableData::DataTablePinName)
{
const UK2Node_GetTableData* GetDataTableRowNode = CastChecked<UK2Node_GetTableData>(Outer);
DataTablePin = GetDataTableRowNode->GetDataTablePin();
if (DataTablePin)
{
auto Names = UDataTableUtility::GetAllTableStructs();

TArray<TSharedPtr<FName>> RowNames;
for (auto Name:Names)
{
/** Create a simple array of the row names */
TSharedPtr<FName> RowNameItem = MakeShareable(new FName(Name));
RowNames.Add(RowNameItem);
}
return SNew(SGraphPinNameList, InPin, RowNames);
}
}
}
}

return nullptr;
}

因为加加一个名字筛选的Filter,不然遇到表格行数特别多的就炸了, 算是模拟一个搜索功能,有需要的话也可以加一个包含或者非包含2种条件