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

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

  游戏中角色拥有的属性值很多,运营多年的游戏,往往会有很多个成长线,每个属性都有可能被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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一地图管理:(MapManager 包) 这个包下有很多的类用于地图上的管理。包括基础类Map,功能加载地图, Map 类下有以下的方法: 实现方案: 方案1.元素构成的地图 二.道具管理(ItemManager 包) 这个包是负责角色的使用物品包括道具(),武器(攻击) ,装备(防御) 三.声音管理(SoundManager 包) 这个包负责声音的特效,加载,卸载,场景音乐播放停止暂停,调整音量大小, 获取音量大小,还有一些显示的问题 四.角色管理(CharacterManager) 这个包负责创建不同的主角和配角,包括创建角色,设置角色属性,获取角色属性, 删除角色,分派角色的常见行为和技能等,设置怪物Ai。写一个接口或者建议基类共享角 色中的属性和方法(当创建主角和创建怪物的时候,也一样可以使用这样方法); 制作使用的设计模式: Strategy 模式; 工厂模式; 单例模式; 外观模式. Decorator 模式: 五.事件管理(GameEvent 包) 划分不同的情节,用不同的类来描述情节。包括不同的任务。 初步想法:写出不同的事件,每一个事件就关于一个故事情节,引发场景动画,引发一些 对白言语等等; 八.通信管理(CommunionManager 包): socket 通信,连接服务器,读取服务器的数据。读取和写入数据库; 打斗时候的各种情况记录 网络游戏中最重要的一个地方,这个关于通信的是影响到游戏是否能够顺利进行。 九.操作管理(ControlManager包): 用于设定游戏的操作键盘,和鼠标,实现与键盘鼠标进行交互管理用户输入的数据 十.游戏管理(GameManager包): (暂定) 包括游戏设定,音乐大小开关,场景特效开启调整画面质量等。查看个人信息、技能 表、自动保存问题,玩家在游戏中状态(包括时间的统计) 基本方法 GameSave();//游戏保存的问题。当玩家在游戏中死了的时候,应该如何去保存当前的数据 SoundOn(); //游戏开关全局 //画面质量,高,中,低 十一游戏Ai管理(AiManager 包 这里包括一套关于Ai 的算法机制,初步的一些想法: 包括一些有限状态机的设计模型,或者在有限状态机基础上去加入随机性去。 深入的技术:神经网络,包括BP 算法 共32页PDF
### 回答1: Cocos Creator是一款集成化游戏开发工具,它可以帮助开发者快速构建游戏。Cocos Creator框架文档就是为这个工具而设计的,它详细介绍了框架的各个方面: 1.游戏组件: Cocos Creator的游戏组件是构成游戏逻辑的基本单位,如:精灵,动画,音效,等等。框架文档详细介绍了这些游戏组件的实现方式和使用方法。 2.游戏场景: 场景是游戏中不可或缺的一部分,游戏场景的绘制、布局、切换等功能在Cocos Creator中的实现方式和使用方法也被框架文档详细介绍了。 3.游戏节点管理: Cocos Creator中管理游戏节点的方式是通过层次结构进行管理,框架文档详细介绍了如何使用节点管理器来组织游戏节点。 4.贴图管理: 游戏中的图片资源在Cocos Creator中管理方式与其他游戏开发工具略有不同,框架文档详细介绍了如何使用贴图管理器来管理游戏中的图片资源,以及如何使用它们。 5.数据存储: 在游戏开发过程中,需要存储很多游戏数据,Cocos Creator提供了一套数据存储方案,框架文档详细介绍了如何使用数据存储器来存储、读取数据。 总之,框架文档为开发者提供了一个非常完整的Cocos Creator游戏开发体验,帮助开发者更好、更快地完成游戏开发工作。 ### 回答2: Cocoscreator是一款广泛使用的游戏开发工具,它支持多种平台,包括Web、iOS、Android等。针对这款工具,设计游戏框架文档是非常必要的。 游戏框架文档是对游戏开发中使用到的各种类、接口、函数等进行的系统化整理和说明,旨在帮助开发人员更加快速、高效地完成游戏的开发。针对cocoscreator,要编写的游戏框架文档必须包括以下内容: 1.场景和节点 这是cocoscreator的核心组成部分,开发人员应该了解和掌握它们的使用方法。文档可以详细介绍场景、节点的作用、属性和接口等,以及它们在开发过程中的使用场景和注意事项。 2.资源管理 在游戏开发中,资源管理是非常重要的,它关系到游戏的加载速度和性能。因此,游戏框架文档应该对资源加载、复用、释放等方面进行详细说明。 3.事件处理 事件处理是cocoscreator的另一大特色,文档中应该详细说明事件的相关接口和使用方法,包括监听事件、派发事件等。 4.动画和动作 cocoscreator提供了丰富的动画和动作接口,可以制作出生动、有趣的游戏效果。文档中应该详细介绍动画和动作的使用方法和属性等。 5.游戏逻辑和交互 游戏逻辑和交互是游戏的核心部分,文档应该对游戏逻辑和交互流程进行详细说明,以及说明与其他模块的关联和调用方式。 综上所述,设计cocoscreator游戏框架文档对游戏开发至关重要,只有编写出清晰、详尽的文档,才能帮助开发人员快速完成游戏开发,提高开发效率和游戏质量。 ### 回答3: Cocoscreator是一款非常强大的游戏开发引擎,它使用的是JavaScript语言进行编写。设计一款优秀的游戏框架,是成功开发一款游戏的重要因素之一。 Cocoscreator的游戏框架文档包含以下几个方面的内容: 1.场景管理器 场景管理器是整个游戏框架中的核心部分,它主要负责游戏场景的管理。在这里可以创建新的场景、进入到不同的场景,以及场景之间的传递和切换。 2.游戏控制器 游戏控制器主要负责控制游戏逻辑,包括游戏运行时的场景、角色、道具等元素的控制和管理。它可以控制游戏的开始、暂停、继续、结束等操作。 3.角色控制器 角色控制器负责游戏中所有角色的控制和管理。它可以控制角色的移动、攻击、受伤、死亡以及角色之间的互动。 4.界面管理器 界面管理器主要负责游戏中所有界面的管理。界面是游戏中很重要的组成部分,它可以控制游戏的暂停、结束、返回主菜单等操作。 5.音效管理器 音效管理器负责游戏中所有音效的管理。在游戏中添加适当的音效可以增强游戏的娱乐性和吸引力,让游戏更加生动和有趣。 这些功能的协同工作,共同构成了一款成功的游戏框架。通过Cocoscreator的游戏框架文档,我们可以更加系统和高效地进行游戏开发。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值