《天天酷跑》游戏后台结构体标准化探索

发表于2015-06-05
评论1 4.42w浏览

想免费获取内部独家PPT资料库?观看行业大牛直播?点击加入腾讯游戏学院游戏程序行业精英群

711501594

在程序设计领域,程序就是数据结构加算法的理论早已深入人心。在手机游戏的后台系统中,数据结构就是各式各样的structclass,遍布于数据存储(DB)、内存(业务逻辑)、网络(C/S交互协议),算法就是各种基于数据的业务处理逻辑了。

 

手机游戏后台数据结构有一些特点:

l  以整形数据为主,字符串类型比例很少,绝大部分数据可以用整形(直接或间接)表示。

l  相对大型端游,数据结构相对简单,绝大多数可以通过2-3层结构来抽象。

 

基于以上特点,《天天酷跑》后台设计了一套基于属性系统的结构体标准化表示方法,用于尽量统一C/S协议、逻辑处理和后台数据存储中用到的各式数据结构。

 

常规结构体定义

         常规的结构体定义过程,就是程序员把用到的各种成员很直观地定义在代码(或者xml之类的模板中)中。如下图所示:

http://km.oa.com/files/post_photo/890/203890/7a55c611b33b352f6f4705a740996a58.jpg

1. 常规结构定义

 

1的结构体定义简单、直接,但也有一些不太好的地方:

l  结构体看上去很类似,字段类型少量变化和变量名称的不同而已。定义时也是到处拷贝后稍作修改,就变成其他结构体的定义。

l  同一对象在程序中对应多个版本的结构定义,例如游戏中的“角色”可能在DB、逻辑、C/S协议中有多个版本的定义,在使用的过程中需要各种对接和转换。

l  结构体随着需求变化时,特别是在C/S协议中共用的结构体,需要不断升级协议的版本号,增加维护和兼容的成本。

 

如果把这些类似的结构体定义统一起来,施以标准化实施,并辅以统一的接口(算法),是否可以既避免上述的弊端,并能够获取到额外的收益呢?

本人在《天天酷跑》的后台开发过程中,进行了一些探索,并使用属性系统作为结构体标准化的载体来实现。

属性系统介绍

由于手机游戏后台中无法用整形(以及无法用整形类型间接)表示的数据占比非常之低,经过对比,选择uint32作为基础的数据类型。

http://avocado.oa.com/fconv/files/201406/e0d8fc4d6de84a5aeb10c5994256b34a.files/image002.jpg

2. 属性系统定义

        

属性系统架构上分为3个层次:

l  属性:Key/Value结构表示,Key是属性IDValue是对象的值。用于表示对象的一个属性。

l  属性堆:多个属性组成一个堆,有一个堆ID。用于表示不同对象实例。

l  属性仓库:多个属性堆组合成一个属性仓库,用于表示对象的数组。

 

http://avocado.oa.com/fconv/files/201406/e0d8fc4d6de84a5aeb10c5994256b34a.files/image003.jpg

3.属性系统层次结构

属性系统特点

属性系统对结构体(对象)的定义进行了标准化,有如下一些特点:

l  不支持字符型成员,需要单独定义并与属性系统进行关联使用。

l  uint32类型表示所有的成员类型,其他类型需要转换。例如浮点转定点,64位整形用232位表示等。

l  本质上是key/value的二维数组结构。

l  可以通过ID(属性堆ID和属性ID)来随机读写成员,提供脚本化访问成员的能力。

l  标准化后可以使用统一的接口操纵(读写、序列化/反序列化等)。

 

在项目用使用属性仓库来表示各种游戏对象,例如角色的数值(类型、等级等)用AttributeStruct表示;单个角色对象通过AttributePile表示,角色对象之间通过属性堆 ID区分;玩家身上的角色列表通过AttributeWarehouse表示。

 

属性仓库的读写有3种方式:

l  一维索引。索引到某个属性堆,通过属性ID遍历读写堆中的属性成员,或者,根据属性堆ID 遍历到某个属性堆,通过索引访问堆中的属性成员。

l  二维索引。通过堆索引和属性索引来读写属性成员。

l  遍历式。完全获取不到索引信息时,使用属性堆ID和属性ID遍历整个属性仓库来对属性成员,性能低但功能强大,可以通过配置2ID的方式读写任何成员。

 

DB中的属性系统

属性系统在DB中的应用,主要是玩家信息表中用于表示各种游戏核心对象的属性仓库字段。如下图:

http://avocado.oa.com/fconv/files/201406/e0d8fc4d6de84a5aeb10c5994256b34a.files/image004.jpg

4. DB中的属性仓库

 

玩家的基本账户信息、角色、坐骑、宠物、精灵、背包等,在DB玩家信息表中的载体,都是属性仓库。它们之间的不同,只是二维数组的2个维度的大小不同而已。例如角色仓库的属性堆数量少,但单个角色的属性数量多。而宠物的属性堆数量非常多,但单个宠物的属性数量少。业务使用不同的维度大的属性仓库来表示不同对象,主要用于减少空间的浪费。

 

代码中的属性系统

         属性系统结合手机游戏后台程序设计的特定,在代码层级上,通常分为三层封装:

l  API层:通过模板封装一些基本的属性系统操作接口和算法。

l  逻辑层:直接操作DB中的属性仓库结构,封装具体业务的各种逻辑。

l  业务层:封装逻辑层的接口,加上日志流水、属性变化通知、DB保存等完整功能,供其他业务系统直接调用,屏蔽细节实现。

http://avocado.oa.com/fconv/files/201406/e0d8fc4d6de84a5aeb10c5994256b34a.files/image005.jpg

5.属性系统的代码层级

 

         三层架构的好处是结构更加清晰,接口功能独立并易于维护。另外中间的逻辑层由于只操作DB字段,可以跨进程代码共享,例如idipsvr拿到玩家的DB字段后,引用gamesvr的逻辑层代码构造管理器,进行操作而不需要知道具体的业务细节。

 

C/S协议中的属性系统

         客户端实现一套属性系统的封装后,就可以与服务器统一使用属性系统来解析玩家的数据了。在C/S协议中,当客户端请求玩家数据时,服务器会下发整个经过TDR打包后的属性仓库的结构。客户端TDR解包后,即可交给属性系统封装进行解析,然后再交由各种业务逻辑自己使用。

         有以下几个好处:

l  客户端和服务器共享同一套数据格式,便于理解和数据处理。

l  减少协议升级的次数。基本上需求的增加和修改,只需增加一个属性定义交由客户端逻辑解析即可,不需要在协议中增加字段来支持。

l  存储系统的数据与协议返回的玩家数据都使用属性系统表示时,处理比较简单,dump出来甚至直接返回给客户端即可解析。

下图是使用属性系统来定义协议的例子。结算协议包含了大量游戏内统计数据,用于单局结算、数据校验、奖励发放、反作弊等大量逻辑运算。

 

http://avocado.oa.com/fconv/files/201406/e0d8fc4d6de84a5aeb10c5994256b34a.files/image006.jpg

6.游戏结算协议

 

属性系统与名称服务

         名称服务(Name Service)用于记录属性名称、属性ID和属性所属对象之间关系。使用之前需要进行属性信息注册,例如< X_ATTRATTR_GOLDNUM,“ATTR_GOLDNUM>三元组就定义了玩家金币数这个属性,属性属于X_ATTR范围,属性IDATTR_GOLDNUM,属性名为“ATTR_GOLDNUM”。

         注册属性信息到名称服务器后,就可以用配表的方式完成多功能,当然前提是底层的命令行解析和相应的业务逻辑代码调用。

         http://avocado.oa.com/fconv/files/201406/e0d8fc4d6de84a5aeb10c5994256b34a.files/image007.jpg

         7.公式化的商店配置表    

总结

通过属性系统,有条件性地标准化了前后台协议、后台业务逻辑和DB中数据存储的结构体表示,并通过一套封装的接口来读写结构体的成员,为开发的中后期带来非常多的便利,早期因为接口封装不完善、面向对象的思维方式等带来了一些沟通的成本。

通常标准化和灵活性是矛盾的双方,标准化之后就要求按照既定的标准(或者限制)来实施,在灵活性要求很高的场景(例如结构层级复杂、变化频繁的业务需求),会带来不便。但从《天天酷跑》后台的探索情况来看,属性系统非常适合在手机游戏中标准化表示各种结构体和对象。

 

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引

游戏学院公众号二维码
腾讯游戏学院
微信公众号

提供更专业的游戏知识学习平台