今天看到 @Funny David 的文章从Odin插件聊基于元数据的编辑器实现,这篇文章非常好。

最后评论部分有朋友提到一个配置的麻烦点,故有感而发,说一点我的思路和处理方法:




unity游戏数据修改 如何修改unity游戏数据_数据


当我们在使用unity自带的数据序列化时,当我们需要更改字段名,字段类型,甚至整个配置的数据格式,层级等的时候,会面临丢失数据的问题。而我们在制作游戏的过程中,根据需求的改变,我们经常都要面对这样的问题,需要及时地重构我们的配置数据,代码结构。

简单罗列一下可能的需求如下:

  • 更改字段名字
  • 更改字段类型,例如从int 转为string,从 int 转为Enum等
  • 更改容器类型,例如从Array或list存储,转换为Dictionary或Hashset
  • 更改数据的保存层级,做一些封装,例如原本Class A,包含字段a, b, c, d,现在要做一次封装,改为,包含a,b, E, Class E 包含c, d
  • 更改数据的含义,例如,原来a字段保存的是百分比数值,根据需求,需要保存为万分比数值

上述的需求在我的当前项目中都遇到过,随着我们 技能编辑器 功能的逐步复杂和修改,优化,无可避免。

我们要做到,让数据结构合理符合我们的逻辑需求的更改,还要保证重构中不要有数据丢失,不能因为我们改了一下代码,让策划承受大量配置重配的代价。

在最开始,我们就注意了这个问题,并考虑了解决方案,主要思路可以概括为:

  • 自定义序列化数据,和unity 序列化分离(可以参考我之前的文章 技能编辑器)
  • 资源的id化,资源和数据分离

举个例子,我们使用protobuf来序列化配置,保存为一个二进制配置文件,代码定义例如:


unity游戏数据修改 如何修改unity游戏数据_序列化_02


protobuf根据ProtoMember设置的id来序列化和反序列化字段,故修改字段名完全不影响数据。

如果涉及到后面几种变化的需求,就是统一的处理方式,举个例子:


unity游戏数据修改 如何修改unity游戏数据_unity 引用prefab_03


例如,我们需要将上图的uint cmdExtendFrame,修改为List<uint> cmdExtendFrames,步骤如下

1,添加一个新ProtoMember id的新字段


unity游戏数据修改 如何修改unity游戏数据_unity 引用prefab_04


2,在protobuf 反序列化后 将老字段和新字段做转换赋值


unity游戏数据修改 如何修改unity游戏数据_unity游戏数据修改_05


3,最重要一步:先反序列化原数据,再序列化为新数据,并保存为新的数据。

反序列化时,会执行步骤2的赋值处理(这里可以根据需求做任意处理)。

序列化为新数据,新数据会含有cmdExtendFrame和cmdExtendFrames两个字段的数据。

可以自己写个通用的数据处理代码,将涉及到的所有配置(例如所有的角色配置)全部执行一遍反序列化,重新序列化,保存的操作。其实就是将所有配置读取,再保存。

4,删除老字段和反序列化处理代码,得到更新后的代码


unity游戏数据修改 如何修改unity游戏数据_unity游戏数据修改_06


通过上述四步,可以任意修改配置数据的类型,格式等,是游戏推进不断重构的利器。并且不会导致任何数据的丢失。配合Odin或AdvancedInspector这类插件,能非常方便地做一些编辑器。

最后说一下:

  • 对于一些prefab,贴图,材质等unity内置资源,我们可以通过序列化资源的GUID来保存,也可以通过自定义的逻辑来维护一个资源id和资源本身的关系,来保证资源引用的保存,此处不展开了。
  • 使用protobuf序列化的坏处是非明文数据,大家可以考虑序列化为json,xml或者其他任意格式,平衡可读性,反序列化性能等各种因素。此处要注意的就是序列化数据不要依赖字段名字,而是字段id,例如protobuf的ProtoMember id。