存档与瞬态
Unciv 是一款万物互联的游戏。例如,每个地图单位都属于一个文明,位于一个地块上,可以有几个晋升,并"继承"自一个基础单位。
保存游戏状态时,我们希望它尽可能小——所以把保存的信息限制到最低限度。我们保存名称而不是指针,任何可以重新计算的东西都不保存。
但在运行时,我们需要这些链接以保证性能——如果能在对象上保存引用,何必每次都做查找?
因此,类在序列化时被冻干,在运行时再补水。由于这些字段在 Kotlin 中标记为 @Transient,我们把补水函数命名为 setTransients。
以地图单位为例。如何计算该单位的 uniques?它们可能来自多个地方:
- 基础单位的 uniques
- 晋升
- 全文明范围的 uniques
所以从存档中我们得到文明名、单位名和晋升名;运行时我们想要的是对文明、基础单位和晋升的引用。
我们可以通过在游戏中查找文明、在规则集中查找单位和晋升来得到它们。
文明本身——一个游戏对象——引用国家——一个规则集对象,这是另一个链接。基础单位引用单位类型,又是一个链接。
国家、基础单位和晋升都包含 uniques——以字符串保存。为了性能,运行时它们也被保存为 Unique 实例——包含 unique 类型、参数、条件等。
除了每个规则集对象保留一份"已补水"的 uniques 列表之外,单位的 uniques 不经常变化——所以我们还可以再加一层缓存:保存单位的所有 uniques,并在每次变化时重建它。
所有这一切对性能都至关重要——Unciv 的目标是在老破小设备上也能跑,而即使是哈希查找,在频繁执行时也很昂贵,更不用说解析 Unique 所需的 Regex 了!