游戏服务器设计之属性管理器

  游戏中角色拥有的属性值很多,运营多年的游戏,往往会有很多个成长线,每个属性都有可能被N个成长线模块增减数值。举例当角色戴上武器时候hp+100点,卸下武器时HP-100点,这样加减逻辑只有一处还比较好控制,如果某天有个特殊功能当被某技能攻击时,角色武器会被击落,这样就会出现减数值的操作不止一处。如果逻辑处理不当,比如击落的时候没有恰当的减数值,再次穿戴武器就导致属性值加了两边,也就是玩家经常说的刷属性。这种bug对游戏平衡性影响很大,反响很恶劣,bug又很难被测试发现。本文将介绍一种管理属性的思路,最大限度的避免此类bug,如果出现bug,也能够很好的排查。

设计思路

  刷属性bug的核心原因是某功能的模块数值加了N次,所以各个模块加的属性要被记录,加过了必须不能重复加。设计这样的数据结构。

//!各个属性对应一个总值
//!各个属性对应各个模块的分值
template<typename T>
class PropCommonMgr
{
public:
typedef T ObjType;
typedef int64_t (*functorGet)(ObjType);
typedef void (*functorSet)(ObjType, int64_t);
struct PropGetterSetter
{
PropGetterSetter():fGet(NULL), fSet(NULL){}
functorGet fGet;
functorSet fSet;
std::map<std::string, int64_t> moduleRecord;
};
void regGetterSetter(const std::string& strName, functorGet fGet, functorSet fSet){
PropGetterSetter info;
info.fGet = fGet;
info.fSet = fSet;
propName2GetterSetter[strName] = info;
}
public:
std::map<std::string, PropGetterSetter> propName2GetterSetter;
};
  1. 关于数据结构的get和set,我们为每个属性命名一个名字,这样处理数据的时候会非常方便(比如道具配增加属性等等),角色属性有很多种,这里不能一一定义,所以属性管理器只是映射属性,并不创建属性值。通过regGetterSetter接口,注册get和set的操作映射。为什么不需要提供add和sub接口能,因为add和sub可以通过get和set组合实现。get和set的接口实现如下:
int64_t get(ObjType obj, const std::string& strName) {
typename std::map<std::string, PropGetterSetter>::iterator it = propName2GetterSetter.find(strName);
if (it != propName2GetterSetter.end() && it->second.fGet){
return it->second.fGet(obj);
}
return 0;
}
bool set(ObjType obj, const std::string& strName, int64_t v) {
typename std::map<std::string, PropGetterSetter>::iterator it = propName2GetterSetter.find(strName);
if (it != propName2GetterSetter.end() && it->second.fSet){
it->second.fSet(obj, v);
return true;
}
return false;
}
  1. 关于add和sub,前面提到要避免刷属性,就必须避免重复加属性。所以每个模块再加属性前必须检查一下是否该模块已经加了属性,如果加过一定要先减后加。因为每次模块加属性都记录在属性管理器中,那么减掉的数值一定是正确的。这样可以避免另外一种常见bug,如加了100,减的时候计算错误减了80,也会积少成多造成刷属性。add和sub的代码如下:
int64_t addByModule(ObjType obj, const std::string& strName, const std::string& moduleName, int64_t v) {
typename std::map<std::string, PropGetterSetter>::iterator it = propName2GetterSetter.find(strName);
if (it != propName2GetterSetter.end() && it->second.fGet && it->second.fSet){
int64_t ret =it->second.fGet(obj);
std::map<std::string, int64_t>::iterator itMod = it->second.moduleRecord.find(moduleName);
if (itMod != it->second.moduleRecord.end()){
ret -= itMod->second;
itMod->second = v;
}
else{
it->second.moduleRecord[moduleName] = v;
}
ret += v;
it->second.fSet(obj, ret);
return ret;
}
return 0;
}
int64_t subByModule(ObjType obj, const std::string& strName, const std::string& moduleName) {
typename std::map<std::string, PropGetterSetter>::iterator it = propName2GetterSetter.find(strName);
if (it != propName2GetterSetter.end() && it->second.fGet && it->second.fSet){
int64_t ret =it->second.fGet(obj);
std::map<std::string, int64_t>::iterator itMod = it->second.moduleRecord.find(moduleName);
if (itMod == it->second.moduleRecord.end()){
return ret;
}
ret -= itMod->second;
it->second.moduleRecord.erase(itMod);
it->second.fSet(obj, ret);
return ret;
}
return 0;
}
int64_t getByModule(ObjType obj, const std::string& strName, const std::string& moduleName) {
typename std::map<std::string, PropGetterSetter>::iterator it = propName2GetterSetter.find(strName);
if (it != propName2GetterSetter.end() && it->second.fGet && it->second.fSet){
int64_t ret =it->second.fGet(obj);
std::map<std::string, int64_t>::iterator itMod = it->second.moduleRecord.find(moduleName);
if (itMod != it->second.moduleRecord.end()){
return itMod->second;
}
}
return 0;
}
std::map<std::string, int64_t> getAllModule(ObjType obj, const std::string& strName) {
std::map<std::string, int64_t> ret;
typename std::map<std::string, PropGetterSetter>::iterator it = propName2GetterSetter.find(strName);
if (it != propName2GetterSetter.end() && it->second.fGet && it->second.fSet){
ret = it->second.moduleRecord;
}
return ret;
}

  如上代码所示,addByModule和subByModule必须提供模块名,比如穿装备的时候加血量:addByModule('HP', 'Weapon', 100),而卸下武器的时候只要subByModule('HP', 'Weapon'),因为属性管理器知道减多少。

总结

  1. 属性提供一个名字映射有很多好处,比如装备配属性,buff配属性的,有名字相关联会特别方便
  2. 提供一个get和set接口的映射,这样属性管理器就和具体的对象的属性字段解耦了。即使是现有的功能模块也可以集成这个属性管理器。
  3. 属性的add和sub操作,都在属性管理器中留下记录,这样即使出现问题,通过getByModule getAllModule两个接口亦可以辅助查找问题。
  4. 属性管理已经集成到H2Engine中,github地址: https://github.com/fanchy/h2engine

H2Engine游戏服务器设计之属性管理器的更多相关文章

  1. h2engine游戏服务器设计之聊天室示例

    游戏服务器设计之聊天室示例 简介 h2engine引擎建群以后,有热心网友向我反馈,想尝试h2engine但是没有服务器开发经验觉得无从入手,希望我能提供一个简单明了的示例.由于前一段时间工作实在忙碌 ...

  2. 游戏服务器设计之NPC系统

    游戏服务器设计之NPC系统 简介 NPC系统是游戏中非常重要的系统,设计的好坏很大程度上影响游戏的体验.NPC在游戏中有如下作用: 引导玩家体验游戏内容,一般游戏内有很多主线.支线任务,而任务的介绍. ...

  3. JMeter学习(二十五)HTTP属性管理器HTTP Cookie Manager、HTTP Request Defaults

    Test Plan的配置元件中有一些和HTTP属性相关的元件:HTTP Cache Manager.HTTP Authorization Manager.HTTP Cookie Manager.HTT ...

  4. 【jmeter】HTTP属性管理器HTTP Cookie Manager、HTTP Request Defaults

    Test Plan的配置元件中有一些和HTTP属性相关的元件:HTTP Cache Manager.HTTP Authorization Manager.HTTP Cookie Manager.HTT ...

  5. HTTP属性管理器详解

      1)HTTP Cache Manager 2)HTTP Cookie 管理器 3)HTTP 信息头管理器 4)HTTP 授权管理器 5)HTTP 请求默认值 为什么会有这些http属性的配置元件? ...

  6. HTTP属性管理器 初探

      1)HTTP Cache Manager 2)HTTP Cookie 管理器 3)HTTP 信息头管理器 4)HTTP 授权管理器 5)HTTP 请求默认值 为什么会有这些http属性的配置元件? ...

  7. <转>jmeter(十九)HTTP属性管理器

    本博客转载自:http://www.cnblogs.com/imyalost/category/846346.html 个人感觉不错,对jmeter讲解非常详细,担心以后找不到了,所以转发出来,留着慢 ...

  8. jmeter(十九)HTTP属性管理器

    jmeter是一个开源灵活的接口和性能测试工具,当然也能利用jmeter进行接口自动化测试.在我们利用它进行测试过程中,最常用的sampler大概就是Http Request, 使用这个sampler ...

  9. JMeter学习(二十四)HTTP属性管理器HTTP Cookie Manager、HTTP Request Defaults(转载)

    转载自 http://www.cnblogs.com/yangxia-test Test Plan的配置元件中有一些和HTTP属性相关的元件:HTTP Cache Manager.HTTP Autho ...

随机推荐

  1. ettercap的中间人欺骗

    环境准备:kali系统 因为kali系统自带ettercap,比较方便, 不需要安装 ifcofing命令查看当前网关 ,当前的IP是: 172.16.42.1 查找局域网所有主机 通过netdisc ...

  2. Best time to buy and sell stocks IV

    题目 https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iv/ Say you have an array for which ...

  3. XML约束(3)

    本节要点: 了解XML的约束模式 了解DTD 了解Schema 1 了解XML的约束模式 计算机程序在处理XML文档之前,必须能够解析出XML文档的内容中各个元素的相关信息.将解析出来的信息再交给下游 ...

  4. springboot定时任务——整合Quartz

    http://blog.csdn.net/liuchuanhong1/article/details/60873295 http://blog.csdn.net/lyg_come_on/article ...

  5. 什么是PMI

    项目管理协会 PMI PMI是世界领先的非盈利会员协会的项目管理专业机构 ,在全球185个国家有70多万会员和证书持有人.此外,PMI还是多个英文短语的缩写,较为著名的是采购经理指数PMI. 项目管理 ...

  6. sqlser 2005 使用执行计划来优化你的sql

    一:sqlserver 执行计划介绍    sqlserver 执行计是在sqlser manager studio 工具中打开,是检查一条sql执行效率的工具.建议配合SET STATISTICS ...

  7. 深入理解计算机系统_3e 第五章家庭作业 CS:APP3e chapter 5 homework

    5.13 A. B. 由浮点数加法的延迟,CPE的下界应该是3. C. 由整数加法的延迟,CPE的下界应该是1. D. 由A中的数据流图,虽然浮点数乘法需要5个周期,但是它没有"数据依赖&q ...

  8. J2EE: JCA (Java Connector Architecture) [转]

    JCA (J2EE 连接器架构,Java Connector Architecture)是对J2EE标准集的重要补充.因为它注重的是将Java程序连接到非Java程序和软件包中间件的开发.连接器特指基 ...

  9. idea 配置热部署

    1. 2.

  10. python字典的操作

    思维导图如下 1.字典的增加 dic1={'name':'wujie','age':18,'gender':'男'} dic1['profession']='python全栈' dic1.setdef ...