凡人修真3D(2)神兵
1.命名很重要,不知道上面有么有提过,不过发现了还是写下来吧。命名的时候,要看看这个单词在其他系统中,是否已经被使用了,已经具有特别的意义。
例如:整个游戏里面,表示魔法的都是用magical这个词,现在神兵也用了这个词,很容易造成误解。而且,神兵怎么翻译,好像都翻译不出magical吧。
2.读取TAttribute表的时候,还是要判断一下是否有这个数据的。
bool GameConfigData::CAttributeConfigManager::getAttribute( int attrId,Message::Db::Tables::TAttribute& attr ) { MapTAttribute::iterator iter = _attrMap.find(attrId); if (iter != _attrMap.end()) { attr = iter->second; return true; } else { return false; } }因为没有数据的时候,附加上去是没有意义的。而且tAttribute的__init()函数好长,所以传进来是没有初始化的。如果在外面没初始化,可能附加了一个随机数,甚至变负。
3.在for循环的初始化语句的地方,很多人为了缩减语句长度,把第一句抽出来。实际上换行就好了,放进for循环中,就相当于一个局部的属性,这个for循环用完了,这个属性就没有了。如果抽了出来,就会一直存在,下面的for循环必须有一个不同的名字,而且如果类型一样,可能会造成混用,编译器也检查不出来。所以如果不是特殊的需要,请把内容放到for循环中去。
int GameConfigData::CMagicalConfigManager::getStarNum(int promoteLevel, int starLevel) { MapMapCStarConfigPtr::const_iterator it = _mapMapStarConfig.begin();//我说的是这句!!!!!要放进for循环中去。 int starNum = 0; for (; it != _mapMapStarConfig.end(); ++it) { if (promoteLevel > it->first) { continue; } starNum += it->second.size(); } starNum += starLevel; return starNum; }
4.这个不算是bug,算是一个建议把。在写代码的时候,在一个函数里面,可能一层一层下来,if、switch之类一堆,就会出现很多层。到下面的层就一个屏幕都无法显示出来了。我一般是把一些if语句抽出来,早点return掉,下面的继续执行。说的不是很清楚,看代码吧。
if (needItemCount < promoteConfig->_tMagicalPromote.itemNum ) { //数量不足 if (autoBuy) { //xxxx这里还有一堆代码,省略了。这部分代码很长很长 } else {// 升阶石不足 CErrorCodeManager::throwException("ErrorGate_MagicalPromoteStoneNotEnough"); } }
一般我会这样写的:
if (needItemCount < promoteConfig->_tMagicalPromote.itemNum ) { //数量不足 if (!autoBuy) { // 升阶石不足 CErrorCodeManager::throwException("ErrorGate_MagicalPromoteStoneNotEnough"); } //继续执行自动购买的代码。 }这样能尽量减少{}的嵌套。只是个小小建议,我也不知道这样算不算好。
5.代码里面尽量不要写死,对于一些用处比较多的数字,可以用一个常量表示。
int price = 0; int shopCode = 10003; //商店类型 CShopPtr shop = CShopConfigManager::instance()->getShop(shopCode); if (shop) {这里面的自动购买商店在CShopConfigManager里面有定义
CShopPtr shop = CShopConfigManager::instance()->getShop(SHOP_CODE_AUTO_BUY);一开始写代码的时候可能大家都不知道有这种东西存在,不过大家要保持这种意识,留意和多看别人的代码。
6.这个好像提过,不过碰到了就写一下吧。读取商店配置的时候,要用上价格单位和数量。单独的数量是无意义的,而且万一改了货币单位呢!
int price = 0; CShopPtr shop = CShopConfigManager::instance()->getShop(SHOP_CODE_AUTO_BUY); if (shop) { CShopSellPtr shopSell = shop->getShopSell(promoteConfig->_tMagicalPromote.itemCode); if (shopSell) { price = shopSell->_tShopSell.price; } } //检测是否有钱 player->enoughMoneyException(EPriceUnitEMoney, price * promoteConfig->_tMagicalPromote.itemNum, updateCode);7.除数必须检测是否为0.
例如:
if (tPlayerMagical.blessingValue + tPlayerMagical.limitBlessingValue + addExp >= promoteConfig->_tMagicalPromote.needBlessing)
8.对于配置在t_const里面的复杂的字符串,如果有自己功能的Config管理类,应该在GameCofnigData里面先分解。
// 成功率 = 取祝福值百分比区间的成功率. int successRate = 0; std::string successRateStr = CConstConfigManager::instance()->getConstValueStr("MagicalPromoteSuccessRate"); std::vector<std::string> successRateTemp; cdf::CStrFun::split_ex(successRateTemp, successRateStr.c_str(), "[]"); for (std::vector<std::string>::iterator iter = successRateTemp.begin(); iter != successRateTemp.end(); ++iter) { std::vector<std::string> successRateTemp1; cdf::CStrFun::split(successRateTemp1, (*iter).c_str(), ','); if (successRateTemp1.size() != 2) { continue; } if (atoi(successRateTemp1[0].c_str()) < blessingPercent) { successRate = atoi(successRateTemp1[1].c_str()); } }
像这样的,应该先分解,然后封装在管理类里面了。
std::string successRateStr = CConstConfigManager::instance()->getConstValueStr("MagicalPromoteSuccessRate"); CUtil::changBracketsStrToMap(_promoteSuccessRate, successRateStr);
bool GameConfigData::CMagicalConfigManager::isPromoteSuccess(int blessingPercent) { int rate = 0; for (DictIntInt::iterator iter = _promoteSuccessRate.begin(); iter != _promoteSuccessRate.end(); ++iter) { if (blessingPercent < iter->first) { break; } else { rate = iter->second; } } int rand = ::Common::CUtil::myRand(1, 10000); return rand < rate; }内容会清晰和简单很多。
9.关于概率计算的问题,C++中的随机数最大值大概是30000多,不能更大了。另外,一般情况下,设计到战斗和属性之类的,总概率都是10000,用常量FIGHT_RATING。其他的概率一般是100.这个不做要求,但是用到的时候要注意,不要搞错。
bool GameConfigData::CMagicalConfigManager::isPromoteSuccess(int blessingPercent) { int rate = 0; for (DictIntInt::iterator iter = _promoteSuccessRate.begin(); iter != _promoteSuccessRate.end(); ++iter) { if (blessingPercent < iter->first) { break; } else { rate = iter->second; } } int rand = ::Common::CUtil::myRand(1, 10000);//这里总概率是10000,但是上面的配置的总概率是100. return rand < rate; }
10.在一段程序里面,对于一些检测判断,比较容易return或者抛异常的,应该放在前面。变量尽可能放到要用的时候才声明。
这个例子可能不算很经典
void ::Message::Game::IMagicalImpl::magicalStar_async(const ::Message::Game::AMD_IMagical_magicalStarPtr& magicalStarCB, int autoBuy, const ::cde::CContext& context) { int succeed = 0;//这两个变量根本没用到,应该放在后面 int exp = 0; CGateEntityPtr gateEntity; CPlayerPtr player; CGateHelper::getPlayerAndEntity(context, gateEntity, player);//一般来说,接口都是这两个比较靠前,因为要取出来用 CMagicalManagerPtr magicalManager = CMagicalManagerPtr::dynamicCast(gateEntity->getComponent(ECOMPONENT_TYPE_MAGICAL_MANAGER)); // 神兵数据 if (!magicalManager->getTPlayerMagical().activate) {//神兵还没激活 CErrorCodeManager::throwException("ErrorGate_MagicalNotActivate"); }
11.尽量不要把int当bool来使用。代码有变动之后很容易出错,而且就算用int来存放结果,很多时候0不一定代表错误。在java中,返回0通常表示成功的。
if (!magicalManager->getTPlayerMagical().activate) {//神兵还没激活 CErrorCodeManager::throwException("ErrorGate_MagicalNotActivate"); }
if (autoBuy) { int price = 0; int shopCode = 10003; //商店类型
12.代码换行之后,该缩进的时候要缩进一下。这个我也不知道什么标准了,不过一般都是按编译器换行后的缩进。
std::string skinStr = ""; for (MapSMagicalSkinInfo::iterator iter = _magicalSkin.begin(); iter != _magicalSkin.end();//for循环中间的应该缩进吧 iter++) {//大括号中间的内容应该缩进 skinStr += "["; skinStr += ToStr(iter->second.skinCode); skinStr += ","; skinStr += ToStr(iter->second.skinGradeLevel); skinStr += ","; skinStr += ToStr(iter->second.skinStarLevel); skinStr += ","; skinStr += ToStr(iter->second.isLimitTime); skinStr += ","; skinStr += ToStr(iter->second.skinLimitTime); skinStr += "]"; }
13.对于一些复杂的内容,要保存到字符串,在放到数据库中。一般都是登陆的时候将字符串分解。保存到数据库的时候才将东西转成字符串。
void GateApp::CMagicalManager::setMagicalPromoteReturnReward(SPromoteReturnRewardInfo sPromoteReturnRewardInfo) { _magicalPromoteReturnReward[sPromoteReturnRewardInfo.promoteLevel] = sPromoteReturnRewardInfo; std::string promoteReturnRewardStr = ""; for (std::map<int, SPromoteReturnRewardInfo>::iterator iter = _magicalPromoteReturnReward.begin(); iter != _magicalPromoteReturnReward.end(); iter++) { promoteReturnRewardStr += "["; promoteReturnRewardStr += ToStr(iter->second.promoteLevel); promoteReturnRewardStr += ","; promoteReturnRewardStr += ToStr(iter->second.consumeNum); promoteReturnRewardStr += ","; promoteReturnRewardStr += ToStr(iter->second.isGet); promoteReturnRewardStr += "]"; } _tPlayerMagical.promoteReturnReward = promoteReturnRewardStr; }
这个每次变化都转一次,是没必要的。在save函数中执行,save函数在隔五分钟才会保存一次,不会过多执行。
void GateApp::CMagicalManager::save(bool logOut) { cdf::CDateTime now; SSaveInfo& saveInfo = getSaveInfo(EMPlayerMagical); if (!saveInfo.needToSave(logOut, now)) { return; } std::string skinStr = ""; for (MapSMagicalSkinInfo::iterator iter = _magicalSkin.begin(); iter != _magicalSkin.end(); iter++) { skinStr += "["; skinStr += ToStr(iter->second.skinCode); skinStr += ","; skinStr += ToStr(iter->second.skinGradeLevel); skinStr += ","; skinStr += ToStr(iter->second.skinStarLevel); skinStr += ","; skinStr += ToStr(iter->second.isLimitTime); skinStr += ","; skinStr += ToStr(iter->second.skinLimitTime); skinStr += "]"; } _tPlayerMagical.skinStr = skinStr; std::string promoteReturnRewardStr = ""; for (MapSPromoteReturnRewardInfo::iterator iter = _magicalPromoteReturnReward.begin(); iter != _magicalPromoteReturnReward.end(); iter++) { skinStr += "["; skinStr += ToStr(iter->second.promoteLevel); skinStr += ","; skinStr += ToStr(iter->second.consumeNum); skinStr += ","; skinStr += ToStr(iter->second.isGet); skinStr += "]"; } _tPlayerMagical.promoteReturnReward = promoteReturnRewardStr; saveInfo.reset(now); Message::Db::IMagicalDbPrx prx = Message::Db::IMagicalDbPrx::dynamicCast( Common::CCoreChannelManager::instance()->getProxy(Common::ECHANNEL_TYPE_DB_CACHE, Common::IMagicalDb)); prx->updatePlayerMagical_async(NULL, _tPlayerMagical); }14.一般来说一个功能一个configManager,除非很大,代码太多才会分开。
MagicalConfigManager
MagicalSkinConfigManager
MagicalAwakenConfigManager
这三个内容都不多,是在没多大必要。