英文原文:https://mzaks.medium.com/flexbuffers-for-unity3d-4d1ab5c53fbe
如果您在游戏中使用 JSON、CSV、XML 甚至 SQLite 来存储数据,那么您绝对应该花几分钟时间阅读这篇博文。如果你不这样做,你可能仍然会学到一些有用的东西。
FlexBuffers 是什么?
FlexBuffers 是一种 JSON 可比较的二进制格式,设计为 FlatBuffers 项目中的无模式替代方案,该项目最初由 Google 构建。
如果您对源头感兴趣,请点击链接。
JSON 可比性是什么意思?
我的意思是,您可以用 JSON 表示的所有内容也可以用 FlexBuffers 表示,反之亦然。这就是为什么 FlexBuffers Unity 提供了一种将 JSON 文件转换为 FlexBuffer 文件的简单方法的原因。并将 FlexBuffer 文件导出为 JSON 文件。但稍后再讨论这个话题。
为什么我应该使用 FlexBuffers 而不是 JSON?
JSON 文件是一个文本文件。为了访问 JSON 文件中的数据,我们需要将文本转换为 C# 对象树。这意味着我们必须将文件的内容读入内存,解析文本,并创建 C# 对象树。有不同的 JSON 解析器,可以使某些步骤或多或少地执行,但是 JSON 解析器无法避免其中一个步骤。
当您从 FlexBuffers 文件中读取值时,只有两个步骤:
- 将文件内容加载到内存中
- 访问字节数组中的值
使用 FlexBuffers,我们不会将您的数据结构反映为 C# 对象树。我们可以使用如下表达式直接从字节数组中读取数据:
var root = FlxValue.FromBytes(bytes);
Assert.AreEqual("Berlin", root["address"]["city"].AsString);
用这个表达式,我们说根有一个属性address,这个属性有一个自己的属性,叫做city。我们知道 city 是字符串类型,我们要求将此值转换为 C# 字符串类型的对象。
基于访问语义 [] 的字节遍历非常快,我们可以通过检查以下分析器屏幕截图看到:
你在这里看到的是我按下一个 UI 按钮,它将一个 161KB 的 FlexBuffer 文件加载到内存中(参见 TextAsset.get_bytes() (0.38ms)),其中包含一个包含 1000 个条目的向量,然后我们从这个向量中访问 50 个条目:
var bytes = Resources.Load<TextAsset>("flx_data").bytes;
var root = FlxValue.FromBytes(bytes);
var names = new List<string>(50);
for (var i = 0; i < 50; i++)
{
names.Add(root[i]["name"].AsString);
}
Debug.Log(names);
中间的 50 个冰柱显示了从字节数组中以字符串形式访问名称所需的时间。这是我放大冰柱的屏幕截图:
这证明了单独访问一个值是一个非常轻量级的操作。 (这可能可以进一步优化)。
让我们使用 Unity 自己的 JSONUtility 来比较一个类似的过程:
包含相同数据的 JSON 文件占用 246KB,加载时间为 1.28ms。这是因为相同数据的 JSON 表示要大 80KB (35%),而且在 FlexBuffers 的情况下,我们只需要一个原始字节数组,在 JSON 的情况下,我们需要一个 UTF-16编码的 C# 字符串对象。
然后我们还看到,大部分时间都花在了 JsonUtility.FromJsonInternal() 中,因为我们需要解析字符串并将其转换为 C# 对象。
var jsonText = Resources.Load<TextAsset>("json_data2").text;
var cities = JsonUtility.FromJson<CityList>(jsonText);
var names = new List<string>(50);
for (var i = 0; i < 50; i++)
{
names.Add(cities.list[i].name);
}
Debug.Log(names);
实际访问这些值并将它们添加到字符串列表中完全可以忽略不计:
所以 JSON 的主要性能瓶颈是解析步骤。如果您想知道其他 JSON 解析器是否比 Unity JsonUtility 更好。你自己看。
这是一个使用 Utf8Json 库的测试,它宣称自己为:
绝对是 C#(NET、.NET Core、Unity、Xamarin)的最快和零分配 JSON 序列化程序
JsonSerializer.Deserialize() 需要 47.87 毫秒才能运行。
并且测试代码并没有那么不同:
var jsonText = Resources.Load<TextAsset>("json_data2").text;
var cities = Utf8Json.JsonSerializer.Deserialize<CityList>(jsonText);
var names = new List<string>(50);
for (var i = 0; i < 50; i++)
{
names.Add(cities.list[i].name);
}
Debug.Log(names);
如果您认为这很糟糕,请查看 Json.Net 的此屏幕截图:
使用 Json.Net,我们设法在 447.67 毫秒内解析 JSON 文件。所以比 Utf8Json 慢 10 倍,比 JsonUtility 慢 100 倍。
使用 Json.Net,我们也不会将 JSON 文本转换为 C# 类实例。我们将结构转换为通用 JArray 和 JObject 实例。从这些实例中访问值会产生一些开销,这比我们在 FlexBuffers 中看到的要低一点:
预付与现收现付
正如我们从这个简短的分析会话中看到的那样,使用 FlexBuffers,我们为将数据加载到内存中以及我们需要访问的每个值付费。使用 JSON,由于必要的解析步骤,我们需要支付高额的预付费用。税收还基于文件的大小,而不是您需要从文件中提取的值的数量。
这就是为什么 FlexBuffers 在配置文件方面完全有意义的原因。配置文件通常包含游戏所有阶段的数据。但是根据玩家的进度,您只能访问一小部分数据。本地化文件、复杂级别文件和其他数据集合也是如此。我知道有些游戏发布 SQLite 是为了在运行时读取“随机”数据。我相信在很多情况下 FlexBuffers 文件会是一个更好的解决方案。
FlexBuffers 是人类可读的吗?
FlexBuffers 不是文本格式,因此您将无法使用文本编辑器打开 FlexBuffers 文件,但是,FlexBuffer Unity 有一个 FlexBuffer 浏览器窗口:
在这里,您可以看到我能够打开一个 127.1MB 的复杂 FlexBuffers 文件并在其中导航,没有任何明显的延迟。根据我的经验,没有文本编辑器能以相当的速度打开一个 127MB 的文件。
我可以在 FlexBuffer 文件中搜索值吗?
目前,FlexBuffer Browser 只有一个路径过滤功能:
如您所见,我们可以定义路径和向量范围以减少树子的数量。目前无法进行完整搜索,但可能会在未来的版本中引入。
如何创建 FlexBuffer 文件?
您可以以编程方式构建 FlexBuffer,但这可能不是您想要为大型配置执行的操作。这就是我介绍以下菜单点的原因:
通过选择 JSON 作为 FlexBuffer… CSV 作为 FlexBuffer… 或 XML 作为 FlexBuffer… 您将看到一个文件选择器来选择源文件和另一个文件选择器来选择您想要存储 .bytes 文件的位置包含 FlexBuffer。
如果我想更改现有 FlexBuffers 文件中的某些内容怎么办?
遗憾的是,直接在 FlexBuffers 文件中更改数据并不是那么简单。但是,我们可以将 FlexBuffer 文件导出为 JSON:
在文本编辑器中打开 JSON 文件并进行编辑:
单击从 JSON 按钮导入:
我怎样才能自己尝试呢?
FlexBuffers Unity 基于 FlexBuffers-CSharp。并且还有一个自给自足的 Github 存储库:https://github.com/mzaks/FlexBuffersUnity
其结构为 Unity 包。因此,如果您想在游戏中使用 FlexBuffers Unity,只需在 manifest.json 文件中添加以下行:
“com.mzaks.flexbuffers”: “https://github.com/mzaks/FlexBuffersUnity.git”,
感谢您阅读。