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

UDataTable部分源代码;

首先,如上图所示,在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()函数体;

继续查看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类;

进一步查看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::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类的构造函数源代码;

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 JsonWriter。

(3) bool bJsonWriterNeedsClose;

从FDataTableExporterJSON类源代码中不难发现,bJsonWriterNeedsClose变量只出现在类的构造和析构函数中,且只用于判断是否执行JsonWriter->Close()。

FDataTableExporterJSON::~FDataTableExporterJSON()
{
	if (bJsonWriterNeedsClose)
	{
		JsonWriter->Close();
	}
}
FDataTableExporterJSON类的析构函数;

在执行UDataTable::GetTableAsJSON()函数体时,在执行FDataTableExporterJSON类构造函数时,bJsonWriterNeedsClose被赋值true;也就是调用UDataTable::GetTableAsJSON()函数需要执行JsonWriter->Close()。不妨直接把JsonWriter->Close()放在UDataTableLibrary::WriteTable()被调用之后,如上述UDataTableLibrary::GetDataTableAsJSONString()函数体代码所示位置。

在了解如何处理上述3个成员变量之后,其它工作就是直接从FDataTableExporterJSON类中copy代码了,稍微改写下函数原型,并在改写函数被调用处增加JsonWriter参数。

4. Usage

图1. GetTableAsJsonString蓝图函数;

图2. 输出Log;

5. Conclusion

本文以改写UDataTable类成员函数GetTableAsJSON()为例,介绍了改写UE4引擎源代码的过程,以实现自己想要的功能。既是对上一期读写DataTable数据表的功能完善,也是想要通过案例,来传递一种想法,虚幻程序员应当尽力挖掘引擎源代码,补充、修改、完善更多引擎功能。