背包以及装备模块封装的思考
起初由于装备和道具要分别进行管理,并且装备存在随机属性的问题,最直接的思考就是生成物品UID的方式来进行区分,并且通过UID来作为map的主键来进行存储。现在仔细思考下来,其实不用map,改用std:array数组来存储会更好一些。UID只需要作为装备的唯一标示信息就可以了,用位置信息来作为唯一性的操作定位。可能说起来有点空白。看下图,原来是这么设计的:
EquipManager和ItemManager分别管理了道具和装备的内存信息以及具体的操作,并且PackageManager来做位置信息的生成,UID生成排序等工作并不包含内存信息的副本还是借助于EquipManager和ItemManager来操作。初看下来也没有什么大问题,加上由于策划将装备,道具分成了两个表并且提出了一个通用的基类给PackageManager提供了公共的函数接口,存储的数据结构采用map方式,为了操作方便用UID来做主键,取操作同样也会很便捷,并且也可以装上的装备位置信息为0,这样内存信息还是保持在EquipManager方便了内存的管理。
但是面对了几个问题。
1.背包的排序问题,由于map本身并没有排序的性质,不能说不能排只是没数组方便,所以还是在排序的时候,暂时放入了一个数组当中
2.放入一个背包物品的时候,位置信息生成,UID的时候PackageManager来管理,但是UID的生成还好理解,位置信息的生成就感觉很不自然
3.PackageManager其实具有了ItemManager以及EquipManager的一些特征了,只是采用了调用的方式进行了操作。
反思:
1.为了保证服务器类层级设计的扁平化和模块的独立性上,PackageObj反而使得继承树复杂化了,不是一个很好的代码范例结构。应当让Item和Equip各自持有各自的背包信息数据,做到和数据库策划表的一一对应。这样的策划的更改或者数据库的更改优化,留有的更改余地更大,不会存在嵌套的问题。
2.PackageManager具有了ItemManager和EquipManager这两者的所有特征,只是逻辑层面上的理解进行了划分将函数分离了出来。所以PackageManager应该是ItemManager和EquipManager的友元函数声明,反而比在PackageManager持有一份两个对象指针效率更高也更加的优化。
3.采用的数据结构而言,采用map显然得不偿失。对单个玩家而言背包的位置信息就具有了唯一性,然而UID的唯一性是在同种装备不同属性的逻辑区分,为其他地方的取操作提供便利。装备在装上卸下的时候,玩家身上的那个已装备格子的数组应该保持该装备的所有信息,在装备当中添加相应的字段做数据库存储,背包数组就不再保持这个已经不在背包里的装备数据了。这么做有以下几个优势
(1),使用数组排序比用map排序效率更高,更方便
(2),在选择角色界面,不需要分开查找数据库,那个时候可以保证数据库查询的步调统一,而不需要得先查询所有的装备信息做取操作或者做一个单独查询的操作。
(3),数组容量直接反应背包大小,不需要循环遍历map来做统计
(4),存储方式天然带位置信息,不需要额外算法的生成,空位也更容易找出
(5),在做道具叠加问题涉及到的背包容量问题时,背包容量问题以及空位问题更容易获取,减少了一些不必要的代码
需要注意的几点
(1),数组的越界检查
(2),背包里面的数组以及玩家身上的装备栏数组的对象释放
(3),位置是否重复的排查
(4),背包是一个连续空间的内存结构,当背包物品存在不连续存放的情况的时候,采用vector形式存储就会存在问题,应当采用array数组来存储。提前分配背包的内存情况。
思考:
1.应当对应策划表的形式,并给出程序上建议。保证策划填表的阅读性的基础上,程序在抽象上尽量做到对应,并且直接关系到数据库的列设计
2.注意类设计的扁平化,尽量少的继承,防止后续代码维护的不方便,出现“头重脚轻”的问题
3.存储内存的数据结构,不一定万事而皆准的采用“通用”的结构,而是应当根据实际情况进行调整,从而能够高效的反映到算法层面优化上来。
4.数据库设计尽量仔细考虑一下反范式的设计方式,游戏数据库而言主要做的还是登陆时候的查询,在游戏过程当中的更新,删除,插入。然而游戏过程当中查询很少,登陆查询在多分表的情况下反而防止了一个表过大的情况,插入,更新,删除等由于数据库表设计的反范式更加的方便操作了。但是列明显还是增加的,但是对于单表查询而言比起一个表的行数几何级递增的情况下,反范式设计的,通过逻辑上的区分纵向分表不见得不是一种优势,这样的设计也变相说明了Key-value数据库在游戏数据库上级当中的设计方式!