引言:2015年11月从上上家公司离职后,开始了独立开发的历程。引擎改用了流行的unity,但是语言还是选择了最喜欢的lua,使用的Slua,没有使用luajit,而是选择了原生Lua(5.3.4),中途历经各种坎坷,17年还中断了一年,现在游戏终于是快要上线了,打算写一些开发手记,记录下这段时间对Lua使用的一些提升。
数据持久化
游戏是款单机游戏,当玩家选择存档或者游戏自动存盘时,需要将整个游戏的进程信息全部保存下来,如果每一个模块都要全部手写一遍代码就会让整个过程变得很繁琐。为了简单的搞定这个功能,于是想到了通过将数据与操作严格的进行分离,然后将存储功能简单的变为将存储数据的表做一个持久化,而读取功能就变成了,将存储的的数据表读出来直接覆盖掉当前的数据表即可。
简单画个图表示:
通过机制保证数据的分离
lua本身是脚本语言,是弱类型的,没有办法保证这点,所以需要建立一套机制来保证这点(以下API的名字都是自定义的,全凭个人喜好)。
控制操作API。所有需要持久化的模块必须从一个基础模块继承(
简单使用Lua实现类的继承
)而来,基础模块里实现了Get(key)和Set(key, value)这两个接口, 所有需要持久化的数据必须使用这2个接口来进行操作(否则会直接写入表本身,无法被持久化),并可以考虑在Set接口里做显性类型检查(避免写入userdata,function等无法持久化类型的变量)。
基础模块部分使用一个表来存储所有数据,这样存储的时候仅需要将这个表做持久化就OK了,并将这个表进行隐藏(Lua实现简单封装),避免平常操作时误修改数据区域数据。(使用GetData() 和 SeData(data)这两个接口来获取完整的数据区域的数据和实现数据覆盖)。
最后为了能尽量方便使用,可以通过设置metatable的__index,在用户试图使用XX.XX的方式访问数据且找不到同名变量时,调用Get函数,使访问数据使用起来与原生数据一致。
用个简单的图来表示下:
表格序列化
最后这个就简单了,用pairs遍历表格,然后根据类型将其以字符串形式格式化输出并保存即可。
PS:注意针对string类型的变量,使用string.format的话,要用%q。
完成上述功能后,一个典型的Save/Load功能就变成了以下样式:
Save:
通过GetData获取存盘数据。
将获取到的table序列化获得生成字符串。
将字符串保存。
Load:
读取保存的字符串
将字符串反序列化(使用lua的load系列函数)获得一个table
通过SetData将这个table设置覆盖。
最后的最后,采用这个方法,在模块的开发代码里,需要持久化的代码一定要通过指定接口来设置,否则就会出现部分数据丢失了= =
下一篇:Lua热更新(应部分朋友要求)
领取专属 10元无门槛券
私享最新 技术干货