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
这三个内容都不多,是在没多大必要。