UE4 源代码改写过程和应用实例
1. Overview
本教程以自定义函数GetTableAsJsonString()实现过程为例,旨在讲解如何将UE4源码中的部分源代码改写为自定义函数代码,同时也是为了补充上一期(第6期 UE4 动态读写DataTable数据表)的缺漏。
2. Introduction
虽然虚幻引擎功能强大,但也并非想要的功能都有。某些情况下,虽然在虚幻引擎源代码中已经存在你想要的功能,但是这些函数代码无法直接调用,常常也会遇到源代码中部分功能被WITH_EDITOR宏包裹,以至于这些函数无法在打包后调用。所以常常需要修改(改写)引擎源代码,它不仅可以弥补引擎内置功能的不足,而且相较于自己编写同样功能的代码的过程,耗时耗力、产生更多隐藏bug等问题;直接改写源代码工作量小,出错率相对低。修改引擎源代码,同时也是一种编程水平的体现,反映个人对源代码的理解程度。
上一期讲述了动态读写UE4 DataTable数据表的实现方法,其中GetTableAsCSVString()就是仿照源代码UDataTable::GetTableAsString()函数,可以轻而易举的实现该函数。但是上一期内容还缺失了一个蓝图函数GetTableAsJsonString(),完成将DataTable导出成Json字符串功能。GetTableAsJsonFile()只需要在获得DataTable相应的JsonString后,将其保存为Json文件即可。本期内容以实现自定义蓝图函数GetTableAsJsonString()为例,讲解如何将ue4引擎源代码中的功能化为己用。
#if WITH_EDITOR
ENGINE_API virtual void CleanBeforeStructChange();
ENGINE_API virtual void RestoreAfterStructChange();
/** Output entire contents of table as a string */
ENGINE_API FString GetTableAsString(const EDataTableExportFlags InDTExportFlags = EDataTableExportFlags::None) const;
/** Output entire contents of table as CSV */
ENGINE_API FString GetTableAsCSV(const EDataTableExportFlags InDTExportFlags = EDataTableExportFlags::None) const;
/** Output entire contents of table as JSON */
ENGINE_API FString GetTableAsJSON(const EDataTableExportFlags InDTExportFlags = EDataTableExportFlags::None) const;
/** Output entire contents of table as JSON */
ENGINE_API bool WriteTableAsJSON(const TSharedRef< TJsonWriter<TCHAR, TPrettyJsonPrintPolicy<TCHAR> > >& JsonWriter, const EDataTableExportFlags InDTExportFlags = EDataTableExportFlags::None) const;
/** Output entire contents of table as a JSON Object*/
ENGINE_API bool WriteTableAsJSONObject(const TSharedRef< TJsonWriter<TCHAR, TPrettyJsonPrintPolicy<TCHAR> > >& JsonWriter, const EDataTableExportFlags InDTExportFlags = EDataTableExportFlags::None) const;
/** Output the fields from a particular row (use RowMap to get RowData) to an existing JsonWriter */
ENGINE_API bool WriteRowAsJSON(const TSharedRef< TJsonWriter<TCHAR, TPrettyJsonPrintPolicy<TCHAR> > >& JsonWriter, const void* RowData, const EDataTableExportFlags InDTExportFlags = EDataTableExportFlags::None) const;
/** Copies all the import options from another table, this does not copy row dawta */
ENGINE_API bool CopyImportOptions(UDataTable* SourceTable);
#endif
首先,如上图所示,在UE4源代码UDataTable类中包含了GetTableAsJSON()函数,已经可以将DataTable数据表导出成Json字符串,但是该函数被WITH_EDITOR宏包裹着,并不能在项目打包之后使用。
FString UDataTable::GetTableAsJSON(const EDataTableExportFlags InDTExportFlags) const
{
FString Result;
if (!FDataTableExporterJSON(InDTExportFlags, Result).WriteTable(*this))
{
Result = TEXT("Missing RowStruct!\n");
}
return Result;
}
继续查看UDataTable::GetTableAsJSON()函数体,可以看出,在构造FDataTableExporterJSON类的对象之后,调用FDataTableExporterJSON类的成员函数WriteTable(),从而获得DataTable导出的Json字符串;
#if WITH_EDITOR
class FDataTableExporterJSON
{
public:
typedef TJsonWriter<TCHAR, TPrettyJsonPrintPolicy<TCHAR>> FDataTableJsonWriter;
FDataTableExporterJSON(const EDataTableExportFlags InDTExportFlags, FString& OutExportText);
FDataTableExporterJSON(const EDataTableExportFlags InDTExportFlags, TSharedRef<FDataTableJsonWriter> InJsonWriter);
~FDataTableExporterJSON();
/** Writes the data table out as an array of objects */
bool WriteTable(const UDataTable& InDataTable);
/** Writes the data table out as a named object with each row being a sub value on that object */
bool WriteTableAsObject(const UDataTable& InDataTable);
/** Writes out a single row */
bool WriteRow(const UScriptStruct* InRowStruct, const void* InRowData, const FString* FieldToSkip = nullptr);
/** Writes the contents of a single row */
bool WriteStruct(const UScriptStruct* InStruct, const void* InStructData, const FString* FieldToSkip = nullptr);
private:
bool WriteStructEntry(const void* InRowData, const UProperty* InProperty, const void* InPropertyData);
bool WriteContainerEntry(const UProperty* InProperty, const void* InPropertyData, const FString* InIdentifier = nullptr);
EDataTableExportFlags DTExportFlags;
TSharedRef<FDataTableJsonWriter> JsonWriter;
bool bJsonWriterNeedsClose;
};
#endif // WITH_EDITOR
进一步查看FDataTableExporterJSON类,该类所有代码也同样被WITH_EDITOR宏包裹着,从而可以得出结论,并不能简单的UDataTable::GetTableAsJSON()函数体代码放在WITH_EDITOR宏外调用,而是应该改写源代码中FDataTableExporterJSON类的成员函数WriteTable()。
3. Source Code
以下为改写后的源代码
.h文件,如下所示
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "DataTableUtils.h" // enum class EDataTableExportFlags : uint8
#include "PrettyJsonPrintPolicy.h" // TPrettyJsonPrintPolicy<TCHAR>
#include "DataTableLibrary.generated.h"
// Forward declarations
class UDataTable;
/** Based on FDataTableExporterJSON::FDataTableJsonWriter */
typedef TJsonWriter<TCHAR, TPrettyJsonPrintPolicy<TCHAR>> FDataTableJsonWriter;
UCLASS()
class FILESHELPER_API UDataTableLibrary : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public://
/** Output entire contents of table as JSON string */
UFUNCTION(BlueprintCallable, DisplayName = "Get Table As JSON String", Category = "DataTable")
static void GetDataTableAsJSONString(UDataTable* DataTable, FString& JSONString);
public: // Based on FDataTableExporterJSON
/** Writes the data table out as an array of objects */
static bool WriteTable(TSharedRef<FDataTableJsonWriter> JsonWriter, const UDataTable& InDataTable);
/** Writes out a single row */
static bool WriteRow(TSharedRef<FDataTableJsonWriter> JsonWriter, const UScriptStruct* InRowStruct, const void* InRowData, const FString* FieldToSkip = nullptr);
/** Writes the contents of a single row */
static bool WriteStruct(TSharedRef<FDataTableJsonWriter> JsonWriter, const UScriptStruct* InStruct, const void* InStructData, const FString* FieldToSkip = nullptr, const EDataTableExportFlags DTExportFlags = EDataTableExportFlags::None);
/** Writes the contents of a struct entry */
static bool WriteStructEntry(TSharedRef<FDataTableJsonWriter> JsonWriter, const void* InRowData, const UProperty* InProperty, const void* InPropertyData, const EDataTableExportFlags DTExportFlags = EDataTableExportFlags::None);
static bool WriteContainerEntry(TSharedRef<FDataTableJsonWriter> JsonWriter, const UProperty* InProperty, const void* InPropertyData, const FString* InIdentifier = nullptr, const EDataTableExportFlags DTExportFlags = EDataTableExportFlags::None);
};
.cpp文件,如下所示
// Fill out your copyright notice in the Description page of Project Settings.
#include "DataTableLibrary.h"
#include "Engine/DataTable.h"
#include "DataTableUtils.h"
#include "JsonWriter.h"
namespace DataTableAlgorithm
{
/** Returns what string is used as the key/name field for a data table */
FString GetKeyFieldName(const UDataTable& InDataTable);
FString GetKeyFieldName(const UDataTable& InDataTable)
{
FString ExplicitString = InDataTable.ImportKeyField;
if (ExplicitString.IsEmpty())
{
return TEXT("Name");
}
else
{
return ExplicitString;
}
}
}// end namespace
/** Based on DataTableJSON.cpp -> WriteJSONObjectStartWithOptionalIdentifier() */
void WriteJSONObjectStartWithOptionalIdentifier(FDataTableJsonWriter InJsonWriter, const FString* InIdentifier)
{
if (InIdentifier)
{
InJsonWriter.WriteObjectStart(*InIdentifier);
}
else
{
InJsonWriter.WriteObjectStart();
}
}
/** Based on DataTableJSON.cpp -> WriteJSONValueWithOptionalIdentifier() */
template <typename ValueType>
void WriteJSONValueWithOptionalIdentifier(FDataTableJsonWriter& InJsonWriter, const FString* InIdentifier, const ValueType InValue)
{
if (InIdentifier)
{
InJsonWriter.WriteValue(*InIdentifier, InValue);
}
else
{
InJsonWriter.WriteValue(InValue);
}
}
void UDataTableLibrary::GetDataTableAsJSONString(UDataTable* DataTable, FString& JSONString)
{
// const EDataTableExportFlags InDTExportFlags = EDataTableExportFlags::None;
// Construct JsonWriter instance
TSharedRef<FDataTableJsonWriter> JsonWriter = TJsonWriterFactory<TCHAR, TPrettyJsonPrintPolicy<TCHAR>>::Create(&JSONString);
// Call function to Write DataTable
if (!UDataTableLibrary::WriteTable(JsonWriter, *DataTable))
{
JSONString = TEXT("Missing RowStruct!\n");
}
// Close JsonWriter, assign json string to JSONString variable, equal to FDataTableExporterJSON::~FDataTableExporterJSON()
JsonWriter->Close();
}
bool UDataTableLibrary::WriteTable(TSharedRef<FDataTableJsonWriter> JsonWriter, const UDataTable& InDataTable)
{
if (!InDataTable.RowStruct)
{
return false;
}
FString KeyField = DataTableAlgorithm::GetKeyFieldName(InDataTable);
JsonWriter->WriteArrayStart();
// Iterate over rows
for (auto RowIt = InDataTable.GetRowMap().CreateConstIterator(); RowIt; ++RowIt)
{
JsonWriter->WriteObjectStart();
{
// RowName
const FName RowName = RowIt.Key();
JsonWriter->WriteValue(KeyField, RowName.ToString());
// Now the values
uint8* RowData = RowIt.Value();
UDataTableLibrary::WriteRow(JsonWriter, InDataTable.RowStruct, RowData, &KeyField);
}
JsonWriter->WriteObjectEnd();
}
JsonWriter->WriteArrayEnd();
return true;
}
bool UDataTableLibrary::WriteRow(TSharedRef<FDataTableJsonWriter> JsonWriter, const UScriptStruct* InRowStruct, const void* InRowData, const FString* FieldToSkip /*= nullptr*/)
{
if (!InRowStruct)
{
return false;
}
return UDataTableLibrary::WriteStruct(JsonWriter, InRowStruct, InRowData, FieldToSkip);
}
bool UDataTableLibrary::WriteStruct(TSharedRef<FDataTableJsonWriter> JsonWriter, const UScriptStruct* InStruct, const void* InStructData, const FString* FieldToSkip /*= nullptr*/, const EDataTableExportFlags DTExportFlags /*= EDataTableExportFlags::None*/)
{
for (TFieldIterator<const UProperty> It(InStruct); It; ++It)
{
const UProperty* BaseProp = *It;
check(BaseProp);
const FString Identifier = DataTableUtils::GetPropertyExportName(BaseProp, DTExportFlags);
if (FieldToSkip && *FieldToSkip == Identifier)
{
// Skip this field
continue;
}
if (BaseProp->ArrayDim == 1)
{
const void* Data = BaseProp->ContainerPtrToValuePtr<void>(InStructData, 0);
UDataTableLibrary::WriteStructEntry(JsonWriter, InStructData, BaseProp, Data);
}
else
{
JsonWriter->WriteArrayStart(Identifier);
for (int32 ArrayEntryIndex = 0; ArrayEntryIndex < BaseProp->ArrayDim; ++ArrayEntryIndex)
{
const void* Data = BaseProp->ContainerPtrToValuePtr<void>(InStructData, ArrayEntryIndex);
UDataTableLibrary::WriteContainerEntry(JsonWriter, BaseProp, Data);
}
JsonWriter->WriteArrayEnd();
}
}
return true;
}
bool UDataTableLibrary::WriteStructEntry(TSharedRef<FDataTableJsonWriter> JsonWriter, const void* InRowData, const UProperty* InProperty, const void* InPropertyData, const EDataTableExportFlags DTExportFlags /*= EDataTableExportFlags::None*/)
{
const FString Identifier = DataTableUtils::GetPropertyExportName(InProperty, DTExportFlags);
if (const UEnumProperty* EnumProp = Cast<const UEnumProperty>(InProperty))
{
const FString PropertyValue = DataTableUtils::GetPropertyValueAsString(EnumProp, (uint8*)InRowData, DTExportFlags);
JsonWriter->WriteValue(Identifier, PropertyValue);
}
else if (const UNumericProperty* NumProp = Cast<const UNumericProperty>(InProperty))
{
if (NumProp->IsEnum())
{
const FString PropertyValue = DataTableUtils::GetPropertyValueAsString(InProperty, (uint8*)InRowData, DTExportFlags);
JsonWriter->WriteValue(Identifier, PropertyValue);
}
else if (NumProp->IsInteger())
{
const int64 PropertyValue = NumProp->GetSignedIntPropertyValue(InPropertyData);
JsonWriter->WriteValue(Identifier, PropertyValue);
}
else
{
const double PropertyValue = NumProp->GetFloatingPointPropertyValue(InPropertyData);
JsonWriter->WriteValue(Identifier, PropertyValue);
}
}
else if (const UBoolProperty* BoolProp = Cast<const UBoolProperty>(InProperty))
{
const bool PropertyValue = BoolProp->GetPropertyValue(InPropertyData);
JsonWriter->WriteValue(Identifier, PropertyValue);
}
else if (const UArrayProperty* ArrayProp = Cast<const UArrayProperty>(InProperty))
{
JsonWriter->WriteArrayStart(Identifier);
FScriptArrayHelper ArrayHelper(ArrayProp, InPropertyData);
for (int32 ArrayEntryIndex = 0; ArrayEntryIndex < ArrayHelper.Num(); ++ArrayEntryIndex)
{
const uint8* ArrayEntryData = ArrayHelper.GetRawPtr(ArrayEntryIndex);
UDataTableLibrary::WriteContainerEntry(JsonWriter, ArrayProp->Inner, ArrayEntryData);
}
JsonWriter->WriteArrayEnd();
}
else if (const USetProperty* SetProp = Cast<const USetProperty>(InProperty))
{
JsonWriter->WriteArrayStart(Identifier);
FScriptSetHelper SetHelper(SetProp, InPropertyData);
for (int32 SetSparseIndex = 0; SetSparseIndex < SetHelper.GetMaxIndex(); ++SetSparseIndex)
{
if (SetHelper.IsValidIndex(SetSparseIndex))
{
const uint8* SetEntryData = SetHelper.GetElementPtr(SetSparseIndex);
UDataTableLibrary::WriteContainerEntry(JsonWriter, SetHelper.GetElementProperty(), SetEntryData);
}
}
JsonWriter->WriteArrayEnd();
}
else if (const UMapProperty* MapProp = Cast<const UMapProperty>(InProperty))
{
JsonWriter->WriteObjectStart(Identifier);
FScriptMapHelper MapHelper(MapProp, InPropertyData);
for (int32 MapSparseIndex = 0; MapSparseIndex < MapHelper.GetMaxIndex(); ++MapSparseIndex)
{
if (MapHelper.IsValidIndex(MapSparseIndex))
{
const uint8* MapKeyData = MapHelper.GetKeyPtr(MapSparseIndex);
const uint8* MapValueData = MapHelper.GetValuePtr(MapSparseIndex);
// JSON object keys must always be strings
const FString KeyValue = DataTableUtils::GetPropertyValueAsStringDirect(MapHelper.GetKeyProperty(), (uint8*)MapKeyData, DTExportFlags);
UDataTableLibrary::WriteContainerEntry(JsonWriter, MapHelper.GetValueProperty(), MapValueData, &KeyValue);
}
}
JsonWriter->WriteObjectEnd();
}
else if (const UStructProperty* StructProp = Cast<const UStructProperty>(InProperty))
{
if (!!(DTExportFlags & EDataTableExportFlags::UseJsonObjectsForStructs))
{
JsonWriter->WriteObjectStart(Identifier);
UDataTableLibrary::WriteStruct(JsonWriter, StructProp->Struct, InPropertyData);
JsonWriter->WriteObjectEnd();
}
else
{
const FString PropertyValue = DataTableUtils::GetPropertyValueAsString(InProperty, (uint8*)InRowData, DTExportFlags);
JsonWriter->WriteValue(Identifier, PropertyValue);
}
}
else
{
const FString PropertyValue = DataTableUtils::GetPropertyValueAsString(InProperty, (uint8*)InRowData, DTExportFlags);
JsonWriter->WriteValue(Identifier, PropertyValue);
}
return true;
}
bool UDataTableLibrary::WriteContainerEntry(TSharedRef<FDataTableJsonWriter> JsonWriter, const UProperty* InProperty, const void* InPropertyData, const FString* InIdentifier /*= nullptr*/, const EDataTableExportFlags DTExportFlags /*= EDataTableExportFlags::None*/)
{
if (const UEnumProperty* EnumProp = Cast<const UEnumProperty>(InProperty))
{
const FString PropertyValue = DataTableUtils::GetPropertyValueAsStringDirect(InProperty, (uint8*)InPropertyData, DTExportFlags);
WriteJSONValueWithOptionalIdentifier(*JsonWriter, InIdentifier, PropertyValue);
}
else if (const UNumericProperty* NumProp = Cast<const UNumericProperty>(InProperty))
{
if (NumProp->IsEnum())
{
const FString PropertyValue = DataTableUtils::GetPropertyValueAsStringDirect(InProperty, (uint8*)InPropertyData, DTExportFlags);
WriteJSONValueWithOptionalIdentifier(*JsonWriter, InIdentifier, PropertyValue);
}
else if (NumProp->IsInteger())
{
const int64 PropertyValue = NumProp->GetSignedIntPropertyValue(InPropertyData);
WriteJSONValueWithOptionalIdentifier(*JsonWriter, InIdentifier, PropertyValue);
}
else
{
const double PropertyValue = NumProp->GetFloatingPointPropertyValue(InPropertyData);
WriteJSONValueWithOptionalIdentifier(*JsonWriter, InIdentifier, PropertyValue);
}
}
else if (const UBoolProperty* BoolProp = Cast<const UBoolProperty>(InProperty))
{
const bool PropertyValue = BoolProp->GetPropertyValue(InPropertyData);
WriteJSONValueWithOptionalIdentifier(*JsonWriter, InIdentifier, PropertyValue);
}
else if (const UStructProperty* StructProp = Cast<const UStructProperty>(InProperty))
{
if (!!(DTExportFlags & EDataTableExportFlags::UseJsonObjectsForStructs))
{
WriteJSONObjectStartWithOptionalIdentifier(*JsonWriter, InIdentifier);
UDataTableLibrary::WriteStruct(JsonWriter, StructProp->Struct, InPropertyData);
JsonWriter->WriteObjectEnd();
}
else
{
const FString PropertyValue = DataTableUtils::GetPropertyValueAsStringDirect(InProperty, (uint8*)InPropertyData, DTExportFlags);
WriteJSONValueWithOptionalIdentifier(*JsonWriter, InIdentifier, PropertyValue);
}
}
else if (const UArrayProperty* ArrayProp = Cast<const UArrayProperty>(InProperty))
{
// Cannot nest arrays
return false;
}
else if (const USetProperty* SetProp = Cast<const USetProperty>(InProperty))
{
// Cannot nest sets
return false;
}
else if (const UMapProperty* MapProp = Cast<const UMapProperty>(InProperty))
{
// Cannot nest maps
return false;
}
else
{
const FString PropertyValue = DataTableUtils::GetPropertyValueAsStringDirect(InProperty, (uint8*)InPropertyData, DTExportFlags);
WriteJSONValueWithOptionalIdentifier(*JsonWriter, InIdentifier, PropertyValue);
}
return true;
}
由于在DataTableLibrary.cpp的#include文件中包含了"JsonWriter.h",该头文件属于Json模块,因此在UDataTableLibrary类所属模块的编译文件(Build.cs文件)中需引入Json模块,如下代码所示位置
PrivateDependencyModuleNames.AddRange(
new string[]
{
"CoreUObject",
"Engine",
"Slate",
"SlateCore",
// ... add private dependencies that you statically link with here ...
"JsonUtilities",
"Json",
}
);
接下来,剖析上述改写代码形成的过程
改写UDataTable类成员函数GetTableAsJSON()重点在于改写class FDataTableExporterJSON 类,而改写class FDataTableExporterJSON类的关键在于如何处理它的3个成员变量,这3个成员变量在构造函数中被初始化。
EDataTableExportFlags DTExportFlags;
TSharedRef<FDataTableJsonWriter> JsonWriter;
bool bJsonWriterNeedsClose;
FDataTableExporterJSON::FDataTableExporterJSON(const EDataTableExportFlags InDTExportFlags, FString& OutExportText)
: DTExportFlags(InDTExportFlags)
, JsonWriter(TJsonWriterFactory<TCHAR, TPrettyJsonPrintPolicy<TCHAR>>::Create(&OutExportText))
, bJsonWriterNeedsClose(true)
{
}
FDataTableExporterJSON::FDataTableExporterJSON(const EDataTableExportFlags InDTExportFlags, TSharedRef<FDataTableJsonWriter> InJsonWriter)
: DTExportFlags(InDTExportFlags)
, JsonWriter(InJsonWriter)
, bJsonWriterNeedsClose(false)
{
}
FDataTableExporterJSON类的构造函数
(1) EDataTableExportFlags DTExportFlags;
从UDataTable类的成员函数声明FString GetTableAsJSON(const EDataTableExportFlags InDTExportFlags = EDataTableExportFlags::None) const,不难看出,成员变量EDataTableExportFlags DTExportFlags可以使用GetTableAsJSON函数参数默认值EDataTableExportFlags::None,并且从FDataTableExporterJSON类的各成员函数的函数体代码中,不难发现成员变量DTExportFlags值始终保持不变。按照修改最小化原则,不妨在相应的改写函数的参数列表中添加该同名参数并赋予默认值EDataTableExportFlags::None,这样既可以不修改函数体内部代码,又可以减少函数调用时参数传递的个数。
(2) TSharedRef
JsonWriter;
首先,仿照FDataTableExporterJSON::FDataTableJsonWriter,在UDataTableLibrary类.h文件中声明FDataTableJsonWriter类型
/** Based on FDataTableExporterJSON::FDataTableJsonWriter */
typedef TJsonWriter<TCHAR, TPrettyJsonPrintPolicy<TCHAR>> FDataTableJsonWriter;
然后,仿照FDataTableExporterJSON类的构造函数,在UDataTableLibrary :: GetDataTableAsJSONString()函数中定义并初始化JsonWriter变量,
TSharedRef<FDataTableJsonWriter> JsonWriter = TJsonWriterFactory<TCHAR, TPrettyJsonPrintPolicy<TCHAR>>::Create(&JSONString);
最后在改写FDataTableExporterJSON成员函数时,若函数体中存在JsonWriter成员变量,则在同名的改写函数参数列表中添加参数TSharedRef
(3) bool bJsonWriterNeedsClose;
从FDataTableExporterJSON类源代码中不难发现,bJsonWriterNeedsClose变量只出现在类的构造和析构函数中,且只用于判断是否执行JsonWriter->Close()。
FDataTableExporterJSON::~FDataTableExporterJSON()
{
if (bJsonWriterNeedsClose)
{
JsonWriter->Close();
}
}
在执行UDataTable::GetTableAsJSON()函数体时,在执行FDataTableExporterJSON类构造函数时,bJsonWriterNeedsClose被赋值true;也就是调用UDataTable::GetTableAsJSON()函数需要执行JsonWriter->Close()。不妨直接把JsonWriter->Close()放在UDataTableLibrary::WriteTable()被调用之后,如上述UDataTableLibrary::GetDataTableAsJSONString()函数体代码所示位置。
在了解如何处理上述3个成员变量之后,其它工作就是直接从FDataTableExporterJSON类中copy代码了,稍微改写下函数原型,并在改写函数被调用处增加JsonWriter参数。
4. Usage
5. Conclusion
本文以改写UDataTable类成员函数GetTableAsJSON()为例,介绍了改写UE4引擎源代码的过程,以实现自己想要的功能。既是对上一期读写DataTable数据表的功能完善,也是想要通过案例,来传递一种想法,虚幻程序员应当尽力挖掘引擎源代码,补充、修改、完善更多引擎功能。