游戏服务器之ai

来源:互联网 发布:java大型游戏源代码 编辑:程序博客网 时间:2024/04/29 10:42

ai是游戏的一个重要组成部分,指的是玩家与npc之间的相互活动。

设计上:

npc  ai流程:逻辑线程主循环-->场景管理器遍历->场景循环-->遍历所有npc-->npc循环-->ai状态机切换和ai状态处理

ai主要功能包括

正常状态:ai状态切换和ai状态处理

死亡状态:死亡状态机


ai状态切换

根据状态机来切换。

游荡状态->追击状态 或 攻击状态

追击状态->攻击状态 或 回归状态

攻击状态->追击状态 或 回归状态

回归状态->游荡状态


ai状态处理

(1)发呆状态(不做事情)

(2)游荡状态 (随机移动)

(3)追击状态(ai的寻路是使用网格计算,目前是a星的优化算法)

(4)攻击状态(战斗使用普通屏索引、优化计算使用有效屏索引)

(5)回归状态 (目前刷新到出生点)

 

数据结构

AI状态控制器

ai类型(ai活动逻辑)


ai状态机

(1)ai类型之间的切换(ai状态机切换)。

(2)ai类型的执行。

(3)ai对象的死亡循环执行。


处理时机

(1)在npc的循环里执行ai状态的切换和ai类型动作

(2)处理技能状态定时器和npc定时状态

(3)死亡状态下会有死亡循环。

 1、npc总体流程

npc的ai循环的调用层次:

逻辑线程主循环-->场景管理器遍历->场景循环-->遍历所有npc-->npc循环-->ai状态机切换和ai状态处理

(1)npc循环

bool scene_npc::loop(){const SceneObjectState &state = getEntryState();if(state == SceneObject_Normal)//npc 正常状态{if (AIC)//ai控制器{if(this->base->npcType == MSG::NpcType_Gun){gunAction();//特殊类型npc 的ai状态}else{AIC->processPhase();//ai状态机切换normalAction();//ai状态处理}}if(_one_sec(main_logic_thread::currentTime)){this->statem->stateTimer();//技能状态管理器定时器(执行技能状态对象),参考:http://blog.csdn.net/chenjiayi_yun/article/details/19429133this->statem->loop();//技能状态循环(回收标识销毁的技能状态对象)//死亡状态life  为0 ,_life_timer 定时器到时if(npcState.life && (_life_timer)(main_logic_thread::currentTime)){killMe();//npc死亡九屏同步,死亡处理}timeCount++;//根据策划配置时间定时回血 ,采集状态检查......                    }}else if(state == SceneObject_Death){                this->statem->deleteAll();//npc死亡删除状态管理器的所有状态对象deathLoop();//npc 对象死亡状态后的死亡循环处理}        return true;}


2、ai状态机

(1)ai类型

ai类型实际上就是复杂动作的一个结合体。可以直接在c++代码里面写,也可以写在脚本方便热切换 ,针对ai的处理又要耗费较多的cpu时间,所以一般就在c++里面写。

ai类型之间的切换是在AI状态切换控制器里面管理的。


目前主要的ai类型有如下:

enum eAIType{NPC_AI_NORMAL,///游荡NPC_AI_MOVETO,///追击NPC_AI_ATTACK,///攻击NPC_AI_RETURN_TO_REGION,///回归NPC_AI_STUN,//发呆};


ai类型定义:

struct t_NpcAIDefine{///类型,NPC在该阶段的主要动作eAIType type;///位置 根据不同动作位置的意义也略不相同///移动时表示目的地,其他表示活动范围中心nPos pos;///追击目标点nPos chaseLoc;///范围 ///移动时表示到达目的地的判定范围,其他表示活动范围int32 regionX, regionY;};

(2)AI状态控制器

AI状态控制器(是npc对象scene_npc的成员)来修改ai状态,记录当前ai动作类型,来根据当前ai类型执行动作。

AI控制器:

保存当前和上一个ai类型

当前ai类型的转换(切换条件判断)

class NpcAIController{///本控制器控制的npcscene_npc * npc;///当前的AI类型和保存的前一个AI类型t_NpcAIDefine curAI,oldAI;///活动范围的中心nPos actPos;//追击的半径nPos chasePos;///活动范围的宽和高int actRegionX, actRegionY;/** * \description 判断Npc是否走出了活动范围之外 * 如果没有在跟踪用户状态,需要在活动范围行走 * 追逐时范围扩大10 * \return 是否超出活动范围 */bool outOfRegion() const;//回到活动范围,追踪敌人超出范围时以5倍速返回/** * \description 设置回到范围的AI使NPC回到活动范围 * 如果NPC是跟踪敌人而超出了活动范围,则以5倍速返回活动范围内,同时放弃跟踪目标 */void returnToRegion();/** * \description 判断npc是否到达某位置的某范围内(范围判断函数) * \param pos 中心位置,默认是当前AI的目标位置 * \param regionX 范围宽,默认是当前AI的范围宽 * \param regionY 范围高,默认是当前AI的范围高 * \return 是否在范围内 */bool arrived(nPos pos = nPos(0,0), int x = -1, int y = -1);void setNormalAI();/** * \description 设置NPC的活动范围 * \param pos 中心位置 * \param x,y 范围的宽和高 * \return  */void setActRegion(nPos pos = nPos(0,0), int x = -1, int y = -1);/** * \description 设置NPC的活动范围 * \param pos 左上位置 * \param x,y 范围的宽和高 * \return  */void setActRegionS(nPos pos = nPos(0,0), int x = -1, int y = -1);/** * \description 得到NPC的活动范围 * \param pos 输出:中心位置 * \param x,y 输出:范围的宽和高 */void getActRegion(nPos &, int &, int  &);/** * \description 状态切换管理 * 该方法在scene_npc::loop中执行 */void processPhase();//当前ai定义t_NpcAIDefine *GetCurAI() {return &curAI;}};

(2-1)ai类型切换

ai的状态切换函数处理,进行ai状态的切换条件的判断和执行切换

void NpcAIController::processPhase(){switch(curAI.type){case NPC_AI_STUN://发呆状态{if(this->stunTime  < main_logic_thread::currentTime._msec &&this->npc->enm->enemySize == 0)//没有敌人,并在发呆时间内{int rand = nMisc::randBetween(0,100);//随机跳入游荡状态if(rand > 90){curAI.type = NPC_AI_NORMAL;}return;}else{curAI.type = NPC_AI_NORMAL;}}break;case NPC_AI_NORMAL://游荡状态{//获取仇恨列表中的最近的玩家scene_player* player = (scene_player*)this->npc->enm->getNearestEnemy();if(!player)//在仇恨列表里没有目标则搜索警戒范围内的目标{//警戒范围之内发现了目标,切换Ai,移向敌人//指定范围上的点是根据npc对象的坐标点为中心的,一定范围内的屏上的玩家对象(玩家类型的)的列表中的其中一个//需要判断玩家是否处于正常状态(可攻击状态)player =  = npc->scene->searchPlayer(npc->getPos(), npc->base->aleRad);//aleRad 是警戒半径}if(player)//找到目标{oldAI = curAI;//保存当前的ai类型nPos posp = player->getPos();nPos posn = npc->getPos();uint16 dis = nGraphAlgo::getDistance(posp, posn);//玩家与npc之间的距离if(!player->scene->map->checkSafeBlock(posp))//只要该玩家的位置不是在安全区域内(这样才能对其发动攻击){if(dis <= npc->base->atkRange)//距离比攻击距离(以NPC进入战斗坐标为中心,该值为半径的一个圆形区域)要小{//游荡状态转到攻击状态curAI.type = NPC_AI_ATTACK;curAI.chaseLoc = posp;//目标点(玩家位置)curAI.pos = posn;//当前点(npc位置)npc->target = player->id;this->npc->enm->addEnemy(player->id, 50, player->roledata.job);//添加玩家到npc的仇恨列表(每次增加50仇恨值)}else if(dis <= npc->base->purRad)//距离比追击半径(以NPC进入战斗坐标为中心,该值为半径的一个圆形区域)要小{//游荡状态转到追击状态curAI.type = NPC_AI_MOVETO;npc->changeNpcSpeed();//根据当前ai类型修改当前移动速度(游荡状态或追击状态)curAI.regionX = posp.x;curAI.regionY = posp.y;curAI.chaseLoc = posp;curAI.pos = posn;npc->target = player->id;this->npc->enm->addEnemy(player->id, 50, player->roledata.job);}}}}break;case NPC_AI_MOVETO://追击敌人状态{nPos posn = npc->getPos();bool flag = false;for(uint16 i = 0; i < this->npc->enm->enemySize; i++)//按仇恨度排列仇恨列表{scene_player *player = this->npc->enm->getAttacker(this->npc->enm->enemy[i].id);if(player) //先检查目标敌人是否还在线{if(player->getEntryState() != SceneObject_Normal){continue;}if(player->scene == npc->scene)//检查目标是否已经切换地图{if(!player->scene->map->checkSafeBlock(player->getPos())){nPos posp = player->getPos();uint16 dis = nGraphAlgo::getDistance(posp, posn);//角色和npc的距离uint16 runoffDis = nGraphAlgo::getDistance(nPos(this->npc->birthData->pos_x, this->npc->birthData->pos_y), posn);if(runoffDis < npc->base->purRad)//判断追击距离,这里需要加判断最大追击距离{flag =  true;if(dis <= npc->base->atkRange)//攻击距离{curAI.type = NPC_AI_ATTACK;//转换成攻击ai状态curAI.pos = posn;curAI.regionX = posp.x;curAI.regionY = posp.y;npc->target = player->id;//找到需要攻击的玩家}else{curAI.type = NPC_AI_MOVETO;//转换成追击ai状态curAI.regionX = posp.x;curAI.regionY = posp.y;npc->target = player->id;//找到需要追击的玩家}break;}}}}}if(!flag){curAI.type = NPC_AI_RETURN_TO_REGION;//转换为回归ai状态curAI.chaseLoc = nPos(0, 0);npc->target = 0;}}break;case NPC_AI_ATTACK://攻击状态(在某范围内攻击){bool flag = false;for(uint16 i = 0; i < this->npc->enm->enemySize; i++){scene_player *player = this->npc->enm->getAttacker(this->npc->enm->enemy[i].id);//从仇恨列表获取攻击者if(player) ///攻击目标还在{nPos posn = npc->getPos();nPos posp = player->getPos();if(player->getEntryState() != SceneObject_Normal)//需要是npc正常状态{continue;}//检查是否在安全区域if(!player->scene->map->checkSafeBlock(player->getPos())){uint16 dis = nGraphAlgo::getDistance(posp, posn);uint16 runoffDis = nGraphAlgo::getDistance(nPos(this->npc->birthData->pos_x, this->npc->birthData->pos_y), posn);if(runoffDis < npc->base->purRad)//在追击范围内{//死亡了或者切换了场景if(player->scene != npc->scene){continue;}else if(dis > npc->base->atkRange){curAI.type = NPC_AI_MOVETO;//切换到追击状态curAI.regionX = posp.x;curAI.regionY = posp.y;npc->target = player->id;flag = true;break;}else//在攻击范围内{curAI.type = NPC_AI_ATTACK;//下一个状态依然是攻击状态curAI.regionX = posp.x;curAI.regionY = posp.y;npc->target = player->id;flag = true;break;}}}}}if(flag == false){curAI.type = NPC_AI_RETURN_TO_REGION;//切换到回归状态(回到原来的出生点)curAI.chaseLoc = nPos(0, 0);npc->target = 0;}}break;case NPC_AI_RETURN_TO_REGION://没有可追击的仇恨敌人就进入回归状态(目前会刷新到出生点){this->npc->enm->clearAll();nPos posn = npc->getPos();curAI.chaseLoc = nPos(0, 0);npc->target = 0;curAI.type = NPC_AI_NORMAL;npc->changeNpcSpeed();//切换到游荡速度}break;default:break;}}}


3、ai类型处理

ai处理的主体函数,处理npc的在当前ai状态下的动作

bool scene_npc::normalAction(){switch (AIC->curAI.type){case NPC_AI_NORMAL://游荡状态{return aiMoveRandom();//随机移动}break;case NPC_AI_MOVETO://追击状态{return doMovetoAI();//a星寻路追击}break;case NPC_AI_ATTACK://攻击状态{   return doAttackAI();//技能攻击}break;case NPC_AI_RETURN_TO_REGION://回归状态{return doReturnToRegionAI();//直接刷新到出生点(可根据需求a星寻路到出生点)}break;default:{return false;}break;}return false;}


(1)ai随机移动

bool scene_npc::aiMoveRandom(){if(this->base->wanRad == 0)//有警戒半径的才计算移动{return true;}//怪物随机的跑动的坐标的//怪物的寻路半径if(_move_timer(main_logic_thread::currentTime)){if(nMisc::randBetween(0,100) < 90)//概率选择移动或者不移动{return false;}if(isset_state(svalue->boolSet, MSG::BoolValueType_CanNotMove))//判断状态是否可以移动{return false;}_move_timer.reset(this->base->walksp, main_logic_thread::currentTime);//移动速度设置定时器int direct = nMisc::randBetween(0,7);//随机移动方向和移动步长int step = nMisc::randBetween(1,2);//移动步长为1或2nPos newPos;newPos.x = getPos().x +  MSG::walk_adjust[direct][0];newPos.y = getPos().y +  MSG::walk_adjust[direct][1];//八方向移动if(nGraphAlgo::getDistance(nPos(this->birthData->pos_x, this->birthData->pos_y), newPos) <= this->base->wanRad)//移动新位置跟出生位置小于等于警戒半径{//是受到定身效果,无法移动if(this->statem && this->statem->getState(StateValueType_Immobilized)){_move_timer.reset(this->base->walksp * step, main_logic_thread::currentTime);return true;}move(direct, step);//根据移动方向和移动步长来移动_move_timer.reset(this->base->walksp * step, main_logic_thread::currentTime); //重新设置移动(速度)定时器return true;}}return false;}
移动八方向
static const int walk_adjust[][2] ={{ 0, -1 },{ 1, -1 },{ 1, 0 },{ 1, 1 },{ 0, 1 },{ -1, 1 },{ -1, 0 },{ -1, -1 },{ 0, 0 } };

(2)追击ai处理

bool scene_npc::doMovetoAI(){if(_move_timer(main_logic_thread::currentTime)){if(this->base->runsp)//有追击速度的才计算寻路{_move_timer.reset(this->base->runsp, main_logic_thread::currentTime);if(this->statem && this->statem->getState(StateValueType_Immobilized))//受到定身效果则不计算寻路{return true;}bool ret = goToFindPath(this->getPos(), nPos(AIC->curAI.regionX,AIC->curAI.regionY));//ai寻路计算(如果目标点改变就清空路径,有路径点就移动,没有路径点就寻路)        return ret;}}return false;}
使用到优化a星寻路,参考:http://blog.csdn.net/chenjiayi_yun/article/details/19506663

(3)攻击ai处理

bool scene_npc::doAttackAI(){if(_attack_timer(main_logic_thread::currentTime)){scene_player* player = g_player_mgr.get_player_by_id(this->target);if(player){uint16 skill_num = base->skills.size();if(skill_num >= 1){int32 index = nMisc::randBetween(0, skill_num - 1);Skill*skill = skillm.getSkillByID(base->skills[index].id);//随机获取一个技能if(skill){uint16 dir = nGraphAlgo::getDirect(this->getPos(), player->getPos());if(dir != this->getDir()){this->setDir(dir);//设置朝向this->sendMeToNine();// 方向改变需要广播}_attack_timer.reset(this->base->attacksp, main_logic_thread::currentTime);MSG::stAttackMagicUserCmd send;send.attackerID = this->tempid;send.entryType = SceneObject_NPC;send.skillID = base->skills[index].id;send.direct = nGraphAlgo::getDirect(this->getPos(), player->getPos());send.defencerID = player->id;send.defenderType = SceneObject_Player;send.x = player->getPos().x;send.y = player->getPos().y;return ScenePk::action(this, &send, skill);//怪物攻击玩家}}else{g_log->debug("怪物【%u】没有学习技能,表格错误了",base->id);}}else{   //大炮类型npcif(MSG::NpcType_Gun == this->base->npcType){uint16 skill_num = base->skills.size();if(skill_num >= 1){nPos dropPoint;int32 index = nMisc::randBetween(0, skill_num - 1);//随机获取技能Skill*skill = skillm.getSkillByID(base->skills[index].id);//获取技能if(skill){       //获取技能掉落位置(掉落地点几率跟玩家密集程度有关)this->scene->getEntryFrequence(nPos(this->birthData->pos_x, this->birthData->pos_y), skill->base->radius, this , dropPoint);_attack_timer.reset(this->base->attacksp, main_logic_thread::currentTime);//重新设置攻击定时器MSG::stAttackMagicUserCmd send;//发送攻击消息send.attackerID = this->tempid;send.entryType = SceneObject_NPC;send.skillID = base->skills[index].id;send.direct = nGraphAlgo::getDirect(this->getPos(), dropPoint); //发射方向send.defencerID = 0;send.defenderType = 0;send.x = dropPoint.x;send.y = dropPoint.y;//g_log->debug("怪物【%u】【%s】释放技能:%u",this->tempid, this->name, base->skills[index].id);return ScenePk::action(this, &send, skill);//执行技能}}}}}    return false;}
执行技能参考:http://blog.csdn.net/chenjiayi_yun/article/details/19429133  技能作用-技能释放
递归获取玩家较密集的点(越密集概率越大)

void Scene::getEntryFrequence(const nPos& center, uint16 r, scene_pk_object *ptrNpc ,nPos &pos){if (!ptrNpc || !ptrNpc->isNpc()){return;}//递归的获取的实体出现的频率最高if(r == 1 || r >= 20){pos = center;//debug_log(".........................................[x=%u,y%u]",pos.x, pos.y);return;}enum ePosDIR{LEFTUP_DIR,RIGHTUP_DIR,LEFTDOWN_DIR,RIGHTDOWN_DIR,MAX_DIR};nPos posArray[4] = {nPos(center.x - r/2, center.y - r/2), nPos(center.x + r/2, center.y- r/2), nPos(center.x - r/2, center.y + r/2),nPos(center.x + r/2, center.y+r/2)};DefenceTargetList target[4];searchEntryRound(posArray[0], r/2, ptrNpc, target[LEFTUP_DIR]);searchEntryRound(posArray[1], r/2, ptrNpc, target[RIGHTUP_DIR]);searchEntryRound(posArray[2], r/2, ptrNpc, target[LEFTDOWN_DIR]);searchEntryRound(posArray[3], r/2, ptrNpc, target[RIGHTDOWN_DIR]);uint16 leftup = target[LEFTUP_DIR].size();uint16 rightup = target[RIGHTUP_DIR].size();uint16 leftdown = target[LEFTDOWN_DIR].size();uint16 rightdown = target[RIGHTDOWN_DIR].size();uint16 total = leftup + rightup + leftdown + rightdown;if(total == 0){if(center.x - r >= 0 &&  center.y-r >= 0){nPos leftup(center.x - r, center.y-r);uint16 x =  nMisc::randBetween(0, 2*r);uint16 y =  nMisc::randBetween(0, 2*r);pos = leftup + nPos(x,y);return;}}uint16 array[4] = {leftup, leftup + rightup, leftup+ rightup+leftdown, total};int32 rate = nMisc::randBetween(1, total);for(uint16 i = 0; i < MAX_DIR; i++){if(rate <= array[i]){pos = posArray[i];getEntryFrequence(posArray[i], r/2,ptrNpc, pos);break;}}}

(4)回归ai处理

bool scene_npc::doReturnToRegionAI(){  //刷新怪物的位置  if(freshPos(nPos(this->birthData->pos_x, this->birthData->pos_y)))  {    return true;  }  return false;}

拓展思考:

在限制的时间内处理ai的类型,超出时间就打断,进行其他的npc的逻辑,和其他模块的逻辑。

4、ai对象死亡循环

死亡循环,需要按死亡状态机执行死亡步骤

死亡的步骤分为以下:

enum NpcDeathStep{    NpcDeathStep_None,    NpcDeathStep_DeathAction,//死亡动作    NpcDeathStep_LostItem,//掉落物品    NpcDeathStep_CheckRecycle,//检查回收(可以在回收前执行一些操作)    NpcDeathStep_Recycle,//从容器中回收    NpcDeathStep_WaitRelive,//等待重生    NpcDeathStep_MAX};
npc的死亡状态机

void scene_npc::deathLoop(bool allOnce){if( doneProcess && (!(_death_loop_timer)(main_logic_thread::currentTime))){return;}do{switch(_death_step){case NpcDeathStep_LostItem://掉落物品{if(!isKilledBySystem() && base->lost.size() != 0){lostDeathItem();}_death_step = NpcDeathStep_CheckRecycle;//g_log->debug("物品掉落");}break;case NpcDeathStep_CheckRecycle://检查回收前准备{if(1 == this->_typetag ){if(scene->_wave - this->_wave > 0){_need_recycle = true;// 从容器中删除std::list<scene_npc*>::iterator it;for (it = scene->_summonlist.begin(); it != scene->_summonlist.end(); it++){if(this == *it){scene->_summonlist.erase(it);//从召唤列表中删除该npc// 强制发倒地消息MSG::stRefreshHpMpSpPropertyUserCmd send;send.id = this->tempid;send.type = SceneObject_NPC;send.hp = 0;send.hpMax = this->getMaxHp();send.level = this->getLevel();this->sendmsgToNine(&send, sizeof(send));break;}}}}//检查是否需要回收if(_need_recycle == true || birthData->interval == 0)//检查是否标记直接回收或者没有出生时间间隔{if(1 == this->_typetag){deathcopytime = 0;}_death_step = NpcDeathStep_Recycle;//进入死亡回收状态}else{_death_loop_timer.reset(birthData->interval*1000, main_logic_thread::currentTime);//有出生时间间隔的,则设置出生定时器doneProcess = true;_death_step = NpcDeathStep_WaitRelive;//进入死亡等待重生状态if((this->base->npcType == MSG::NpcType_Monster && this->base->subType == MSG::NpcSubType_Mapboss)){    MSG::Social::stBossDieReliveMsg send;    uint8 lineid = MAPID2LINENUM(this->scene->mapID);    if(this->scene->mapID == 18)        lineid = 1;    send.id = mapid_lineid_hash(this->scene->map->id,lineid,this->base->id);     send.bossid = this->base->id;     send.time = birthData->interval / 60;     socialClient->sendmsg(&send,sizeof(send));    g_log->info("怪物【%u,%s】死亡时间:%u",this->base->id, this->name,send.time);}}}break;case NpcDeathStep_Recycle://死亡回收该npc{if(1 == this->_typetag ){if(++deathcopytime < 3000){return;}// 从_summonlist容器中删除std::list<scene_npc*>::iterator it;for (it = scene->_summonlist.begin(); it != scene->_summonlist.end(); it++){if(this == *it){scene->_summonlist.erase(it);break;}}}_death_step = NpcDeathStep_None;unreg();}break;case NpcDeathStep_WaitRelive:<span style="font-family: Arial, Helvetica, sans-serif; font-size: 12px;">//设置等待重生</span>{    setRelive();    if((this->base->npcType == MSG::NpcType_Monster                 && this->base->subType == MSG::NpcSubType_Mapboss) || _is_gold_monster)//通知社会关系服务器(用于统计)    {        MSG::Social::stBossDieReliveMsg send;        uint8 lineid = MAPID2LINENUM(this->scene->mapID);//地图id获取线号        if(this->scene->mapID == 18)            lineid = 1;        send.id = mapid_lineid_hash(this->scene->map->id,lineid,this->base->id);         send.bossid = this->base->id;         send.time = 0;         socialClient->sendmsg(&send,sizeof(send));        g_log->info("怪物【%u,%s】复活:%u",this->base->id, this->name,send.time);    }    _death_step = NpcDeathStep_None;}break;default://没有处理的状态均要return,否则死循环 return;}if(allOnce == false)//是否一次执行完除了重生之外的所有死亡步骤.true表示所有步骤执行完{return;}} while(true);}


5、npc配置

npc配置结构NpcB(npc的基础配置信息):

struct NpcB: public base_object {typedef base_object super;    uint32                    id;                           /**< 编号 */    char                      npcName[MAX_NAME_LEN];        /**< 名称 */    uint8                     npcType;                      /**< 类型 (1.NPC,2普通怪,3精英怪,4BOSS,5采集物)*/    uint8                     subType;                      /**< 子类型 (1仓库,2拍卖行,3任务,4商店 ,5无功能)*/    std::set<uint32>          shops;                        /**< 出售道具组ID 需要对应商店道具组表,暂填空*/    std::set<uint32>          quests;                       /**< NPC身上挂的任务ID */    uint32                    level;                        /**< NPC等级 */    uint32                    wanRad;                       /**< NPC游荡半径;以NPC出生点为圆心,该值为半径,确定NPC的游荡区域 */    uint32                    atkRange;                     /**< 攻击距离 */    uint32                    aleRad;                       /**< 警戒半径;以NPC当前坐标为中心,该值为半径的一个圆形区域 */    uint32                    purRad;                       /**< 追击半径;以NPC进入战斗坐标为中心,该值为半径的一个圆形区域 */    uint16                    stopHurTime;                  /**< 停止伤害时间;NPC停止对其目标造成伤害的时间,单位毫秒 */    uint32                    walksp;                       /**< 待机移动速度 */    uint32                    runsp;                        /**< 追击移动速度 */    uint32                    attacksp;                     /**< 攻击速度 */    uint32                    hpMax;                        /**< 生命值上限 */    uint32                    hpReturn;                     /**< 自动生命值 */    uint32                    atkMax;                       /**< 最大攻击力 */    uint32                    atkMin;                       /**< 最小攻击力 */    uint32                    def;                          /**< 防御力 */    uint32                    accuracy;                     /**< 命中 */    uint32                    avoid;                        /**< 闪避 */    std::vector<NpcSkill>     skills;                       /**< 可以使用的所有技能 */    uint32                    exp;                          /* 携带的经验值 */                       std::vector<std::vector<NpcLost> >  lost;               /* 掉落 */    GroupEquipAttrbuteList    lostEquipAttrL;               //npc掉落装备属性组列表    TransLostList             transLostL;                   // npc转生掉落   uint32                     stunTime;                     // npc发呆时间};

6、场景npc对象(容器)

scene_npc类(场景npc)是继承于场景对象类(SceneEntry)的 。

场景对象包括:npc、玩家、场景道具

场景npc(静态npc不计算)的出现和消失会记录在场景屏索引容器的npc类型容器里。

(1)场景npc实体的容器

添加场景npc到场景实体容器

template< class MapT, class GatewayConnT, class SceneEntryT>bool nIndexScene< MapT, GatewayConnT, SceneEntryT>::add_sceneEntry(SceneEntryT *entry, const nPos &newPos){if(entry->inserted == true){assert_debug(false && "已经添加");g_log->error("向屏索引插入 %s 失败 (%u,%u)", entry->name, newPos.x, newPos.y);return false;}nPos old = entry->getPos();//新加入地图if(!entry->setPos(newPos)){assert_debug(false && "设置位置失败");g_log->error("向屏索引插入 %s 失败 (%u,%u)", entry->name, newPos.x, newPos.y);return false;}SceneObjectType type = entry->getType();screen newscreen = newPos.getscreen();//位置所在的屏_pos_index[newPos.hash()].push_back(entry);//位置索引对象容器if(_posi_index[type][newscreen].insert(entry).second)//类型屏索引对象容器{_posi_index_vec[type][newscreen].push_back(entry);}//在全局索引中添加all[type].insert(entry);//类型对象容器entry->inserted=true;entry->onAddToSceneIndex();return true;}

场景npc实体容器类型:

1)网格位置索引对象容器

_pos_index[newPos.hash()] 是位置索引所有场景实体的链表。

2)类型屏索引对象容器

分实体集合和实体vector:

_posi_index[type][newscreen] 是屏类型容器(数组的一个)的屏位置索引该类型实体的集合(set)。

 _posi_index_vec[type][newscreen] 是屏类型容器(数组的一个)的屏位置索引该类型实体的列表(vector)。

实际上_posi_index 和_posi_index_vec的内容是一致的,_posi_index 只是确保被索引的实体容器内没有重复的实体,而_posi_index_vec可以提高遍历的效率。

3)类型对象容器

all[type] 是场景类型实体集合(集合数组)。

其中, 这里的类型type 是npc类型(此外还有玩家类型和场景道具类型,场景道具用于场景循环时需要回收等)

(2)场景对象的同步

同步视野九屏,包括对npc、场景道具、玩家的九屏同步

分两种情况处理:(1)移动跨屏 、(2)跳转跨屏


bool scene_player::synView(const screen& oldscreen){    if (screen_index::check_in_view(oldscreen, getscreen()))//前一个屏与当前屏是九屏关系,(1)移动跨屏     {        GetAllRemovePosNpc allNpcs(this);        GetAllRemovePosUser allUsers(this);        GetAllRemovescreentem allItems(this);        const screen_vector &pv =         scene->get_reverse_direct_view(oldscreen,screen_index::get_view_direct(oldscreen, getscreen()));//获取反向的屏的列表        for (screen_vector::const_iterator it = pv.begin(); it != pv.end(); ++it)        {            scene->execAllOfScreen(SceneObject_NPC, *it, allNpcs);//遍历该屏上的所有的npc,组成消息缓存,批量移除客户端的npc的消息            scene->execAllOfScreen(SceneObject_Player, *it, allUsers);//遍历该屏上的所有的玩家,组成消息缓存,批量移除客户端的玩家的消息            scene->execAllOfScreen(SceneObject_Item, *it, allItems);//遍历该屏上的所有的道具,组成消息缓存,批量移除客户端的道具的消息        }        if (allNpcs.canSend())        {            sendmsgToMe(allNpcs.getSendBuffer(), allNpcs.getSize());//发送批量移除客户端的npc的消息        }        if (allUsers.canSend())        {            sendmsgToMe(allUsers.getSendBuffer(), allUsers.getSize());//发送批量移除客户端的玩家的消息        }        if (allItems.canSend())        {            sendmsgToMe(allItems.getSendBuffer(), allItems.getSize());//发送批量移除客户端的物品的消息        }        const int dir = screen_index::get_view_direct(oldscreen, getscreen());        removeMeFromReverseDirect(oldscreen, dir);// 移除反向上的 玩家对本玩家的视野(网关到反向5屏或者3屏用户 )        sendMeToNineDirect(dir);//发送本玩家到九屏该方向上屏的玩家        sendNineToMeDirect(dir);//发送九屏该方向上屏的实体到本玩家    }    else//跳转地图或者跳转屏,(2)跳转跨屏    {        removeNineEntry(oldscreen);//移除本玩家的九屏上的 所有的场景对象(发送移除物品、npc、玩家消息给自己)        removeMeFromNine(oldscreen);//把本玩家从旧屏的九屏上的移除(广播消息到九屏的玩家)        sendMeToNine();//广播自己到新屏的九屏        sendNineToMe();//发送新屏的九屏消息给自己(把所有玩家、道具、npc发送给自己,按类型发送3类不同消息)    }    return true;}



0 0