凡人修真3D(3)神翼

来源:互联网 发布:真实流量软件 编辑:程序博客网 时间:2024/06/09 17:39

1.对于一些集成起来的通用配置表,如下面这样的:


还是上图吧

这种通用表,字段直接是value1,value2,value3,value4,value5,value6。

这是GameConfigData里面的类:

class CGodWingJinJieBase:public cdf::CRefShared{public:Message::Db::Tables::TGodWingConfig _godWingJinJieConfig;};typedef cdf::CHandle<CGodWingJinJieBase> GodWingJinJieBasePtr;
这是使用的代码:
if(!CGodWingConfigManager::instance()->getJinJieInfoConfig(playerGodWing.jinJieLevel, godWingJinJieConfig)){CErrorCodeManager::throwException("Public_GameConfigDataError");}//判断是否已拥有该皮肤if(!godWingManager->isSkinExist(godWingJinJieConfig.value6, true)){//添加皮肤godWingManager->addGodWingSkin(godWingJinJieConfig.value6, "", gateEntity, player);//设置皮肤godWingManager->setGodWingSkin(godWingJinJieConfig.value6, player, gateEntity);}

这样直接使用value6,以后每个人看到这里的代码,都要去数据库查一下,还得对照一下其他代码。非常麻烦。

在GameConfigData读取的时候应该进行封装,转换成可读取的字段,而不是直接使用。

修改后:

class CGodWingJinJieConfig:public cdf::CRefShared{public:int jie;//阶int jiePropNum;//消耗进阶道具数量int blessValue;//祝福值int attributeId;//属性int skillCode;//技能int skinCode;//皮肤 };typedef cdf::CHandle<CGodWingJinJieConfig> CGodWingJinJieConfigPtr; typedef std::map<int, CGodWingJinJieConfigPtr> MapCGodWingJinJieConfig;
Message::Db::Tables::SeqTGodWingConfig::const_iterator iter1 = tGodWingConfigs.begin();for(; iter1 != tGodWingConfigs.end(); iter1++){switch(iter1->type){case 1://神翼升星的经验{_starExpMap[iter1->value1][iter1->value2] = iter1->value3;}break;case 2://阶的数据{CGodWingJinJieConfigPtr jinJenConfig = new CGodWingJinJieConfig();jinJenConfig->jie = iter1->value1;jinJenConfig->jiePropNum = iter1->value2;jinJenConfig->blessValue = iter1->value3;jinJenConfig->attributeId = iter1->value4;jinJenConfig->skillCode = iter1->value5;jinJenConfig->skinCode = iter1->value6;_jinJieMap[iter1->value1] = jinJenConfig;}break;
这样取出来的配置,别人可以直接使用。

做游戏的代码,一定要时刻准备随时让别人看,因为随时要改。


2.typedef相当于重新声明了一个结构,所以开头应该大写。

typedef std::map<int, GodWingJinJieConfigPtr> mapCGodWingJinJieInfoList;typedef std::map<int, GodWingExpSkinPtr> mapCGodWingSkinInfoList;typedef std::map<int, GodWingSkillPtr> mapCGodWingSkillInfoList;typedef std::map<int, Message::Game::SPlayerGodWingCritPrompt> mapJinJieExpInfoList;
开头的m都应该改成M.

另外,对于这种别名,最好起名字的时候不要带有具体功能意思的名字,毕竟其他地方可以重用的。


3.对于拼音的命名,一个词的开头才大写。是词,不是字。

GodWingJinJieConfigPtr
应该是

GodWingJinjieConfigPtr
如果要用拼音的话。


4.除数不能为0

bool CGodWingConfigManager::getBlessingValue(int jinJieLevel, int blessingValue, bool& isLevelUp, Message::Game::SPlayerGodWingCritPrompt& cirtInfo){MapCGodWingJinjieConfig::const_iterator iter1 = _jinJieMap.find(jinJieLevel);if(_jinJieMap.end() == iter1){return false;}if (iter1->second->blessValue <= 0){return false;//这里是我加的。}//获取当前经验百分比int blessPercent = blessingValue * 10000 / iter1->second->blessValue;//load配置的地方没检测,函数内也没有检测//判断是否可以直接进阶int rand = Common::CUtil::myRand(1, 10000);for(DictIntInt::const_reverse_iterator iter2 = _jinjieLevelUpMap.rbegin();_jinjieLevelUpMap.rend() != iter2;++iter2){

5.函数不要用大写开头,类名,结构名都要用大写开头。属性不要用大写。

interface IGodWing{/** 神翼培养接口* @param type 1激活神翼系统,2神翼加经验* @param amount 使用数量* @exception --失败返回异常 使用异常代码*/["ami","amd"] void GodWingLevelUp(int type, int amount);//大写了/** 神翼进阶接口* @param autoBuy 是否自动买 0不自动,1自动* @param result 返回进阶结果 1进阶成功,2进阶失败* @param jinJieValue 返回进阶值* @exception --失败返回异常 使用异常代码*/["ami","amd"] void GodWingJinJie(int autoBuy, out int result, out int jinJieValue);//大写了/** 打开神翼系统接口*/["ami","amd"] void OpenGodWing();//<span style="font-family: Arial, Helvetica, sans-serif;">大写了</span>/** 领取返还进阶石*/["ami","amd"] void getJinJieReward();
居然还写到cdl里面了。。。。。。

6.保存数据库的时候如非必要,都是延时保存。immediatelyUpdate是立即更新的意思,一般使用changeFlag。

//保存数据库getSaveInfo(ETPlayerGodWing).immediatelyUpdate = true;save(false);

7.使用json,有好处也有坏处。好处是比较方便,节省字段。

但是坏处也很明显,

1.多了很多额外的运算

2.极大增加调试的时候查看内存

3.因为不需要声明,所以也就不知道具体有多少字段在里面。

所以一般情况下,

1.json只是用来节省字段,保存数据库的时候才转化一下。

2.对于一些大的系统json的key值放在Common/Config中

3.对于一些小的地方,只是简单一个字符串就行。

4.不保存复杂的结构,太复杂的要考虑新建一个表。

5.如果实在要保存复杂的内容,先转成string,然后保存到json中,登陆的时候,转成各自的结构,要保存的时候才转成字符串。

6.key中要写明是哪个数据类型,value一般只是基础数据类型。

总结得不好,其实我觉得最大问题是,根本没人知道你里面保存的是什么。

bool GateApp::CGodWingManager::isSkinExist(int skinCode, int flag){Json::Value json;json.parse(_playerGodWing.skinJsStr);if(flag){for(Json::Value::iterator iter = json[ALL_SKIN_KEY].begin(); json[ALL_SKIN_KEY].end() != iter; ++iter){if((*iter).isInt()){if(skinCode == (*iter).asInt()){return true;}}}}else{for(Json::Value::iterator iter = json[LIMIT_SKIN_KEY].begin(); json[LIMIT_SKIN_KEY].end() != iter; ++iter){if(!iter.key().isNull()){std::string temp = iter.key().asString();if(skinCode == atoi(temp.c_str())){return true;}}}}return false;}
像这种以后不要再用了。

8.playerExtend和playerExtend2保存

CPlayerHelper::updatePlayerExtend(gateEntity, player);//用这个player->getSaveInfo(ETPlayerExtend).changeFlag = true;//不要这样写

9.自己写的功能,要每一句代码都调试过才行。

void GateApp::CItemExtend::useProp( CGateEntityPtr gateEntity, CPlayerPtr player,::GateApp::Bag::CBagPtr bag,const CPlayerItemPtr& playerItem,Common::CBaseItemPtr item,int useAmount,const ::Message::Public::SeqString& values   ){switch( item->getTItem().type ){case EPropMountDan:{CItemExtend::useMountAttributeDan(gateEntity, bag, playerItem, item, useAmount);}break;case EPropGodWingDan:{CItemExtend::useMountAttributeDan(gateEntity, bag, playerItem, item, useAmount);//这里明显是坐骑的丹药。。。}break; 
我们做游戏的流程,测试人员只是负责做黑盒测试,因为非技术人员的员工,关注点一般都在客户端。而服务端的bug除非很明显,当然这个是很明显的。否则是测不到的,很多情况下要靠自己的编码习惯来规避一些低级错误。

10.商品价格必须从t_shop_shell中获得,

//判断元宝是否足够player->enoughMoneyException(EPriceUnitEMoney, cost * oweAmount, updateCode);//商品价格和单位必须从t_shop_shell中获得,//购买道具CShopManager::instance()->buyItem(gateEntity, SHOP_CODE_AUTO_BUY, itemCode, oweAmount, updateCode);//这里根本不需要购买道具,而是在下面直接扣钱
不能自己找个地方配置。

使用商品的时候必须使用t_shop_shell的unit和amount。

商店打折活动肯定会有的。这个必须要注意。


11.对于bool的内容,做判断的时候不要再==true或者==false了。

tlogGodWing.oldLevel = (true == flag) ? playerGodWing.jinJieLevel - 1 : playerGodWing.jinJieLevel;tlogGodWing.newLevel = playerGodWing.jinJieLevel;tlogGodWing.newExp = playerGodWing.blessingValue + playerGodWing.limitBlessingValue;tlogGodWing.oldExp = oldBless;tlogGodWing.addExp = (true == isLevelUp) ? totalExp - oldBless : exp;


12.在一般的消耗某种道具,然后获得某种属性的流程中,要先判断所有可能抛错误码的情况,然后再去执行扣除道具,最后是获得东西。

按具体的例子来说吧:

在神翼进阶的流程中,先判断了是否有当前配置,然后根据道具为空判断是否为最大阶数。其实是没必要的。下一个阶的配置是无论如何都要获取的。

//获取进阶信息CGodWingJinjieConfigPtr jinjieConfig = CGodWingConfigManager::instance()->getJinJieConfig(playerGodWing.jinJieLevel); if (!jinjieConfig){CErrorCodeManager::throwException("Public_GameConfigDataError");}//判断是否最高阶数if (0 == jinjieConfig->jiePropNum)//不消耗道具,是最大阶{CErrorCodeManager::throwException("Public_jinJieMaxLevel");}
这是后面,消耗道具之后,再去获取下一阶的配置。如果这里出现问题,之前计算的内容,都白费了,而且还白扣钱了。
CGodWingJinjieConfigPtr nextJinjieConfig = CGodWingConfigManager::instance()->getJinJieConfig(playerGodWing.jinJieLevel + 1);if (!nextJinjieConfig){CErrorCodeManager::throwException("Public_jinJieMaxLevel");}
这里可以考虑一个问题。在程序运行过程中,极小概率出现意想不到的情况,是先扣除玩家道具呢?还是先给玩家属性?

按理说,玩家处于弱势,扣了钱,不给属性,非常亏,一怒之下可能都不想玩了。好像我们应该优先为玩家考虑的样子。

但是实际情况是,市面上存在各种各样的外挂以及各种各样想占便宜的玩家。如果玩家发现了bug,而这个bug对自己有利,可以不劳而获,即使是极小概率,他们也会去研究,将这种特殊情况出现的概率大大提高,甚至每次都出现。如果是玩家自己发现的还好,可能他自己偷偷刷bug,我们发现了就处理了,没发现也只是影响一个服的问题。但是如果是被外挂利用了,可能在极短时间内,被无数人疯狂刷,这个时候很可能我们就得回档,即使没这么惨,也得各种补偿,对游戏的生命周期大大影响。而且非常打击正常玩家的信心。

说多了,总之是先扣钱,再给属性。

在写代码的时候要有意识,先把可能抛错的放前面,到了扣物品扣钱的那一步开始,通常就不能再去抛错了。

13.还是那一句,起名是非常重要的,关乎后来者能不能正确看你的代码。

这是神翼进阶的代码,同一个函数内的。

int totalExp = jinjieConfig->blessValue; int totalexp = playerGodWing.blessingValue + playerGodWing.limitBlessingValue;

14.不要在一个语句里面做太多的计算。

其实我是想说三目运算符?:

tlogGodWing.addExp = ( isLevelUp) ? totalExp - oldBless : exp;
出现这种情况,通常都是上面已经分成很多分支了,定义个addExp,在其中复制就好了。可以代码更加清晰。

15.客户端调用接口的时候,一些不是很紧要的代码,可以放在Response之后。

GodWingJinJieCB->cdeResponse(isLevelUp, exp);//for logMessage::Db::Tables::TLogGodWing tlogGodWing;tlogGodWing.__init();tlogGodWing.userName = player->getTPlayer().username;
就像这样,日志的保存,放在response之后。


16.bool不要和int混用

//判断皮肤是否已拥有,flag为true检测永久卡中是否已拥有该卡,false检测限时卡中是否已拥有该卡bool isSkinExist(int skinCode, int flag);
一般情况下是不会有问题的,但是如果出问题的时候,只能呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵了,碰到过,非常难找。尽量不要用默认类型转换。这相当于削弱了C++强类型的属性,变成弱类型,就会像客户端的Lua一样,非常多bug,而且很难修的。

17.检测限时的皮肤的时候,要判断一下时间是否过期了。虽然已经有每分钟检测的定时器,但是出于严谨,还是要检测一下。

bool GateApp::CGodWingManager::isSkinExist(int skinCode, bool isForever){ if (isForever){for (SeqInt::iterator iter = _foreverSkins.begin();iter != _foreverSkins.end();iter ++){if (skinCode == *iter){return true;}} }else{DictIntInt::iterator iter = _limitSkinMap.find(skinCode);if (iter != _limitSkinMap.end()){cdf::CDateTime now;if (iter->second > now.getTotalSecond())//-----这个检测是我加的,本来没有的----------------{return true;//还没过期}} }return false;}


18.对于穿戴皮肤,传0过来就是脱掉了。没必要弄多一个接口。语义上也是一样的,分出来很多余。

void::Message::Game::IGodWingImpl::setGodWingSkin_async(const ::Message::Game::AMD_IGodWing_setGodWingSkinPtr& setGodWingSkinCB,int skinCode,const ::cde::CContext& context)
下面这个是多余的。

void::Message::Game::IGodWingImpl::takeOffGodWingSkin_async(const ::Message::Game::AMD_IGodWing_takeOffGodWingSkinPtr& takeOffGodWingSkinCB,const ::cde::CContext& context)

19.有些函数里面会抛异常,在外面就不用再去抛了。

GateApp::Bag::CBagPtr roleBag = CBagHelper::getBag(gateEntity, EPlayerItemPosTypeRole);if(!roleBag){CErrorCodeManager::throwException("ErrorGate_BagNotExist");}
实际上函数里面已经抛异常了。。。

GateApp::Bag::CBagPtr GateApp::CBagHelper::getBag( const CGateEntityPtr& gateEntity,const ::Message::Public::EPlayerItemPosType& type, bool isException /*= true*/ ){::GateApp::Bag::CBagSystemPtr bagSystem = getBagSystem(gateEntity, isException);if (bagSystem == NULL){return NULL;}GateApp::Bag::CBagPtr bag = bagSystem->getBag( type );if ( ! bag && isException ){Common::CErrorCodeManager::throwException( "ErrorGate_BagNotExist" );}return bag;}

20.对于某些属性,过了某个时间失效的情况。设个定时器,到了这个时间点,就把所有的都改掉。这是策划的思想,而不是程序员的做法。

int CPlayerGodWingTimer::handleTimeout(const cdf::CDateTime ¤tTime, const void *act){CGateEntityPtr gateEntity = CGateEntityManager::instance()->findGateEntity( _entityId );if (!gateEntity){return 0;}CPlayerPtr player = CPlayerPtr::dynamicCast(gateEntity->getComponent(ECOMPONENT_TYPE_PLAYER));if(!player){return 0;}CGodWingManagerPtr godWingManager = CGodWingManagerPtr::dynamicCast(gateEntity->getComponent(ECOMPONENT_TYPE_PLAYER_GODWING));if(!godWingManager){return 0;}//等级判断if (player->isFunctionOpen("GodWing")){//清空限时祝福值godWingManager->checkLimitBless(player);}gateEntity->removeTimer(ETIMER_TYPE_PLAYER_GOD_WING_TIMER);CPlayerGodWingTimerPtr timer = new CPlayerGodWingTimer();timer->_entityId = _entityId;cdf::CReactor::instance()->schedule(timer, NULL,cdf::CInterval(1, 5, 0, 0, 0), 0);gateEntity->addTimer(ETIMER_TYPE_PLAYER_GOD_WING_TIMER, timer);return 0;}

定时器是个很方便的东西,但是用多了可能造成负担。而且这种限定时间,对所有玩家都有效的情况。可能会造成集中时间爆发大量计算。那个时刻会很卡。

而且五点钟,应该是很少人的时候,系统一般都进行一些比较耗时的操作,更新升级维护备份什么的。说多了,就是尽量不要用定时器。

正确的做法是在用到的时候,检测一下,发现时间过时了,就更新一下。

void::Message::Game::IGodWingImpl::godWingJinJie_async(const ::Message::Game::AMD_IGodWing_godWingJinJiePtr& GodWingJinJieCB,int autoBuy,const ::cde::CContext& context){CGateEntityPtr gateEntity;CPlayerPtr player;CGateHelper::getPlayerAndEntity(context, gateEntity, player);//参数检查CCheckInput::validValue(autoBuy, 0, 1);CGodWingManagerPtr godWingManager = CGodWingManagerPtr::dynamicCast(gateEntity->getComponent(ECOMPONENT_TYPE_PLAYER_GODWING));if(!godWingManager){CErrorCodeManager::throwException("Public_NullPointer");}godWingManager->checkLimitBless(player);

makeClientGodWingInfo函数里面也检测一下就够了。


21.虽然我们做的不是网站,但是也要有MVC的概念。发送给客户端的信息应该相对独立。不能从发送给客户端的消息中触发属性的计算。假如没发到呢,难道属性就不用算了?

void GateApp::CGodWingManager::makeClientGodWingInfo(Message::Game::SPlayerGodWingInfo& playerGodWing, CPlayerPtr& player, CGateEntityPtr& gateEntity){。。。此处省略。。。if(_isNeedcalc){getGodWingAllAttribute(gateEntity, player, playerGodWing.attributeInfo);getGodWingBaseAttribute(playerGodWing.baseAttribute);_allAttribute = playerGodWing.attributeInfo;_baseAttribute = playerGodWing.baseAttribute;_isNeedcalc = false;}else{playerGodWing.attributeInfo = _allAttribute;playerGodWing.baseAttribute = _baseAttribute;}}

22.过度依赖json,写出来的代码,调试的时候根本没法看到内存。

void GateApp::CGodWingManager::getActivedSkinEquipSkills(int skinCode, Message::Public::DictIntInt& skillList){std::string temp1 = _activeSkinSkillJson[ToStr(skinCode)].asString();if(temp1.empty()){return;}Json::Value js;js.parse(temp1);std::string temp2 = js[ACTIVE_EQUIT_SKILL_LIST].asString();if(temp2.empty()){return;}std::vector<std::string> VecSkill;cdf::CStrFun::split(VecSkill, temp2.c_str(), ';');for(std::vector<std::string>::const_iterator it = VecSkill.begin(); VecSkill.end() != it; ++it){std::vector<std::string> temp;cdf::CStrFun::split(temp, it->c_str(), ',');if(2 != temp.size()){continue;}skillList[atoi(temp[0].c_str())] = atoi(temp[1].c_str());}}
一层套一层的,无法看。如此复杂的结构,应该新建一个表来保存内容。

后台php统计的时候用到json也是很麻烦的,这样最终还是拖慢整个计算的速度。

















1 0
原创粉丝点击