刷属性bug的核心原因是有功能的模块数值加了N次。XML文件一般就是一个文件文件。

设计思路

  刷属性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;
    }
    
  2. 关于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’),因为性管理器知道减多少。

Tinyxml2学习,tinyxml2

转自http://www.360doc.com/content/13/1223/16/3684846\_339528825.shtml,尊重原文

什么是XML?

XML全称EXtensible Markup
Language,翻译啊可扩大标记语言,简而言之即是您得起定义数据的标识,以之来分各种不同的数量,以便让进行数据交换,例如html就可理解也同样栽简单的xml语言。XML文件一般就是一个文件文件,可以运用另外编码。

 

达图就是是自身系统受到一个xml文件之图标,使用VC2005开拓它,你可看到如下内容:

 

XML也是出就几乎个目标成了,一般的话我们经常采取的切近如下:

l TiXmlDocument:文档类,它代表了一切xml文件。

l TiXmlDeclaration:声明类,它表示文件的扬言部分,如齐图所展示。

l TiXmlComment:注释类,它代表文件之笺注部分,如齐图所显示。

l
TiXmlElement:元素类,它是文本之机要组成部分,并且支持嵌套结构,一般以这种结构来分类的贮存信息,它好涵盖属性类和文本类,如达到图所出示。

n
TiXmlAttribute/TiXmlAttributeSet:元素属性,它一般嵌套在要素中,用于记录之因素的有些性,如齐图所出示。

n TiXmlText:文本对象,它嵌套在某元素中,如达到图所著。

 

保存文档对象

 

当你吗可以使用SaveFile()函数来进行其它存为,这个函数的面目如下:

bool SaveFile( const std::string& filename ) const

当程序中若可以如下使用:

 

//载入xml文档

TiXmlDocument doc(“tutorial.xml”);

doc.LoadFile();

doc.Print(); //输出文档

cout<<endl;

doc.SaveFile(“tutorial.txt”);

用记事本打开tutorial.txt,你可以看如下内容。

 

 

 

回来第一单根元素

 

此外文档对象还提供了一个实用的函数用于返回第一个彻底对象,它可以被你方便之遍历整个文档结构,查找自己欲的数目。函数原形如下:

+TiXmlElement* RootElement()

咱俩以介绍元素类的时候再次详尽介绍其的运用。

声明类

 

以正规的XML文件被,声明也文件的首先宗,例如<?xml version=”1.0″
standalone=”yes”?>,声明对象具备三个属于性值,版本,编码和单独文件宣称

貌似的话文档的第一实行就是是宣称对象,你可以拿文档对象的率先个头节点换为声明对象。

 

//使用TinyXml的宣示对象

TiXmlDeclaration *decl;

decl = doc.FirstChild()->ToDeclaration();

接下来就可以以它的功能了,它可以被您归时之本,编码等消息,函数原形如下:

+const char *Version() const

+const char *Encoding() const

+const char *Standalone() const

每当先后中你可以如下使用:

 

//使用TinyXml的扬言对象

TiXmlDeclaration *decl;

decl = doc.FirstChild()->ToDeclaration();

cout<<“使用TinyXml的宣示对象(TiXmlDeclaration)”<<endl;

//输出声明对象对应之xml内容

decl->Print(0,4,&str);

cout<<str<<endl;

//分别出口声明对象的性质

cout<<“版本:”<<decl->Version()<<”
是否也相对文件:”<<decl->Standalone()<<”
编码方式:”<<decl->Encoding()<<endl;

cout<<endl;

 

 

注释类

 

此看似一般也xml数据提供解释说明,在次中貌似不动她,因此,这里虽无介绍了。

元素类

 

要素呢一个容器类,它有元素名称,并得以分包其他元素,文本,注释和未知节点,这些目标统称为因素的节点,即节点可以为要素、文本、注释和茫然节点类型。元素呢足以涵盖自由个数的属性。

俺们还是以如下的XML代码来证实是仿佛的功能。

 

<element attribute=”this a attribute(这是一个属性)” int= “1” float =
“3.14”>

<subelement1>

This a text(这是一个文件)

</subelement1>

<subelement2/>

<subelement3/>

<subelement4/>

</element>

节点名

 

在上方元素的代码中,element为根元素的称,你得经如下的函数来安及归她。

+const std::string& ValueStr() const

+void SetValue( const std::string& _value )

父节点

 

subelement1,subelement2,subelement3,subelement4都是element的子元素,如果手上因素对象的指针指向subelement1,subelement2,subelement3,subelement4,你得透过Parent()函数来回到指向element对象的指针,Parent()函数的宣示如下:

+TiXmlNode* Parent()

子节点

 

经过父节点的指针,你可遍历所有的子节点。

+TiXmlNode* FirstChild()

+TiXmlNode* FirstChild( const std::string& _value )

面两只函数用于返回第一身材节点目标的指针,带参数称呼之怪函数表示回去第一个名叫吧_value的子节点。

+TiXmlNode* LastChild()

+TiXmlNode* LastChild( const std::string& _value )

点的少独函数用于返回最后一个节点目标的指针,带参数叫的不可开交函数表示回去最后一个名也_value的子节点。

而也可采取IterateChildren()函数来挨家挨户遍历所有的节点,它们的函数声明如下:

+TiXmlNode* IterateChildren( const TiXmlNode* previous )

+TiXmlNode* IterateChildren( const std::string& _value, const
TiXmlNode* previous )

带来参数称呼之不胜函数表示仅遍历同名的节点。

编辑子节点

 

公可插、删除替换所有的子节点。

+TiXmlNode* InsertEndChild( const TiXmlNode& addThis );

+TiXmlNode* InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode&
addThis );

+TiXmlNode* InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode&
addThis );

方三单函数用于插入节点,InsertEndChild函数让您将新节点插入到最终,InsertBeforeChild和InsertAfterChild函数允许而以指定的节点位置前后插入节点。

+TiXmlNode* ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode&
withThis );

ReplaceChild函数用于替换指定的节点。

+bool RemoveChild( TiXmlNode* removeThis );

RemoveChild函数让你剔除指定的节点。

void Clear();

Clear函数会删除本节点的所有子节点(包括子节点包含的从子节点),但无会见修改以节点。

同级节点

 

 

<element attribute=”this a attribute(这是一个性质)” int= “1” float =
“3.14”>

<subelement1>

This a text(这是一个文书)

</subelement1>

<subelement2/>

<subelement3/>

<subelement4/>

</element>

每当上面的xml代码中,subelement1、subelement2、subelement3、subelement4都属同级节点,我们吧提供了连带的函数用于在这些同级节点受到遍历。

+TiXmlNode* PreviousSibling()

+TiXmlNode* PreviousSibling( const std::string& _value )

足因当下的节点,返回上一个节点的指针。带参数名叫之十分函数表示回去上一个名为也_value的节点。

理所当然你为可因当下底节点,返回下一个节点的指针。带参数称之老大函数表示回去下一个曰吧_value的节点。

+TiXmlNode* NextSibling()

+TiXmlNode* NextSibling( const std::string& _value)

遍历元素

 

要素是均等栽奇特之节点,以'<‘为开端字符,后接元素名称。函数NextSiblingElement用于返回下一个同级元素,而忽视任何项目的节点。它们的函数声明如下:

+TiXmlElement* NextSiblingElement()

+TiXmlElement* NextSiblingElement( const std::string& _value)

带来参数叫作之生函数表示回去下一个叫吧_value的同级元素。

本类也提供了连带的函数,让您回去第一个子元素。

+TiXmlElement* FirstChildElement()

+TiXmlElement* FirstChildElement( const std::string& _value )

带动参数称为之怪函数表示回去下一个号称吧_value的子元素。

素属性

 

属性一般保存于要素被,它们为运用”=”号连接的少个字符串,左边的意味属性名,等号右侧边的表示属性值,通常用字符串、整数和浮点数等数据类型表示。例如,pi
= 3.14。

若得由此如下的函数,返回属性值。

+const std::string* Attribute( const std::string& name ) const;

+const std::string* Attribute( const std::string& name, int* i )
const;

+const std::string* Attribute( const std::string& name, double* d )
const;

以地方3单函数中,第一只函数使用字符串保存返回的属于性值,第二独函数把属性值转换为整数然后回到,第三单函数把属性值转换为浮点数然后返。不过,第二、三独函数都见面为字符串的款型记录属性值,并视作函数的回值返回。

除此以外,你也可行使模板函数:

+template< typename T > int QueryValueAttribute( const
std::string& name, T* outValue ) const

来回到特点之属于性值,它见面根据你传入的参数,自动选择当数据类型。

除此以外,本类也供了如下三单函数让您设置属性,参数的品类以及归函数类似。

+void SetAttribute( const std::string& name, const std::string& _value
);

+void SetAttribute( const std::string& name, int _value );

+void SetDoubleAttribute( const char * name, double value );

FirstAttribute和LastAttribute可以吃你回第一单跟末段一个性,它们的函数声明如下:

+TiXmlAttribute* FirstAttribute()

+TiXmlAttribute* LastAttribute()

RemoveAttribute函数可以被您去指定名称的特性,它的函数声明如下:

+void RemoveAttribute( const std::string& name )

素函数总结

 

ValueStr //返回元素名称

SetValue //设置元素名称

Parent //返回父节点对象

FirstChild //返回第一个头节点

LastChild //返回最后一个子节点

IterateChildren //返回下一个子节点

InsertEndChild //在最后一个子节点后插入子节点

InsertBeforeChild //在指定的子节点前插入子节点

InsertAfterChild //在指定的子节点后插入子节点

ReplaceChild //替换指定的子节点

RemoveChild //删除指定的子节点

Clear //删除所有的子节点

PreviousSibling //返回同级中前一个节点

NextSibling //返回同级中晚一个节点

NextSiblingElement //返回同级中后一个素

FirstChildElement //返回第一个子元素节点

Attribute //返回元素被的属性值

QueryValueAttribute //返回元素中的属于性值

SetAttribute //设置元素被之属性值

FirstAttribute //返回元素被率先单特性对象

LastAttribute //返回元素中最终一个性能对象

RemoveAttribute //删除元素被指定的习性对象

属性类

 

属性也名称=”值”对,元素得以有属性值,但名称必须唯一。

君得经过

+const std::string& NameTStr() const

回去属性名称

为得透过下面三单函数返回属性值:

+const std::string& ValueStr() const

+int IntValue() const;

+double DoubleValue() const;

理所当然你吗可以安装属性值,它们的函数声明如下:

+void SetName( const std::string& _name )

+void SetIntValue( int _value );

+void SetDoubleValue( double _value );

+void SetValue( const std::string& _value )

上述函数和素看似中之连锁函数类似,这里不另行介绍了。

每当要素属性被,通常有众多性质,你可以经过Next函数返回下一个特性对象的指针,也可经Previous函数获得上一个性能对象的指针。它们的函数声明如下:

+TiXmlAttribute* Next()

+TiXmlAttribute* Previous()

 

TinyXml使用文档对象模型(DOM)来解析xml文件,这种模型的处理方式为于解析时,一次性的拿全体XML文档进行解析,并以内存中形成相应的培训结构,同时,向用户提供相同文山会海之接口来聘和编辑该树结构。这种措施占据内存大,但好于用户提供一个面向对象的走访接口,对用户越来越和睦,非常好用户用。下面我们逐一来介绍各个类的用法。

文档类

 

 

 

文档类代表一个XML文档,通过她,你可以保留,载入和打印输出文档。你得经过以下方式载入xml文档到TiXmlDocument。

创办文档对象

 

l 创建一个拖欠的文档对象,然后载入一个xml文档

利用到之函数原形如下:

+TiXmlDocument();

+bool LoadFile( const std::string& filename)

当次中君得如下使用:

 

//载入xml文档

TiXmlDocument doc();

doc.LoadFile(“tutorial.xml”);

l 2、在构造函数中盛传文档的称,然后调用load函数完成解析载入

用到的函数原形如下:

+TiXmlDocument( const std::string& documentName );

+bool LoadFile();

当程序中君得如下使用:

 

//载入xml文档

TiXmlDocument doc(“tutorial.xml”);

doc.LoadFile();

出口文档对象

 

文档类提供了Print()函数用于在决定高出口当前底文档内容,这个函数的本色如下:

+void Print() const

每当次中公可如下使用:

 

//载入xml文档

TiXmlDocument doc(“tutorial.xml”);

doc.LoadFile();

doc.Print(); //输出文档

tutorial.xml的始末如下:

 

<?xml version=”1.0″ standalone=”yes” encoding=”utf-8″?>

<!–comment 注释–>

<element attribute=”this a attribute(这是一个性)” int= “1” float =
“3.14”>

<subelement1>

This a text(这是一个文本)

</subelement1>

<subelement2/>

<subelement3/>

<subelement4/>

</element>

于控制台中你可以抱如下输出:

出于文件使用UTF-8编码,而Windows下的控制台默认使用gb2312编码,因此会生成乱码。

http://www.bkjia.com/cjjc/1214389.htmlwww.bkjia.comtruehttp://www.bkjia.com/cjjc/1214389.htmlTechArticleTinyxml2学习,tinyxml2
转自http://www.360doc.com/content/13/1223/16/3684846\_339528825.shtml,尊重原文
什么是XML? XML全称EXtensible Markup Language,翻译为而扩大…

娱服务器设计的性管理器

  游戏中角色有所的属于性值很多,运营多年的游乐,往往会生出成百上千单成人线,每个属性都有或受N个成长线模块增减数值。举例当角色戴上铁上hp+100沾,卸下武器时HP-100点,这样加减逻辑只出相同地处还比较好控制,如果某天有只特别意义当被有技能攻击时,角色武器会吃击落,这样尽管见面产出减数值的操作不止一处于。如果逻辑处理不当,比如击落的下没合适的减数值,再次穿戴武器就导致属性值加了点滴度,也即是玩家经常说的刷属性。这种bug对娱乐平衡性影响格外老,反响好粗劣,bug又生为难给测试发现。本文将介绍一种植管理性的思绪,最要命限度的免此类bug,如果起bug,也能够非常好的排查。

总结

  1. 性能提供一个名字映射出那么些益处,比如装备配属性,buff配属性的,有名字相关联会特别方便
  2. 提供一个get和set接口的投,这样属性管理器就跟切实的靶子的特性字段解耦了。即使是存活的功能模块也堪合这个特性管理器。
  3. 特性的add和sub操作,都于性能管理器中养记录,这样便出现问题,通过getByModule
    getAllModule两独接口也足拉查找问题。
  4. 特性管理都合龙及H2Engine中,github地址:
    https://github.com/fanchy/h2engine

相关文章