如何在打包的 UE4 项目中即时更改游戏数据?

How to change game data on the fly in a packaged UE4 project?

我的问题似乎很简单,但是我无法在网上找到任何解决方案。我看过许多不同类型的对象,例如 DataTables 和 DataAssets,才意识到它们仅适用于静态数据。

我项目的目标是拥有数据驱动的可配置资产,我们可以在其中为不同的对象选择不同的配置。我已经能够在 运行 时从数据库中成功提取 JSON 数据,但是,我想将所述数据保存到数据资产之类的东西或我可以读写的类似东西中.因此,当我们稍后从所述数据库中提取时,我们只会提取对不同配置的更新,而不是整个数据库(每次启动时)。

附带说明:这会是 possible/feasible 使用 .ini 文件还是这种东西对于类似的东西来说太大了(即 1000+ json 个对象)?

如能解决此问题,我们将不胜感激。

就像你说的,DataTable 在这里并不真正可用。您需要使用 UE4 的各种文件 IO API 实用程序。


获取本地路径

此函数将相对于预期保存目录的路径转换为相对于 UE4 可执行文件的路径,这是整个 UE4 的文件 IO 中预期的格式。

//DataUtilities.cpp

FString DataUtilities::FullSavePath(const FString& SavePath) {
    return FPaths::Combine(FPaths::ProjectSavedDir(), SavePath);
}

"Campaign/profile1.json" 因为输入会导致类似:

"<game.exe>/game/Saved/Campaign/profile1.json".

在本地写任何东西之前,你应该找到合适的地方来做。使用 ProjectSaveDir() 会导致在打包构建中将文件保存到 <your_game.exe>/your_game/Saved/,或者在开发构建中将文件保存到项目的 Saved 文件夹中。此外,如果 ProjectSavedDir() 不适合您的目的,FPaths 还有其他名为 Dir 的函数。

使用 FPaths::Combine 连接路径比尝试使用 '/' 附加字符串更不容易出错。


存储生成的 JSON 磁盘上的文本数据

我假设你有一个有效的 JSON 填充的 FString(而不是 FJSONObject),因为生成有效的 JSON 是相当微不足道的。

您可以尝试直接写入上述函数给出的完整路径的位置,但如果它的目录树不存在(即 first-运行),它'会失败。因此,要生成该路径树,需要进行一些路径处理和 PlatformFile 用法。

//DataUtilities.cpp

void DataUtilities::WriteSaveFile(const FString& SavePath, const FString& Data) {
    auto FullPath = FullSavePath(SavePath);
    FString PathPart, Disregard;
    FPaths::Split(FullPath, PathPart, Disregard, Disregard);
    IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
    if (PlaftormFile.CreateDirectoryTree(*PathPart)){
        FFileHelper::SaveStringToFile(Data, *FullPath);
    }
}

如果您不确定其中任何一项的作用,请阅读下面文档部分中的 FPathsFPlatformFileManager

至于生成 JSON 字符串:我没有使用 Json 模块的 DOM,而是在需要时直接从我的 FStructs 生成 JSON 字符串,所以我不有使用 Json 模块序列化功能的经验。 似乎很好地涵盖了这一点,但是,如果你走那条路。


从磁盘中提取文本数据

// DataUtilities.cpp

bool DataUtilities::SaveFileExists(const FString& SavePath) {
    return IFileManager::Get().FileExists(*FullSavePath(SavePath));
}

FString DataUtilities::ReadSaveFile(const FString& SavePath) {
    FString Contents;
    if(SaveFileExists(SavePath)) {
        FFileHelper::LoadFileToString(Contents, *FullSavePath(SavePath));
    }
    return Contents;
}

很明显,这仅适用于字符串或类似字符串的数据,其中 JSON 符合条件。

您可以将 SaveFileExists 合并为 ReadSaveFile,但我发现对其他方法使用简单的 "does-this-exist" 探测有好处。 YMMV.

我假设如果您已经从服务器上拉出 JSON,您可以将其反序列化为某种形式的可遍历容器。如果您不这样做,this 是 UE4 Answer Hub 中使用 Json 模块执行此操作的示例。


相关文档


为了解决您的旁注:我建议使用与保存的内容类型相匹配的扩展名,如果只是为了明确意图。即,descriptive_name.json 表示包含 JSON 的文件。如果您提前知道您将同时成为 reading/needing 所有数百个 数千个 个 JSON 个对象,它将尽可能多的文件分组到更少的文件中可能会更好,以最大限度地减少开销。