技术标签: ue5 ue4 Unreal 必学系列 unreal engine
初级:
Gameplay数据
USTRUCT(BlueprintType)
struct DATADRIVEN_API FShopItem
{
GENERATED_BODY();
public:
UPROPERTY(BlueprintReadWrite,EditAnywhere)
FText Name;
UPROPERTY(BlueprintReadWrite, EditAnywhere)
UTexture2D* Icon;
UPROPERTY(BlueprintReadWrite, EditAnywhere)
float Price;
};
ConstructorHelpers
只能在构造函数中使用。如果在非构造函数中使用的话,则会引起Crash(原因不明),在其代码内部检查了是否在构造函数中调用:CheckIfIsInConstructor(ObjectToFind);
。
如下 在构造函数中查找UMG 并且设置WidgetClass
ConstructorHelpers::FClassFinder<UUserWidget> classFinder(TEXT("/Game/Blueprint/CPP/UMG_Shop_CPP"));
Widget= CreateDefaultSubobject<UWidgetComponent>("Widget");
Widget->SetupAttachment(RootComponent);
Widget->SetRelativeLocation(FVector(0.f, 0.f, 110.f));
Widget->SetRelativeScale3D(FVector(0.3f, 0.3f, 0.3f));
Widget->SetDrawAtDesiredSize(true);
Widget->SetManuallyRedraw(true);
if (classFinder.Succeeded())
{
Widget->SetWidgetClass(classFinder.Class);
}
中级:
数据结构设计:
USTRUCT(BlueprintType)
struct FShopItem : public FTableRowBase
{
GENERATED_BODY()
UPROPERTY(BlueprintReadWrite,EditAnywhere)
FText Name;
UPROPERTY(BlueprintReadWrite, EditAnywhere)
UTexture2D* Icon;
UPROPERTY(BlueprintReadWrite, EditAnywhere)
float Price;
};
基于ShopItem创建DataTable, 可以在UE引擎里 点击添加进行操作,也可以导出成Json文件或CSV文件 进行修改,再点重新导入
创建单个UMG_Item
在图表里,添加ShopItem变量,并且公开,去更新图片,价格,名字
创建一个UMG_Shop,用来显示数据表里的Item
蓝图里获取DataTable的值
添加UMG_Item
// ShopConfig.h
struct FShopItem;
UCLASS(Blueprintable, Category = "DataDriven")
class UShopConfig : public UObject
{
GENERATED_BODY()
public:
virtual bool Initialize();
public:
UFUNCTION(BlueprintPure, DisplayName = "ShopConfig")
static UShopConfig* Get();
public:
void GetAllItems(TArray<FShopItem*>& outItems);
protected:
UPROPERTY()
UDataTable* mItemsDataTable;
};
//ShopConfig.cpp
bool UShopConfig::Initialize()
{
mItemsDataTable = LoadObject<UDataTable>(nullptr, TEXT("/Game/Blueprint/DataTable/DT_ShopItems.DT_ShopItems"));
return mItemsDataTable!=nullptr;
}
UShopConfig* UShopConfig::Get()
{
//获取类的可变默认对象,GetDefault不可变
return GetMutableDefault<UShopConfig>();
}
void UShopConfig::GetAllItems(TArray<FShopItem*>& outItems)
{
static const FString ContextString(TEXT("GENERAL"));
mItemsDataTable->GetAllRows(ContextString,outItems);
}
使用方法:
void UBaseShopWdget::NativePreConstruct()
{
Super::NativePreConstruct();
if (ItemWidgetClass == nullptr)
{
return;
}
ItemsUniformGridPanel->ClearChildren();
TArray<FShopItem*> outItems;
UShopConfig::Get()->GetAllItems(outItems);
for (int32 i = 0; i < outItems.Num(); ++i)
{
UBaseItemWdget* itemWidget = CreateWidget<UBaseItemWdget>(ItemsUniformGridPanel, ItemWidgetClass);
itemWidget->Item = *outItems[i];
ItemsUniformGridPanel->AddChildToUniformGrid(itemWidget, i / 5, i % 5);
}
}
当出现相同的数据索引时,以最后面的表为第一优先级
外部CSV 编辑好表后,直接拖入场景,选择CurveTable
继承自UPrimaryDataAsset(方便管理数据对象的加载释放) ,也可以继承UDataAsset
UCLASS(BlueprintType)
class UShopItemAsset :public UPrimaryDataAsset
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadWrite,EditAnywhere)
FText Name;
UPROPERTY(BlueprintReadWrite, EditAnywhere)
UTexture2D* Icon;
UPROPERTY(BlueprintReadWrite, EditAnywhere)
float Price;
};
资产管理器中添加路径,可以动态加载
加载方式:
1.
2.通过Key动态加载
张乂卓:UE4动作游戏实例RPG Action解析五:GameInstance加载UPrimaryDataAsset(武器资源)并加载默认武器资源
多个DataAsset 可以批量编辑
6.2 全局数据
UCLASS(BlueprintType)
class UShopGlobalSettings : public UDataAsset
{
GENERATED_BODY()
public:
UShopGlobalSettings();
public:
UFUNCTION(BlueprintPure, DisplayName = "ShopGlobalSettings")
static UShopGlobalSettings* Get();
UPROPERTY(BlueprintReadWrite, EditAnywhere)
float PlayerStartMoney = 100.f;
};
//cpp
UShopGlobalSettings::UShopGlobalSettings()
{
}
UShopGlobalSettings* UShopGlobalSettings::Get()
{
static UShopGlobalSettings* obj=nullptr;
if (obj==nullptr)
{
obj = LoadObject<UShopGlobalSettings>(nullptr, TEXT("/Game/Blueprint/DataAsset/DA_ShopGlobalSettings"));
obj->AddToRoot();
}
return obj;
}
使用方法:
UCLASS(Abstract)
class UPlayerWidget : public UUserWidget
{
GENERATED_BODY()
public:
public:
virtual void NativePreConstruct()override;
virtual void NativeConstruct()override;
public:
UPROPERTY(meta = (BindWidget))
class UTextBlock* DifficultyTextBlock;
};
void UPlayerWidget::NativePreConstruct()
{
Super::NativePreConstruct();
}
void UPlayerWidget::NativeConstruct()
{
Super::NativeConstruct();
AWorldSettings* settings= UGameplayStatics::GetGameMode(this)->GetWorld()->GetWorldSettings();
AShopWorldSettings* shopSettings=Cast<AShopWorldSettings>(settings);
if (shopSettings!=nullptr)
{
DifficultyTextBlock->SetText(UKismetTextLibrary::Conv_FloatToText(shopSettings->LevelDifficulty, ERoundingMode::HalfToEven));
}
}
高级:
创建一个C++文件继承DeveloperSettings
UCLASS(config = DataDrivenProjectSettings, defaultconfig)
class UDataDrivenProjectSettings :public UDeveloperSettings
{
GENERATED_BODY()
public:
/** Gets the settings container name for the settings, either Project or Editor */
virtual FName GetContainerName() const override { return TEXT("Project"); }
/** Gets the category for the settings, some high level grouping like, Editor, Engine, Game...etc. */
virtual FName GetCategoryName() const override { return TEXT("DataDriven"); }
/** The unique name for your section of settings, uses the class's FName. */
virtual FName GetSectionName() const override { return TEXT("DataDriven"); }
public:
UFUNCTION(BlueprintPure, DisplayName = "DataDrivenProjectSettings")
static UDataDrivenProjectSettings* Get() { return GetMutableDefault<UDataDrivenProjectSettings>(); }
public:
UPROPERTY(Config, EditAnywhere, BlueprintReadWrite, Category = Shop)
float PriceOff = 50.f;
UPROPERTY(Config, EditAnywhere, BlueprintReadWrite, Category = Player)
float PlayerStartLevel = 10.f;
};
重新编译之后,在项目设置里面会出现如下信息
修改其中的值,在项目Config目录里会出现新的文件
蓝图调用:
调用父类方法
UCLASS(config = CustomConfig)
class ACustomConfigActor :public AActor
{
GENERATED_BODY()
public:
ACustomConfigActor();
public:
UPROPERTY(Config, EditAnywhere, BlueprintReadWrite, Category = MyCustom)
float MaxLife = 100.f;
UPROPERTY(transient, EditAnywhere, BlueprintReadWrite, Category = MyCustom)
float CurrentLife = 50.f;
public:
virtual void BeginPlay()override;
UFUNCTION(BlueprintCallable)
void AddMaxLifeAndSave(float val);
};
//cpp
ACustomConfigActor::ACustomConfigActor()
{
}
void ACustomConfigActor::BeginPlay()
{
Super::BeginPlay();
LoadConfig();
}
void ACustomConfigActor::AddMaxLifeAndSave(float val)
{
MaxLife+=val;
SaveConfig();
}
dd.ShopEnabled 接数值
UCLASS(BlueprintType,Blueprintable)
class AConsoleVariableActor :public AActor
{
GENERATED_BODY()
public:
AConsoleVariableActor();
public:
virtual void BeginPlay()override;
virtual void Tick(float DeltaSeconds)override;
};
static TAutoConsoleVariable<bool> CVarDDShopEnabled(
TEXT("dd.ShopEnabled"),
false,
TEXT("The switch to enable shop or not."),
ECVF_Default);
static TAutoConsoleVariable<int> CVarDDShopCount(
TEXT("dd.ShopCount"),
3,
TEXT("The count of shop."),
ECVF_Default);
static TAutoConsoleVariable<FString> CVarDDShopName(
TEXT("dd.ShopName"),
TEXT("Jack"),
TEXT("The name of shop."),
ECVF_Default);
AConsoleVariableActor::AConsoleVariableActor()
{
PrimaryActorTick.bCanEverTick=true;
}
void AConsoleVariableActor::BeginPlay()
{
Super::BeginPlay();
}
void AConsoleVariableActor::Tick(float DeltaSeconds)
{
Super::Tick(DeltaSeconds);
FString str=FString::Printf(TEXT("dd.ShopEnabled: %s"), CVarDDShopEnabled.GetValueOnGameThread()?TEXT("true"):TEXT("false"));
str += FString::Printf(TEXT("\ndd.ShopCount: %d"), CVarDDShopCount.GetValueOnGameThread());
str += FString::Printf(TEXT("\ndd.ShopName: %s"), *CVarDDShopName.GetValueOnGameThread());
GEngine->AddOnScreenDebugMessage(0,5.f,FColor::Red, str);
}
FString ARawDataActor::ReadTxtFile(FString path)
{
FString realPath = FPaths::ProjectDir() / path;
FString content;
FFileHelper::LoadFileToString(content, *realPath);
return content;
}
bool ARawDataActor::LoadCsvAsTable(FString path, TArray<FShopItem>& Items)
{
UDataTable* DataTable = NewObject<UDataTable>(GetTransientPackage(), FName(TEXT("TempDataTable")));
DataTable->RowStruct = FShopItem::StaticStruct();
FString realPath = FPaths::ProjectDir() / path;
FString content;
if (!FFileHelper::LoadFileToString(content, *realPath))
{
return false;
}
DataTable->CreateTableFromCSVString(content); //CreateTableFromJSONString
static const FString ContextString(TEXT("GENERAL"));
DataTable->ForeachRow<FShopItem>(ContextString, [&Items](const FName& key, const FShopItem& value)
{
Items.Add(value);
});
return true;
}
bool ARawDataActor::LoadJsonAsTable(FString path, TArray<FShopItem>& Items)
{
FString realPath = FPaths::ProjectDir() / path;
FString content;
FFileHelper::LoadFileToString(content, *realPath);
TSharedPtr<FJsonObject> jsonRoot = MakeShareable(new FJsonObject());
TSharedRef<FJsonStringReader> jsonReader= FJsonStringReader::Create(content);
TArray<TSharedPtr<FJsonValue>> OutArray;
if (FJsonSerializer::Deserialize<TCHAR>(jsonReader, OutArray))
{
for (TSharedPtr<FJsonValue> itemJson: OutArray)
{
const TSharedPtr<FJsonObject>& obj=itemJson->AsObject();
FShopItem& newItem= Items.AddDefaulted_GetRef();
newItem.Name=FText::FromString(obj->GetStringField(TEXT("Name")));
newItem.Icon=LoadObject<UTexture2D>(nullptr,*obj->GetStringField(TEXT("Icon")));
newItem.Price = obj->GetNumberField(TEXT("Price"));
}
return true;
}
return false;
}
bool UExcelWorkbook::StartWatch(const UObject* WorldContextObject, FExcelWorkbookChangedDelegate onChanged, bool autoReload /*= true*/, float rate/*=1.f*/)
{
#ifdef WIN32
//wchar_t watchDirectory[512] = { 0 };
//MultiByteToWideChar(CP_ACP, 0, (char*)*mPath, strlen((char*)*mPath), watchDirectory, sizeof(watchDirectory) / sizeof(wchar_t));
FString path = FPaths::GetPath(mPath);
const wchar_t* p = *path;
mFindHandle = ::FindFirstChangeNotification(p, 0, FILE_NOTIFY_CHANGE_LAST_WRITE);
if (INVALID_HANDLE_VALUE == mFindHandle)
{
DWORD errorCode = GetLastError();
UE_LOG(LogDirectExcel, Error, L"%d", errorCode);
return false;
}
UWorld* world = GEngine->GetWorldFromContextObjectChecked(WorldContextObject);
world->GetTimerManager().SetTimer(mWatchTimer, [this, WorldContextObject, onChanged, autoReload]
{
DWORD dwWait = ::WaitForSingleObject(mFindHandle, 0);
UE_LOG(LogDirectExcel, Warning, TEXT("Watch result:%d."), dwWait);
if (dwWait == WAIT_FAILED)
{
DWORD errorCode = GetLastError();
UE_LOG(LogDirectExcel, Error, L"%d", errorCode);
StopWatch(WorldContextObject);
return;
}
if (WAIT_OBJECT_0 == dwWait)
{
bool isOpen = true;
if (autoReload)
{
isOpen = Reload();
}
if (isOpen)
{
onChanged.ExecuteIfBound(this);
}
else
{
onChanged.ExecuteIfBound(nullptr);
}
if (!::FindNextChangeNotification(mFindHandle))
{
::FindCloseChangeNotification(mFindHandle);
mFindHandle = NULL;
UE_LOG(LogDirectExcel, Warning, TEXT("Lost watch."));
}
}
}, rate, true);
#endif
return true;
}
UCLASS(BlueprintType,Blueprintable)
class ASqlDataActor :public AActor
{
GENERATED_BODY()
public:
ASqlDataActor();
public:
virtual void BeginPlay()override;
public:
UFUNCTION(BlueprintCallable)
bool LoadSqlAsTable(FString path,TArray<FShopItem>& Items);
};
//CPP
ASqlDataActor::ASqlDataActor()
{
}
void ASqlDataActor::BeginPlay()
{
Super::BeginPlay();
}
bool ASqlDataActor::LoadSqlAsTable(FString path, TArray<FShopItem>& Items)
{
FSQLiteDatabaseConnection db;
if (!db.Open(*path, nullptr, nullptr))
{
return false;
}
FString query = FString::Printf(TEXT("SELECT Name,Icon,Price FROM ShopItems"));
FDataBaseRecordSet* outRecords = nullptr;
if (!db.Execute(*query, outRecords))
{
delete outRecords;
return false;
}
int count = outRecords->GetRecordCount();
if (count == 0)
{
delete outRecords;
return false;
}
int result = -1;
for (FDataBaseRecordSet::TIterator i(outRecords); i; ++i)
{
FShopItem& newItem = Items.AddDefaulted_GetRef();
for (FDatabaseColumnInfo column : i->GetColumnNames())
{
if (column.ColumnName == TEXT("Name"))
{
newItem.Name = FText::FromString(i->GetString(*column.ColumnName));
}
else if (column.ColumnName == TEXT("Icon"))
{
newItem.Icon = LoadObject<UTexture2D>(nullptr, *i->GetString(*column.ColumnName));
}
else if (column.ColumnName == TEXT("Price"))
{
newItem.Price = i->GetFloat(*column.ColumnName);
}
}
}
delete outRecords;
db.Close();
return true;
}
使用插件:
其中有一些函数是无法直接弄出来的,所以要在C++中添加一些东西,将函数暴露在蓝图界面中:
UCLASS(Blueprintable, Category = "DataDriven")
class UMyDataTableFunctionLibrary : public UBlueprintFunctionLibrary
GENERATED_BODY()
public:
#if WITH_EDITOR
UFUNCTION(BlueprintCallable, Category = "Editor Scripting | DataTable", DisplayName = "AddRowToDataTable")
static void AddVolumeToDataTable(UDataTable* DataTable, FName rowName,const FVolumeItem& row);
UFUNCTION(BlueprintCallable, Category = "Editor Scripting | DataTable", DisplayName = "ClearDataTable")
static void ClearDataTable(UDataTable* DataTable);
UFUNCTION(BlueprintCallable, Category = "Editor Scripting | DataTable", DisplayName = "DataTable_RemoveRow")
static void DataTable_RemoveRow(UDataTable* DataTable, FName rowName);
#endif
;
void UMyDataTableFunctionLibrary::AddVolumeToDataTable(UDataTable* DataTable, FName rowName, const FVolumeItem& row)
if (DataTable == nullptr)
return;
DataTable->AddRow(rowName, row);
DataTable->GetOutermost()->MarkPackageDirty();
void UMyDataTableFunctionLibrary::ClearDataTable(UDataTable* DataTable)
if (DataTable == nullptr)
return;
DataTable->EmptyTable();
DataTable->GetOutermost()->MarkPackageDirty();
void UMyDataTableFunctionLibrary::DataTable_RemoveRow(UDataTable* DataTable, FName rowName)
if (DataTable == nullptr)
return;
DataTable->RemoveRow(rowName);
DataTable->GetOutermost()->MarkPackageDirty();
DataTable->GetOutermost()->MarkPackageDirty();
这个代码就让小星星出现,代表已经对他进行了修改。
将启用状态改成false。
在DataDriven.Build.cs
中添加PublicDependencyModuleNames.Add("DeveloperSettings");
然后就可以成功编译运行了!
在名为Watch代码
的文件夹中有部分插件中的代码,可以借鉴参考一下。
原文链接:童年的琴:第6期 UE4 动态读写DataTable数据表
//.h
// Declare General Log Category, header file .h
DECLARE_LOG_CATEGORY_EXTERN(LogUtiliesNode, Log, All);
/**
*
*/
UCLASS()
class DATADERIVEDEXCISE_API UDataTableBFLibrary : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
/**
* Empty and fill a Data Table from CSV string.
* @param CSVString The Data that representing the contents of a CSV file.
* @return True if the operation succeeds, check the log for errors if it didn't succeed.
*/
UFUNCTION(BlueprintCallable, DisplayName = "Fill Data Table from CSV String", Category = "DataTable")
static bool FillDataTableFromCSVString(UDataTable* DataTable, const FString& CSVString);
/**
* Empty and fill a Data Table from CSV file.
* @param CSVFilePath The file path of the CSV file.
* @return True if the operation succeeds, check the log for errors if it didn't succeed.
*/
UFUNCTION(BlueprintCallable, DisplayName = "Fill Data Table from CSV File", Category = "DataTable")
static bool FillDataTableFromCSVFile(UDataTable* DataTable, const FString& CSVFilePath);
/**
* Empty and fill a Data Table from JSON string.
* @param JSONString The Data that representing the contents of a JSON file.
* @return True if the operation succeeds, check the log for errors if it didn't succeed.
*/
UFUNCTION(BlueprintCallable, DisplayName = "Fill Data Table from JSON String", Category = "DataTable")
static bool FillDataTableFromJSONString(UDataTable* DataTable, const FString& JSONString);
/**
* Empty and fill a Data Table from JSON file.
* @param JSONFilePath The file path of the JSON file.
* @return True if the operation succeeds, check the log for errors if it didn't succeed.
*/
UFUNCTION(BlueprintCallable, DisplayName = "Fill Data Table from JSON File", Category = "DataTable")
static bool FillDataTableFromJSONFile(UDataTable* DataTable, const FString& JSONFilePath);
/** Output entire contents of table as CSV string */
UFUNCTION(BlueprintCallable, DisplayName = "Get Table As CSV String", Category = "DataTable")
static void GetDataTableAsCSVString(UDataTable* DataTable, FString& CSVString);
/** Output entire contents of table as CSV File */
UFUNCTION(BlueprintCallable, DisplayName = "Get Table As CSV File", Category = "DataTable")
static void GetDataTableAsCSVFile(UDataTable* DataTable, const FString& CSVFilePath);
};
//cpp
//Declare General Log Category, source file .cpp
DEFINE_LOG_CATEGORY(LogUtiliesNode);
bool UDataTableBFLibrary::FillDataTableFromCSVString(UDataTable* DataTable, const FString& CSVString)
{
if (!DataTable || (CSVString.Len() == 0))
{
UE_LOG(LogUtiliesNode, Warning, TEXT("FillDataTableFromCSVString -> Can't fill DataTable with CSVString: %."), *CSVString);
return false;
}
// Call bulit-in function
TArray<FString> Errors = DataTable->CreateTableFromCSVString(CSVString);
if (Errors.Num())
{
// It has some error message
for (const FString& Error : Errors)
{
UE_LOG(LogUtiliesNode, Warning, TEXT("%s"), *Error);
}
return false;
}
return true;
}
bool UDataTableBFLibrary::FillDataTableFromCSVFile(UDataTable* DataTable, const FString& CSVFilePath)
{
FString CSVString;
if (FPlatformFileManager::Get().GetPlatformFile().FileExists(*CSVFilePath))
{
// Supports all combination of ANSI/Unicode files and platforms.
FFileHelper::LoadFileToString(CSVString, *CSVFilePath);
}
else
{
UE_LOG(LogUtiliesNode, Warning, TEXT("FillDataTableFromCSVFile -> Cannot find CSV file %s"), *CSVFilePath);
return false;
}
return UDataTableBFLibrary::FillDataTableFromCSVString(DataTable, CSVString);
}
bool UDataTableBFLibrary::FillDataTableFromJSONString(UDataTable* DataTable, const FString& JSONString)
{
if (!DataTable || (JSONString.Len() == 0))
{
UE_LOG(LogUtiliesNode, Warning, TEXT("FillDataTableFromJSONString -> Can't fill DataTable with JSONString: %."), *JSONString);
return false;
}
// Call bulit-in function
TArray<FString> Errors = DataTable->CreateTableFromJSONString(JSONString);
if (Errors.Num())
{
// It has some error message
for (const FString& Error : Errors)
{
UE_LOG(LogUtiliesNode, Warning, TEXT("%s"), *Error);
}
return false;
}
return true;
}
bool UDataTableBFLibrary::FillDataTableFromJSONFile(UDataTable* DataTable, const FString& JSONFilePath)
{
FString JSONString;
if (FPlatformFileManager::Get().GetPlatformFile().FileExists(*JSONFilePath))
{
// Supports all combination of ANSI/Unicode files and platforms.
FFileHelper::LoadFileToString(JSONString, *JSONFilePath);
}
else
{
UE_LOG(LogUtiliesNode, Warning, TEXT("FillDataTableFromJSONFile -> Cannot find CSV file %s"), *JSONFilePath);
return false;
}
return UDataTableBFLibrary::FillDataTableFromJSONString(DataTable, JSONString);
}
void UDataTableBFLibrary::GetDataTableAsCSVString(UDataTable* DataTable, FString& CSVString)
{
CSVString = FString();
if (!DataTable || (DataTable->RowStruct == nullptr))
{
UE_LOG(LogTemp, Warning, TEXT("UGenericMiscLibrary::GetTableAsCSV : Missing DataTable or RowStruct !"));
return;
}
// First build array of properties
TArray<FProperty*> StructProps;
for (TFieldIterator<FProperty> It(DataTable->RowStruct); It; ++It)
{
FProperty* Prop = *It;
check(Prop != nullptr);
StructProps.Add(Prop);
}
// First row, column titles, taken from properties
CSVString += TEXT("---");
for (int32 PropIdx = 0; PropIdx < StructProps.Num(); PropIdx++)
{
CSVString += TEXT(",");
CSVString += StructProps[PropIdx]->GetName();
}
CSVString += TEXT("\n");
// Now iterate over rows
for (auto RowIt = DataTable->GetRowMap().CreateConstIterator(); RowIt; ++RowIt)
{
FName RowName = RowIt.Key();
CSVString += RowName.ToString();
uint8* RowData = RowIt.Value();
for (int32 PropIdx = 0; PropIdx < StructProps.Num(); PropIdx++)
{
CSVString += TEXT(",");
CSVString += DataTableUtils::GetPropertyValueAsString(StructProps[PropIdx], RowData, EDataTableExportFlags::None);
}
CSVString += TEXT("\n");
}
}
void UDataTableBFLibrary::GetDataTableAsCSVFile(UDataTable* DataTable, const FString& CSVFilePath)
{
FString CSVString;
UDataTableBFLibrary::GetDataTableAsCSVString(DataTable, CSVString);
if (CSVString.Len() == 0)
{
return;
}
FFileHelper::SaveStringToFile(CSVString, *CSVFilePath, FFileHelper::EEncodingOptions::ForceUTF8);
}
蓝图调用:
UE4包含Json和JsonUtilities这两个模块
原文链接:【UE4】UE4读写Json文件 - Goulandis
void UDataTableBFLibrary::CreateJson()
{
FString filePath = FPaths::GetProjectFilePath() + TEXT("MysqlConfig/Connect.json");
FString jsonStr;
TSharedRef<TJsonWriter<>> jsonWriter = TJsonWriterFactory<>::Create(&jsonStr);
jsonWriter->WriteObjectStart();
jsonWriter->WriteValue(TEXT("server"), TEXT("127.0.0.1"));
jsonWriter->WriteObjectEnd();
jsonWriter->Close();
FFileHelper::SaveStringToFile(jsonStr, *filePath);
}
序列化写入:
void AMyActor::Test()
{
TSharedPtr<FJsonObject> rootObj = MakeShareable(new FJsonObject());
rootObj->SetStringField("root", "1");
TArray<TSharedPtr<FJsonValue>> arrValue;
TSharedPtr<FJsonValueString> tmp = MakeShareable(new FJsonValueString("array"));
arrValue.Add(tmp);
rootObj->SetArrayField("array", arrValue);
FString filePath = FPaths::GameContentDir() + TEXT("MysqlConfig/text.json");
FString jsonStr;
TSharedRef<TJsonWriter<TCHAR>> jsonWriter = TJsonWriterFactory<TCHAR>::Create(&jsonStr);
FJsonSerializer::Serialize(rootObj.ToSharedRef(), jsonWriter);
FFileHelper::SaveStringToFile(jsonStr, *filePath);
UE_LOG(LogTemp, Error, TEXT("%s"),*filePath);
}
反序列化读取:
TArray<FName> AMysqlJsonCpp::ReadMysqlConnectConfig()
{
FString filePath = FPaths::GameContentDir() + TEXT("MysqlConfig/Connect.json");
if (FPaths::FileExists(filePath))
{
FString server;
FString dbName;
FString userId;
FString passwd;
TArray<FName> connectConfig;
FString fileStr;
FFileHelper::LoadFileToString(fileStr, *filePath);
TSharedPtr<FJsonObject> rootObject = MakeShareable(new FJsonObject());
TSharedRef<TJsonReader<>> jsonReader = TJsonReaderFactory<>::Create(fileStr);
if (FJsonSerializer::Deserialize(jsonReader, rootObject))
{
server = rootObject->GetStringField("server");
dbName = rootObject->GetStringField("dbName");
userId = rootObject->GetStringField("userId");
passwd = rootObject->GetStringField("passwd");
}
connectConfig.Add(FName(*server));
connectConfig.Add(FName(*dbName));
connectConfig.Add(FName(*userId));
connectConfig.Add(FName(*passwd));
}
return TArray<FName>();
}
文章浏览阅读1.7k次。sqlmap是一个开源的渗透测试工具,它可以自动化检测sql注入漏洞利用sql注入缺陷 接管数据库服务器。_kali安装sqlmap
文章浏览阅读598次,点赞19次,收藏4次。本题来自第六届蓝桥杯省赛C++ B组 /JAVA A组C组《移动距离》_移动距离 蓝桥杯 c++
文章浏览阅读3.4k次。zram disksize 设置小内存项目:1G,2G,3G RAMzram disksize设置.高通:高通的设置比较简单:相关代码:init.qcom.post_boot.shif [ -f /sys/block/zram0/disksize ]; thenif [ -f /sys/block/zram0/use_dedup ]; thenecho 1 > /sys/block/zram0/use_dedupfiif [ $MemTotal -le5242_use_dedup
文章浏览阅读281次。今天中学化学园给大家推荐几款超有趣的教育软件APP,大家可以自行搜索下载,又萌又有趣,在玩乐中还能学到知识!手机要有足够内存哦~~~!下面几款适用于苹果系统~~~1.神奇的化学元素简介:可以高效帮助您记忆有关元素的基本知识。适用对象:初高中学生2.烧杯简介:150多种药剂、300多种神奇的化学反应任你尝试。安全、有趣生动、随时随地做各种化学实验,生动直观,充满乐趣~适用对象:高中学生锂..._化学游戏软件
文章浏览阅读618次。QMA8658A 是一款功能强大的6轴加速度传感器,其内置了3轴加速度计和3轴陀螺仪,能够同时测量三个方向的加速度和角速度。该传感器广泛应用于无人机、机器人、智能手机等领域。为了帮助开发人员快速评估和开发基于QMA8658A的解决方案,我们推出了QMA8658A-EVB全面的评估板。该评估板精心设计,预置了所有必需的硬件接口,兼容I2C和SPI接口,方便与任意MCU处理器进行连接和通信。此外,我们还提供了详细的驱动程序和使用指南,以便开发者能够轻松使用该评估板进行二次开发。4.1 I2C接口。_qmi8658a中文资料
文章浏览阅读551次。点击蓝字 关注我们缺血性心脏病相关肠道微生物及菌群代谢物研究进展iMeta主页:http://www.imeta.science综 述●原文链接DOI: https://doi.org/10.1002/imt2.94● 2023年2月26日,宁波大学附属第一医院崔翰斌团队、浙江省动脉粥样硬化疾病精准医学研究重点实验室范勇团队在iMeta在线发表了题为“Microbiota-related ..._与急性心肌梗死有关的微生物
文章浏览阅读1.5k次。渗透测试-安服面试点总结_安服题
文章浏览阅读424次。错误类型判断 在C语言中,常见的错误类型包括语法错误、逻辑错误和运行时错误。逻辑错误是指程序的逻辑错误,导致程序的输出不符合预期。运行时错误是指在程序运行过程中发生的错误,例如除以零、访问不存在的内存等。通过本文的介绍,你已经了解了在C语言项目中打开文件、编辑、运行和测试程序的基本方法,以及常见的错误类型判断和调试技巧。同时,持续学习和实践是提高编程技能的关键,希望本文能为你在C语言编程之路上提供帮助和指导。此外,还将探讨常见的错误类型判断和程序测试方法,帮助你提高代码质量和开发效率。_c语言编辑
文章浏览阅读2.8k次。对于集合的遍历首选方法是for-eachfor(Element e :c){ doSomething(e);}这是1.5版本之后的做法;java1.5之前使用的是Iterator迭代器。为了弄清楚为啥比普通的for循环或者whlie循环好,请看一下代码Iterator i=c.iterator();while(i.hasNext()){_c++中的while循环可有什么替代
文章浏览阅读7.7k次,点赞5次,收藏33次。一、问题为什么要进行网页授权?首先我们进行网页授权的需求是,获取用户信息、最主要是获取openid唯一值,可以用于用户登录、支付等功能,这时候就需要进行网页授权获取用户的信息以及openid。二、静默授权/非静默授权在操作之前可以先提前看看网页授权官方文档静默授权snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid;用来获取进入页面的用户的openid的,并且自动跳转到回调页的。用户感知的就是直接进入了回调页(往往是业务页面)。非静默授权snsapi_user_微信公众号静默授权
文章浏览阅读235次。A Key Volume Mining Deep Framework for Action Recognition_a key volume mining deep framework for action recognition
文章浏览阅读3.9k次。广告关闭腾讯云11.11云上盛惠 ,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高返5000元!2、python生成目录树上述 cmd 方式虽然可以生成目录树,但是并不美观,让我们用 python 实现。 2.1 标准库pathlib介绍python有一个标准文件路径处理库 os.path ,从 python3.4 开始,python 又加入了一个标准库 pathlib ,该库..._python创建一个窗口