游戏服务器之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;}
- 游戏服务器之ai
- 游戏AI之RTS游戏
- 游戏AI之Lua版
- 游戏AI入门系列之可预测AI
- libgdx API之AI:AI让游戏对象思考
- 游戏AI之长距离武器作战
- 游戏AI之有限状态机(1)
- 游戏开发之-AI行为树
- [Unity3D]Unity3D游戏开发之怪物AI
- 建立完整游戏AI实践之1
- [Unity3D]Unity3D游戏开发之怪物AI
- 游戏AI之行为树(下)
- 游戏AI之行为树(上)
- 游戏AI之行为树(中)
- 开发游戏AI之行为树
- 游戏AI
- 游戏AI
- 游戏AI
- 11.2.0.4 Patch Set - Availability and Known Issues (Doc ID 1562139.1)
- hdu1084解题报告
- [Java] 动态代理 02 --生成代理主题角色
- HDU 1215 七夕节 数学题~
- java设计模式4——建造者模式(Builder)
- 游戏服务器之ai
- Ranorex automation test qt
- 设计模式11——结构型模式之外观模式
- 简单教你提高网站速度的6种小方法
- Python-Project Euler 46
- 题目1111:单词替换
- ApacheTomcat7.0配置
- 流鼻涕真不爽
- 双向链表