代码风格请尽量统一,写下来的代码不是自己的,是整个团队的。说不定某天也要看别人的代码,或者某个新来的要看我们的代码。说的不好听,项目时间一长,就会出现人员流动,如果每个人都写一个风格,就会很难看了。

很多问题还是要提早修正,否则后面东西太多,特殊处理太多,牵一发而动全身,要花更多的时间。

一些功能性、业务逻辑上的东西我就不修改了,这样相当于重写了。只是单纯从技术上进行优化,以后有空再重构。

1.在写配置文件的时候,尽量用map代替vector,因为查找某条配置的时候,可以减少运算。有的地方,set可以代替vector会更好

例如:

优化前:

bool GameConfigData::CMountConfigManager::getMountConfig(int code, Message::Db::Tables::TMount& tMount)
{for (Message::Db::Tables::SeqTMount::iterator iter = _tMounts.begin();iter != _mountMap.end();iter ++){if (iter->code == code){tMount == *iter;return true;}}return false;
}

优化后:

bool GameConfigData::CMountConfigManager::getMountConfig(int code, Message::Db::Tables::TMount& tMount)
{MapTMount::iterator iter = _mountMap.find(code);if (iter == _mountMap.end()){return false;}tMount = iter->second;return true;
}

可以用set替代vector的地方,set不会重复,而且可以用count函数来看是否存在,就不用下面这个循环了。

bool GateApp::CPlayer::isFunctionOpen(std::string functionName)
{for (std::vector<std::string>::iterator it = _openFunction.begin(); it != _openFunction.end(); ++it){if (functionName == *it){return true;}}return false;
}

优化后:

bool GateApp::CPlayer::isFunctionOpen(std::string functionName)
{ return _openFunction.find(functionName) != _openFunction.end();
}

2.代码里面的code很多,物品,技能,buff,updateCode。。。。。几乎每个配置都有一个,最好在用到的时候加上各自的单词。

例如:itemCode,skillCode,buffCode等等,不要只写code,难以阅读,而且代码增多之后很容易造成混乱。

3.建表的时候,主键在配置文件里尽量用code,在主库中保存的信息用id。含义:code表示编码,相当于一种定义;id表示具体的标识,对应着一个实例。

例如在配置里面技能的主键是skill_code。在主库里面,我们很多表的主键是没有实际意义的,直接写id就行了。有意义的例如t_player的player_id,t_guild的guild_id。

4.字符串拼接,一般使用“,”,如果要多一层,可以使用“;”。当然,有些配置的地方,使用了“[]”来包含一条数据,这样可以看的比较清晰。

有些符号是不适合作为分隔符的,例如:“:”

保存和读取,碰到时间CDateTime就会出错了。

//限时皮肤{std::string timingSkin = "";for (std::map<int, cdf::CDateTime>::iterator it = _timingSkin.begin(); it != _timingSkin.end(); it++){timingSkin += ToStr(it->first);timingSkin += ":";timingSkin += it->second.asString();timingSkin += ",";}_playerMountJson[TIMING_MOUNT_KEY] = Json::Value(timingSkin);}
 Message::Public::SeqString mountVec;cdf::CStrFun::split(mountVec, (*iter).c_str(), ':');if (mountVec.size() == 2){if (cdf::CStrFun::is_num(mountVec[0].c_str())){int mountCode = cdf::CStrFun::str_to_int32(mountVec[0].c_str());cdf::CDateTime limitDate;limitDate.parse(mountVec[1].c_str(), "YYYY-MM-DD hh:mm:ss");_timingSkin[mountCode] = limitDate;}}

datetime是的字符串里面是包含冒号的,这样分割,就会出错。

5.把时间保存到json的时候,使用整形保存秒数,或者字符串都可以。经过计算,使用整形来表示时间,因为从1970年开始算起,大概道2030年之后几年才会溢出。所以如果想方便,可以使用整形来保存秒数到json中。用int能够节省json的长度!

6.关于指针的问题,我们使用智能指针,但是智能指针并不是万能的,在星型结构(就是互相指引或者间接互相指引),如果不认为释放,可能会有内存泄露。所以,请尽量保持树形结构。

例如:在GateApp中,

GateEntity

Player       Role         Bag       Mount 。。。。。

这些都是挂在GateEntity中,缺了什么,就从GateEntity中取出来。

像这样:

/*
* 获取背包中坐骑卡数量
* return 坐骑卡数量返回
* @param cardCode 坐骑卡Code
*/
int GateApp::CMountManager::getMountCardCount(int cardCode) const
{assert(_bag);return _bag->getItemCount(cardCode);
}

在Mount下面再挂一下Bag,实在是有很大隐患的。

7.不要觉得很简单的代码,不按照规范没所谓,如果是细枝末节的就算了,如果是在比较顶层的功能中,一定要严格按照规范。我们的需求是经常变动的。一个功能可能改十几二十次,现在比较少见,如果上线之后,效果不好,就会经常碰到了。

8.一些结构的typedef在CdlPublic中,另外,Common/Public/ComPublic.h写了一些cdl里面没有的,方便大家使用。

例如:

 //settypedef std::set<int> SetInt;  typedef std::set<long64_t> SetLong64;typedef std::set<std::string> SetString;//maptypedef std::map<int, SeqInt> MapSeqInt;typedef std::map<long64_t, int> MapLongInt;typedef std::map<int, long64_t> MapIntLong;typedef std::map<int, MapIntLong> MapMapIntLong;

9.关于json,有好处也有坏处,json可以节省大量字段,但是调试的时候,如果要看内存的值,非常困难,而且使用json的计算量肯定会比较大的。

例如:

/*
* 检测玩家是否已拥有该坐骑
* return true为已拥有,false为未拥有
* @param mountCode 坐骑Code
*/
bool GateApp::CMountManager::isMountExist(int mountCode) const
{for (Json::Value::const_iterator citer = _playerMountJson[ALL_MOUNT_KEY].begin();citer != _playerMountJson[ALL_MOUNT_KEY].end(); ++citer){if ((*citer).isInt()){if ((*citer).asInt() == mountCode){return true;}}}return false;
}

这里面的内容基本上是无法查看的。

所以一般情况下,建议json只是保存某些数据,用来节省字段。另外用一个结构来保存在内存,保存数据库的时候再转换一下。

10.一些功能的检测、登陆特殊处理,必须等整体数据初始化之后才能使用。

void CPlayer::setTPlayerExtend2( const Message::Db::Tables::TPlayerExtend2& tPlayerExtend2 )
{ _tPlayerExtend2 = tPlayerExtend2;_playerExtend2Json.parse(_tPlayerExtend2.jsStr);if (!isFunctionOpen("OfflineHour"))//这里还没初始化!!!{_tPlayerExtend2.offlineHour = 0;}else{

11.关于保存数据库的问题,很多功能,如果没有特殊要求,都是延时保存的。服务端最大的压力的地方。

如果要立刻保存,可以这样写,

getSaveInfo(ETPlayerMount).immediatelyUpdate= true;

但是这是不推荐的。所有GateEntity的component里面,都是默认300秒的延时保存的。

所以一般都这样写:

mountManager->getSaveInfo( ETPlayerMount ).changeFlag= true;
mountManager->save(false);//记得加上这个,否则没保存的

可以看一下这里的代码:

logout是下线的时候调用的,time是当前时间,intervalSeconds是延时保存的时间间隔

bool SSaveInfo::needToSave( bool logOut, const cdf::CDateTime& time, int intervalSeconds )
{if ( immediatelyUpdate )//一些特殊的,非常非常重要的东西才立刻保存,用这个属性{return true;}if ( ! changeFlag )//一般情况下用这个属性。{return false;}if ( logOut ){return true;}#ifdef _DEBUG{intervalSeconds = 10;}
#endifif ( ( time - lastUpdateTime ).getTotalSeconds() > intervalSeconds ){return true;}return false;
}

记得调用save函数,否则不会触发保存的。

12.虽然代码顺序一般是不能更改,但是也应该保持独立性,尽量做到封装。

例如:

Message::Game::SCodeAttribute GateApp::CMountManager::getMountAttribute(int code)
{Message::Game::SCodeAttribute sCodeAttribute;Message::Db::Tables::TMount tMount;if (CMountConfigManager::instance()->getMountConfig(code, tMount)){Message::Db::Tables::TAttribute attr;CAttributeConfigManager::instance()->getAttribute(tMount.attributeId, attr);//攻击、生命、物防、法防、穿透、格挡、命中、闪避、暴击、韧性sCodeAttribute.attribute.push_back(attr.attack);sCodeAttribute.attribute.push_back(attr.life);sCodeAttribute.attribute.push_back(attr.physicalDefense);sCodeAttribute.attribute.push_back(attr.magicalDefense);sCodeAttribute.attribute.push_back(attr.wreck);sCodeAttribute.attribute.push_back(attr.block);sCodeAttribute.attribute.push_back(attr.miss);sCodeAttribute.attribute.push_back(attr.demiss);sCodeAttribute.attribute.push_back(attr.crit);sCodeAttribute.attribute.push_back(attr.decrit);}sCodeAttribute.code = code;return sCodeAttribute;
}

这种按顺序把这么多个属性放在一个数组里面,如果中间不小心插入或者删除一个,就会没救了。

vector是用来存放同样的东西,这个同样的东西,不是只数据类型上面,同样的整形,而是指业务逻辑上,同样的内容。这里面是八个不同的属性。调试起来也无法看了。

13.在涉及到客户端的cdl定义的时候,尽量考虑扩展性,因为我们自己改自己代码容易,让客户端改一下还是很麻烦的。

14.像某个功能的战斗力,这种服务端完全没有意义,不用保存,只是显示作用的,如果客户端能够获得数据,应该让客户端去做,而不是服务端计算好才传过去。用于显示的东西,应该是客户端做的,这是表现层的功能,而不应该服务端做了。而且服务端应该尽量减少计算,以降低负荷,这是有实际意义的。另外做的时候还是得看沟通,有时候如果确实不影响性能,是可以适当分担一下客户端的开发工作的。不过本质上,应该客户端去分担服务端的计算量是不变的。

15.任何输入都是不可信的。对于一个函数来说,输入的参数是不可信的,需要检测,当然,都是服务端的代码,大部分的时候不是那么严格。但是对于cdl定义的接口,客户端传过来的内容,是绝对不可信的,需要严格检测其合法性。这并不是说我们的客户端程序员的水平不行,而是因为市面上存在大量免费的外挂,不免费的可能也几块钱,用来刷我们的漏洞。被刷的漏洞,轻的影响是刷的很厉害,被其他玩家或者运营发现,我们修改回去。中度的影响是发现了问题,我们怎么也找不到是怎么刷的。更严重的是我们根本不知道被刷了。

所以要减低出错的可能,cdl定义的接口,可以尽可能减少传输的内容。

16.有时候为了方便,将一些数据配在t_const表里面,每次用到的时候都取出来。但是如果放进去的是一窜数字,最好还是在启动服务端的时候取出来,整理成合适的格式。

例如:

在坐骑培养的时候使用这个,其实代码没有什么大的问题,就是每次都用字符串解释成数字,多麻烦啊。

/*
* 坐骑培养暴击经验
* @return 暴击经验返回
*/
int GateApp::CMountManager::getFosterCritExp()
{int mountCritExp = 0;int haveRate = 0;int oldRate = 0;int randRate = ::Common::CUtil::myRand(1, 10000);std::string critStr = CConstConfigManager::instance()->getConstValueStr("MountPropCritFoster");int wakanFosterLv = CConstConfigManager::instance()->getConstValue("MountWakanFosterLevel");if (wakanFosterLv > _tPlayerMount.fosterLevel){critStr = CConstConfigManager::instance()->getConstValueStr("MountWakanCritFoster"); }std::vector<std::string> critTemp;cdf::CStrFun::split_ex(critTemp, critStr.c_str(), "[]");//每次都要转化,多麻烦啊for (std::vector<std::string>::iterator iter = critTemp.begin(); iter != critTemp.end(); ++iter){std::vector<std::string> critTemp1;cdf::CStrFun::split(critTemp1, (*iter).c_str(), ',');if (critTemp1.size() == 2){oldRate = haveRate;haveRate += atoi(critTemp1[1].c_str());if (oldRate < randRate && randRate <= haveRate){mountCritExp = atoi(critTemp1[0].c_str());//这里已经获得了,还不跳出循环,后面的运算都是多余的。}}}return mountCritExp;
}

修改一下:

//在load坐骑的时候,顺便转一下这些字符串const std::string& pStr = CConstConfigManager::instance()->getConstValueStr("MountPropCritFoster");Common::CUtil::changBracketsStrToMap(_propFosterExpMap, pStr);//使用道具的配置,使用这个函数可以把[]的格式的字符串转成mapconst std::string& wStr = CConstConfigManager::instance()->getConstValueStr("MountWakanCritFoster");Common::CUtil::changBracketsStrToMap(_wakanFosterExpMap, wStr);//使用灵力_wakanFosterLevel = CConstConfigManager::instance()->getConstValue("MountWakanFosterLevel");

然后每次点击培养的时候,运行一下获得经验值,这里单纯配置的运算,可以放到配置的管理类里面。

int GameConfigData::CMountConfigManager::computeFosterExp(int fosterLevel)
{int randRate = ::Common::CUtil::myRand(1, 10000);Message::Public::DictIntInt expMap;if (_wakanFosterLevel >= fosterLevel){expMap = _wakanFosterExpMap;//使用灵力培养的配置}else{expMap = _propFosterExpMap;//使用道具培养的配置}for (DictIntInt::iterator iter = expMap.begin(); iter != expMap.end(); ++iter){if (randRate <= iter->second){return iter->first;//已经获得就直接返回}else{randRate -= iter->second;}}return 1;
}

17.推送数据要尽量缩减,一个全局的数据,在登陆的时候推一次就行了。之后有更新的时候,一般而言,只推送更新的数据。而不是每次都推全部的数据。

 Message::Game::SPlayerMount_Ptr clientMountMsg = new Message::Game::SPlayerMount();mountManager->makeClientMountInfo(gateEntity, *clientMountMsg);gateEntity->messageToClient(ECmdGateMountInfo, clientMountMsg);

这里的坐骑信息多,没有必要每次都推送,这样增大了推送的数据量,增大了出现网络丢包的概率。客户端收到数据之后,又要全局初始化,双方都要算更多。

18.对于一些升级的功能,每一级都有属性增加,例如坐骑,如果每级的属性都是直接配,以后每次计算的时候,都要加起来。看起来没什么。但是坐骑900个等级,每次改一下属性就要重新计算一下。。。。。。应该每一个当前等级都是一个总和,升级之后,直接用新的attribute_id就行了。

void GameConfigData::CMountConfigManager::getMountFosterConfigAttribute(int transTimes, int curFosterLevel, int& totalExp,Message::Db::Tables::TAttribute& configAttribute)
{MapSubType2MountConfig fosterConfig = _mapMountFosterConfig[transTimes];//1.[0, curFosterLevel)经验,attribute属性int toFosterLv = curFosterLevel;if (curFosterLevel == _maxFosterLevel){toFosterLv = _maxFosterLevel - 1;}for (int fosLv = 0; fosLv <= toFosterLv; fosLv++)//在这里,如果到10级就要循环10次了。{if (!fosterConfig[fosLv]){assert(false);break;}//total experiencetotalExp += fosterConfig[fosLv]->_tMountFoster.maxExp;//[0, toFosterLv]的所有attributeId属性Message::Db::Tables::TAttribute tempAttr;CAttributeConfigManager::instance()->getAttribute(fosterConfig[fosLv]->_tMountFoster.attributeId, tempAttr);configAttribute.attack          += tempAttr.attack;configAttribute.physicalDefense += tempAttr.physicalDefense;configAttribute.magicalDefense   += tempAttr.magicalDefense;configAttribute.life           += tempAttr.life;configAttribute.crit         += tempAttr.crit;configAttribute.block            += tempAttr.block;configAttribute.demiss          += tempAttr.demiss;configAttribute.miss           += tempAttr.miss;configAttribute.decrit           += tempAttr.decrit;configAttribute.wreck          += tempAttr.wreck;configAttribute.moveSpeed += tempAttr.moveSpeed;}

19.价格,是在指定了货币单位之后才有意义的。从商店里面取出来的物品,就要按配置的价格,不能因为自己知道现在是某种价格,就写死了某种价格。

例如:这个函数,只返回了需要的货币数量,没有返回货币价格,调用的时候是写死了。

/*
* 道具不足,获取培养所需的费用
* return 道具费用返回
* @param propCode 道具Code
* @param propNum 道具数量
*/
int GateApp::CMountManager::getFosterPropPrice(const CGateEntityPtr& gateEntity,const CPlayerPtr& player,int propCode,int propNum)
{int price = 0; CShopPtr shop = CShopConfigManager::instance()->getShop(SHOP_CODE_AUTO_BUY);if (shop){CShopSellPtr shopSell = shop->getShopSell(propCode);if (shopSell){price = shopSell->_tShopSell.price;}}if (price == 0){CErrorCodeManager::throwException("ErrorGate_MountItemNotOnSale");}return price * propNum;
}
costMoney = mountManager->getFosterPropPrice(gateEntity, player, propCode, config->_tMountFoster.propNum2 - ownPropNum);
player->enoughMoneyException(EPriceUnitPoint, costMoney, updateCode);

这样就限制死了使用EPriceUnitPoint了,万一策划改了是不会通知你的。急着,策划就像女人一样,是善变的。
20.对于一些==0的检测,如果不会有负数,最好改成≤0。

 if (moneyAmount == 0){CErrorCodeManager::throwException("ErrorGate_MountItemNotOnSale");}

例如这个购买商品,如果某个地方出错或者溢出了变成负数,这里就检测不出来了。

另外,很多最大值的检测也是改成≥比较好。

21.声明变量的时候,顺手初始化一下,要养成习惯。

int flushNum;
int leftSeconds;
mountManager->getPotentialLeftSeconds(flushNum, leftSeconds);

22.对于某些经常用到的“[]”结构的字符串,例如一些配置,最好在GameConfigData中,启动的时候就进行分解。不要每次用到的时候才分解。其他的情况也应该这样处理。写代码的时候,要把自己看成一台计算机,怎样才能让自己计算更少而达到相同的目的。东西不多,估计优化的效果也不大,但是整个游戏,所有功能,所有代码都这样,肯定会有问题的。

例如:坐骑计算皮肤属性的时候,每次都要分解这个字符串。

void GateApp::CMountManager::calSkinAttribute(int mountCode, Message::Public::DictIntInt& specSkinAdd, Message::Public::DictIntInt& specWeaponAdd, Message::Db::Tables::TPlayerRoleFightInfo& playerFightInfo)
{Message::Db::Tables::TMount tMountConfig;if (! CMountConfigManager::instance()->getMountConfig(mountCode, tMountConfig)){return;}SeqString vecTemp;cdf::CStrFun::split_ex(vecTemp, tMountConfig.specialAddition.c_str(), "[]");for (std::vector<std::string>::iterator iter = vecTemp.begin(); iter != vecTemp.end(); ++iter){std::vector<std::string> vecAddition;cdf::CStrFun::split(vecAddition, (*iter).c_str(), ',');if (vecAddition.size() == 3){//1跟2区分坐骑装备加成还是皮肤某一属性加成if (atoi(vecAddition[0].c_str()) == 1){specWeaponAdd[atoi(vecAddition[1].c_str())] = atoi(vecAddition[2].c_str());}else{specSkinAdd[atoi(vecAddition[1].c_str())] = atoi(vecAddition[2].c_str());}}}

修改一下,放到MountConfigManager::loadCofngi()中去。

//TMountMessage::Db::Tables::SeqTMount mounts;Message::Db::Tables::Loader::loadFile(mountConfigFile, mounts);for (Message::Db::Tables::SeqTMount::iterator iter = mounts.begin();iter != mounts.end();iter ++){CMountConfigPtr m = new CMountConfig();m->_tMount = *iter;SeqString vecTemp;cdf::CStrFun::split_ex(vecTemp, iter->specialAddition.c_str(), "[]");for (SeqString::iterator iter2 = vecTemp.begin(); iter2 != vecTemp.end(); ++iter2){SeqString vecAddition;cdf::CStrFun::split(vecAddition, iter2->c_str(), ',');if (vecAddition.size() == 3){CMountSpecialAdd sa;sa.type = atoi(vecAddition[0].c_str());sa.attrType = atoi(vecAddition[1].c_str());sa.addPercent = atoi(vecAddition[2].c_str());m->_specialAdds.push_back(sa);}}_mountMap[iter->code] = m;}

23.一些字段的命名的问题。这是个很蛋疼的问题,一方面我们要命名得尽量跟策划写的文档尽量相同,但是很多中文无法翻译到对应的英文的。另外,策划是善变的,更何况这些名称,一到上线的时候,就会变得更快。所以要自己把握,不要直接按照策划定义的名称来做。

例如:坐骑的配置表,按照策划写的来做,其实怎么看都看不懂。因为在我们的代码里面,type、subType好多都是类型、小类来定义了。贸然按照策划的命名,尤其是策划定义的英文,很有可能是跟我们一些常用的翻译,甚至是C++里面的关键字相悖。对代码阅读造成极大困难。

1) 星配置:坐骑共有0-14转(0-9),每转有10阶(1阶0星-10阶0星),每阶有5星(10阶没有5星,而是要转生),因此每转需要配置45星,t_mount_foster的type是转,sub_type是星级,config_type=1是培养进阶,config_type=2是转生

Type=0,sub_type=0,是0转1阶0星

Type=0,sub_type=1,是0转1阶1星

Type=0,sub_type=2,是0转1阶2星

Type=0,sub_type=3,是0转1阶3星

Type=0,sub_type=4,是0转1阶4星

Type=0,sub_type=5,是0转2阶0星(1阶5星满则升1阶,变为2阶0星,所以1阶5星=2阶0星)

以此类推……

对了,顺便说一句,配置文件还是我们自己设计吧,策划给的只是建议和参考。

24.关于Map的一些命名,直接上例子好了:

typedef std::map<int, CMountFosterConfigPtr> MapSubType2MountConfig;

这个别名,加了个subType,但是其他地方一样可以用到,可能key值不是subtype。

所以我的建议是

typedef std::map<int, CMountFosterConfigPtr> MapCMountFosterConfigPtr;
typedef std::map<int, MapCMountFosterConfigPtr> MapMapCMountFosterConfigPtr; 

对于typedef的命名,脱离具体功能。在使用的时候,注释上具体的key和value

MapCMountFosterConfigPtr _mapMountTransConfig;       //坐骑转生配置[转生次数,config]
MapMapCMountFosterConfigPtr _mapMountFosterConfig;  //坐骑培养配置[阶[星,config]]

25.坐骑配置分散的问题,会导致后面高级的时候有一点变化就会造成循环几百次。对于这种情况,要从两个角度来考虑。

(1)在极短时间内,例如那1秒钟内,会不会造成计算量暴增,通常一些限制时间的功能,例如答题、帮派战等,将整个服的玩家堆积在一起,大家都对服务器发送相似的请求。计算量肯定会暴增的,这个时候可能会卡。这个现象会非常明显,玩家肯定会投诉,但是有明确修改方向,往往针对性优化一下,就会能够解决问题。

(2)在一个比较长的时间段里面,例如一整天,累积下来的总的时间量会不会很大。坐骑配置的问题就是这个,可能不会导致服务器卡,甚至开始的时候完全不会存在问题。但是越往后面,服务器会越来越卡,让你找问题也找不到,玩起来就是觉得卡,想优化,发现优化一个地方也不会有明显效果。这是慢性毒药,通常发现问题的时候就是病入膏肓,做什么都回天无力的感觉。

所以卡的问题,要有意识地避免。

void GameConfigData::CMountConfigManager::initFosterExpAndAttribute()
{//计算每个培养等级的总属性以及总经验。int lastExp = 0;//之前的所有等级的经验。Message::Db::Tables::TAttribute lastAttr;//之前所有等级获得的属性lastAttr.__init();Message::Db::Tables::TAttribute thisAttr;//当前这条属性for (MapCMountFosterConfigPtr::iterator iter = _transMap.begin();iter != _transMap.end();iter++){MapCMountFosterConfigPtr& curFosterMap = _fosterMap[iter->first];//当前转的培养配置。for (MapCMountFosterConfigPtr::iterator iter2 = curFosterMap.begin();iter2 != curFosterMap.end();iter2++){//经验值lastExp += iter2->second->_tMountFoster.maxExp;//iter2->second->_currentMaxExp = lastExp;//属性 if (!CAttributeConfigManager::instance()->getAttribute(iter2->second->_tMountFoster.attributeId, thisAttr)){  continue;//当前配置没有属性}  Common::CFightAlgorithm::addAttribute(lastAttr, thisAttr);//当前的加上之前的属性iter2->second->_currentAttribute = lastAttr;//当前的总属性}//这是培养满了,没有转生的情况//经验值lastExp += iter->second->_tMountFoster.maxExp;iter->second->_currentMaxExp = lastExp;//转生前的总属性  if (!CAttributeConfigManager::instance()->getAttribute(iter->second->_tMountFoster.attributeId, thisAttr)){continue;//当前配置没有属性}Common::CFightAlgorithm::addAttribute(lastAttr, thisAttr);//当前的加上之前的属性iter->second->_currentAttribute = lastAttr;//当前的总属性 }
}

这是坐骑属性的解决办法。本来真正的解决办法,是改配置的方式,让策划把东西算好。但是现在功能已经做完,客户端也是用到这个配置。这种涉及到客户端的问题,可能就比较难沟通。自己当时没想好的东西,要人家背锅,而且,你不知道客户端要改到什么程度。所以一般在功能完成了,再去优化,我们能做的事有限,往往要选择折衷的办法,最好还是在开干之前就考虑这方面的问题。这次的解决办法是在启动的时候,把所有等级的培养属性都算一遍。

凡人修真3D(1)坐骑相关推荐

  1. 凡人修真3D(3)神翼

    1.对于一些集成起来的通用配置表,如下面这样的: 还是上图吧 这种通用表,字段直接是value1,value2,value3,value4,value5,value6. 这是GameConfigDat ...

  2. 凡人修真3D(4)神装

    1.for循环的iter如果不是之后特殊要求用到,千万不要提出来,要养成习惯, Message::Db::Tables::SeqTGodEquip::const_iterator it = seqTG ...

  3. 凡人修真3D(2)神兵

    1.命名很重要,不知道上面有么有提过,不过发现了还是写下来吧.命名的时候,要看看这个单词在其他系统中,是否已经被使用了,已经具有特别的意义. 例如:整个游戏里面,表示魔法的都是用magical这个词, ...

  4. 凡人修真3D(6)背包

    1.背包检测空位的时候要注意.直接上代码吧. 正确的做法:检测完之后,需要立刻附加到背包中,才能进行下一次检测. for (SeqPlayerItemPtr::iterator iter = play ...

  5. 《凡人修真2》 架设

    具体内容见 百度网盘 链接:http://pan.baidu.com/s/1qY9wqXe 密码:j9n8 1. 解压文件: 看里面的 视频教程:按照里面的步骤:执行:重要的是:建立数据库 2. 凡人 ...

  6. 鸿蒙生息 不朽凡人,不朽凡人

    不朽凡人是一款Q版仙侠风格手游,游戏不但场景精致,技能特效也十分酷炫,神兵武器幻形更是趣味无穷.还有新颖独特的时装染色系统,翅膀幻形玩法,增长战力的同时还能选择自己的独特造型,满足你的个性化装备需求! ...

  7. Bookmarks_2012_06_13

    Bookmarks 书签栏 VeryCD 邮件 - 入职申请SVN - zengfeng@verycd.com2345网址导航-我的个性化主页-中国最好的网址站我的工作台 - 心动游戏项目管理 手册A ...

  8. 页游《火影忍者》角色和背景遮挡半透明效果的实现

    这里讲的是关于2D游戏的角色和背景以及物体之间的遮挡关系,用半透明角色处理的讨论和实现方式.这里主要是讨论关于页游<火影忍者>里对于角色和背景物体之间的遮挡处理方式.同时也实现了和他一样的 ...

  9. 角色和背景遮挡半透明效果的实现

    这里讲的是关于2D游戏的角色和背景以及物体之间的遮挡关系,用半透明角色处理的讨论和实现方式.这里主要是讨论关于页游<火影忍者>里对于角色和背景物体之间的遮挡处理方式.同时也实现了和他一样的 ...

最新文章

  1. Visual Studio2005无法启动web调试的真正原因
  2. 阿里达摩院百万大奖评选开启!这次人人都能给青年科学家当伯乐
  3. 从GitHub中整理出来的15个最受欢迎的Python开源框架,你喜欢哪个
  4. android 闹钟服务,如果闹钟时间已经过去,android可以防止即时触发闹钟服务
  5. java ee4.8下载_在WildFly 8.2中修补焊接3 – Java EE 8的第一个实验RI
  6. vue-day04-vue前端交互
  7. 辽宁计算机应用考试报名时间,2021辽宁上半年计算机应用水平考试报名时间及方法...
  8. 如何理解皮尔逊相关系数(Pearson Correlation Coefficient)?
  9. 网页版QQ、MSN等等聊天工具web版大全
  10. 计算机专业复试有英语口语吗,计算机考研复试英语口语技巧
  11. SVN创建分支与合并
  12. java找不到符号解决办法
  13. 批量抓取羊毛网站信息,定时自动发送到邮箱进行阅览
  14. C++:hex、oct、dec使用
  15. c语言网页版在线编译器_简单好用的网页版在线公式编辑器
  16. Eclipse/Myeclipse自定义JSP模板
  17. 18650圆柱锂电池comsol5.6模型 参数已配置,电化学生热研究,三种放电倍率,
  18. RAID磁盘阵列是什么(一看就懂)
  19. Linux C popen函数返回Shell命令执行结果
  20. 微博平台架构和安全——微博平台首席架构师杨卫华演讲

热门文章

  1. 安装dlib遇到的错误AttributeError: module 'dlib' has no attribute 'get_frontal_face_detecetor'
  2. Apt-get 如何在Ubuntu/Debian上正常工作
  3. 爬虫 人人车字体反爬分析(含源码)
  4. 水货iPad 2价格骤降数千元
  5. LoRa 节点侧学习笔记_汇总
  6. css实现文字无缝横向滚动
  7. 优秀的UI设计所具有的13个原则,天瑞地安小编总结
  8. 手游运营需网络文化经营许可证?1000万资本金门槛
  9. 2019年4月中国编程语言排行榜,java占有率一骑绝尘,python工资领先
  10. python 读取txt函数总结