-
Notifications
You must be signed in to change notification settings - Fork 385
DataList
我们主张,如非必要,应该采用易于人阅读的文本格式记录持久化数据。
在游戏引擎中,非结构化的大数据块,出于性能考虑,通常使用二进制格式。例如着色器、模型的顶点数据、贴图、骨骼动画的关键帧等等。这些数据一般不需要人阅读,引擎需要和更底层的硬件驱动或独立第三方库对接时会传输这些数据块,所以在程序运行时,会直接从文件系统读出这个数据,不需要转换,按对接模块需要的数据结构直接输送。这些二进制数据块,我们选择在 VFS 中保留单独的二进制文件。
但这些二进制数据通常都有一些可供人查看的元信息,就需要在文件中组织为合理的结构化数据格式了。还有一些数据本身只由少量的结构化信息构成,不需要有独立的二进制数据块。
很多游戏引擎会提供通用的结构化数据的持久化方案。有些使用非文本,如 Unity ,有些直接使用文本格式。我们更青睐文本格式,但我们没有选择 json , yaml 这些流行的格式,而是自己重新实现了一个 datalist 。
json 虽然因 javascript 而流行,并相当简单,但明显未经过设计。json 是一个 javascript 对象的简单序列化方案,直接映射的是 javascript 自己的数据模型。对于非 javascript 语言,例如 Lua ,数据模型的细微差异带来的问题会被放大。未经扩展的 json 不支持注释、书写别扭(例如字典的 key 必须加引号),需要太多的花括号表达层级……
我们仔细考虑过 yaml ,它更加规范,格式丰富,解析也不算太难。但最终我还是放弃了它:因为它的格式过于丰富,带来不必要的复杂度和潜在的歧义。另外,解析过程也不够高效。似乎没有必要为那些花哨的用不上的特性无端损失性能。毕竟,我们会在引擎的各个边角使用它。
有许多人热爱 lisp like 的方言。因为 lisp 是一种被严肃设计过的语言,一切都是 list 被反复的证明可以轻松的表达一切结构化数据。Paradox 公司的一系列游戏就采用了一种类 lisp 的数据格式 ,它影响了 datalist 的设计。同时,yaml 的语法也颇能博得好感,在一定程度上,datalist 看起来像是一个简化版的 yaml 。
手写一个这样的解析器并不复杂,且我们只需要面对 Lua 一种语言即可。也曾经有许多 Lua 爱好者直接使用 Lua 源码表达数据,但经过权衡,我还是决定一个独立的解析器更为严谨,比 Lua 本身的解析器更为高效。
Ant 在所有需要表达持久化数据的文件中,都使用了 datalist 格式。有些数据有专有的后缀名,比如 .texture 是一个贴图描述文件,但文件格式是 datalist 的。还有一些文件没有专有的后缀名,这类文件一律使用 .ant 作为后缀,这可以方便开发者配置文本编辑器。
下面是一个预制件的示例,可以一窥 datalist 格式大致的样子:
# /pkg/ant.test.simple/resource/light.prefab
---
policy:
ant.sky|skybox
ant.render|ibl
ant.render|simplerender
data:
visible_state: main_queue
scene: {}
ibl:
LUT:
size: 256
intensity: 30000
prefilter:
size: 128
source:
tex_name: /pkg/ant.resources.test/sky/colorcube2x2.texture
facesize: 512
material: $path ./skybox.material
render_layer: "background"
simplemesh: false
skybox:
facesize: 512
---
policy:
ant.render|light
data:
scene:
r: {0.5, 0.0, 0.0, 0.8660253}
t: {0, 10, 0, 1}
light:
color: {1, 1, 1, 1}
intensity: 120000
intensity_unit: lux
type: directional
make_shadow: true
visible: true
用 # 注释; --- 分割列表 ; 同时支持用换行缩进和花括号表示树结构的数据;字符串的引号是可选的,在没有歧义时可以省略;内置数字和布尔类型,等等。