玩家移动 玩家移动包的通知是很重要的 当玩家移动时客户端发过来的 struct TgtPos { short mov_mode; // 移动方式,对应枚举值EnumMoveMode(转向、走、跑、传送、冲撞) short mov_face; // 移动朝向(不是方向),对应枚举:EnumMoveFace,(正向、倒退、跳跃) int dir; // 方向,对应枚举Direction(0-7 从上开始顺时针 -1为NULL) float speed; // 移动速度 pos2d tgtPos; // 目标坐标 } //处理玩家移动函数 void Map::UpdatePlayerPos(TgtPos pos, PlayerInfo* pl) { // 检查位置包 if (!CheckMovePos(pos, pl)) return; auto& mpos = m_spBase->GetMapPos(pos.tgtPos); pl->SetAttrCurPos(pl->GetAttrTgtPos());// = pl->GetAttrTgtPos(); pl->SetAttrTgtPos(pos.tgtPos);// = pos.tgtPos; pl->SetAttrSpeed(pos.speed);// m_fSpeed = pos.speed; pl->SetAttrMoveFace(pos.mov_face);// mov_face = pos.mov_face; pl->SetAttrMoveMode(pos.mov_mode);//mov_mode = pos.mov_mode; pl->SetAttrDir(pos.dir);//dir = pos.dir; pos2d CurPos = pl->GetAttrCurPos(); pos2d TgtPos = pl->GetAttrTgtPos(); //[+]设置阻挡(压测的时候取消设置) pos2d cur_map_pos = m_spBase->GetMapPos(CurPos); pos2d tgt_map_pos = m_spBase->GetMapPos(TgtPos); int id = pl->GetAttrObjID_Id(); //标记移动 m_MoveMgr.AddMask(pl); //没有发生位置移动 if(cur_map_pos == tgt_map_pos) return; m_MapCellMgr.UpdateObjPos(pl, cur_map_pos, tgt_map_pos); //分配到指定的地图块上面(确定是否离开了所在块) pos2d blockPos(pl->GetAttrBlockX(), pl->GetAttrBlockY()); //更新区域 //pl->region = getRegion(tgt_map_pos); UpdateRegionInfo(pl, tgt_map_pos); pos2d newBlockPos = m_spBase->GetBlockPos(TgtPos); // 火墙移动处理 FireWall(pl, pos.tgtPos); if(blockPos == newBlockPos) return ; //没变化 // 更新Block坐标并进行区域通知 pl->SetAttrBlockX(newBlockPos.x);// = newBlockPos.x; pl->SetAttrBlockY(newBlockPos.y);// = newBlockPos.y; m_MapBlockMgr.Del(blockPos, pl->GetAttrObjID()); NotifyBlockX(blockPos, newBlockPos, pl, Map::eBlockChange_Move); m_MapBlockMgr.Add(newBlockPos, pl); } 1.纠正包 根据客户端发送的坐标位置和移动方式,如果计算出玩家的位置不正确,比如所处位置为阻塞,移动的距离过大,此时就会想客户端发送各个纠正之后的位置,此位置是客户端之前的位置和方向 2.标记移动 m_MoveMgr.AddMask(pl); void MoveManager::AddMask(ObjecInfo* obj) { auto& objId = obj->GetAttrObjID(); MoveOpt tMoveOpt = {m_nCurMoveSeq++, obj}; m_mapMoveObjs[objId] = tMoveOpt;//玩家移动存储的map容器 } 3.更新单元格中玩家信息 m_MapCellMgr.UpdateObjPos(pl, cur_map_pos, tgt_map_pos); void MapCellMgr::UpdateObjPos(CellObject* pCellObj, pos2d src_mpos, pos2d tgt_mpos) { // 更新对象位置,不知为什么这里用list std::list<CellObject*>*& pSrcObjList = m_vecMapCell[src_mpos.x][src_mpos.y].m_pObjList; if(!pSrcObjList) { Plug::PlugMessageBox("MapCellMgr.cpp中pObjListS为空"); } pSrcObjList->remove(pCellObj); if(pSrcObjList->empty()) { delete pSrcObjList; pSrcObjList = nullptr; } auto& pTgtObjList = m_vecMapCell[tgt_mpos.x][tgt_mpos.y].m_pObjList; if(!pTgtObjList) pTgtObjList = new std::list<CellObject*>; pCellObj->m_LocalMapPos = tgt_mpos; pTgtObjList->push_back(pCellObj); } 4.更新区域 UpdateRegionInfo(pl, tgt_map_pos); 玩家移动时起所在的区域和绑定的安全区索引都可能变化,需要实时更新 5.火墙处理 //火墙根据各自进行计算,当玩家走到的格子有火墙时,会发生相应的数值计算 FireWall(pl, pos.tgtPos); 6.block格子更新 //如果玩家移动走出了block格子,就需要从原来的block格子中删除 m_MapBlockMgr.Del(blockPos, pl->GetAttrObjID()); 7.新近和离开的block格子中玩家通知 { {// 两个不同的块相对而言都是从对方块中离开 m_MapBlockMgr.ProcessLeaveObjs(leaveBlockPos, enterBlockPos, ProcessLeaveObj); m_MapBlockMgr.ProcessLeaveObjs(enterBlockPos, leaveBlockPos, ProcessEnterObj); if(ObjID_Player == pObject->GetAttrObjID_Type()) { m_MapBlockMgr.ProcessLeaveMapObjs(leaveBlockPos, enterBlockPos, false, ProcessMapObj); m_MapBlockMgr.ProcessLeaveMapObjs(enterBlockPos, leaveBlockPos, true, ProcessMapObj); } } } 关于这段代码有点难理解 ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** 当玩家从一个block格子走到另一个block格子,会通知当前block格子中距离大于2的格子中的所有玩家,同样对于新进入的block格子也会通知里面的玩家 从一个block格子到另一个block格子都是相对于两个格子离开的而计算的,只是一个发的离开包,一个发的进入包 如果离开的格子中是玩家,直接发包给那个玩家进行通知,然后将玩家,npc,怪物都放到待通知的vecLeaveObjs容器中 接下来就是一起通知本地玩家 if (!vecLeaveObjs.empty()) pPlayer->m_fnSendObjLeave(vecLeaveObjs); 对于道具和装饰物也是类似的通知方式 这些只需通知离开和进入即可 8.将玩家加入到新的block格子当中 m_MapBlockMgr.Add(newBlockPos, pl); 上面发的都是针对进入和离开的玩家发送的objinfo通知包,客户端会保存可视范围内所有玩家的objInfo,所以当离开或进入时需要发送objinfo 9.玩家移动发包处理 第二步会将玩家加入到移动标记中 在map初始化中有个非常重要的定时器 auto tf = m_spTimerFactory; m_UpdateMovePosTimer.reset(tf->createTimer()); m_UpdateMovePosTimer->setInterval(50); m_UpdateMovePosTimer->regTimer(std::bind(&Map::UpdateMovePosTimer, this)); m_UpdateMovePosTimer->start(); 50ms执行一次,此过程中将50ms内玩家的移动数据发送到客户端,50ms客户端是完全感觉不到的 void MoveManager::Run() { #ifndef USE_BLOCK_MGR if(m_mapMoveObjs.empty()) return ; //需要通知的player std::list<PlayerInfo*> listTmpNotifyPlayers; for(auto it = m_mapMoveObjs.begin(); it != m_mapMoveObjs.end(); ++it)//遍历移动玩家列表 { ObjecInfoEx* pObjectInfoEx = (ObjecInfoEx*)it->second.m_pObjInfo; ObjTgtPos tmpPos; tmpPos.seq = it->second.m_nCurSeq; //顺序变量 tmpPos.speed = pObjectInfoEx->GetAttrSpeed();//m_fSpeed; tmpPos.objId = pObjectInfoEx->GetAttrObjID();//objId; tmpPos.player.tgtPos = pObjectInfoEx->GetAttrTgtPos();//tgtPos; tmpPos.player.curPos = pObjectInfoEx->GetAttrCurPos();//m_CurPos; tmpPos.mov_face = pObjectInfoEx->GetAttrMoveFace();//mov_face; tmpPos.move_mode = pObjectInfoEx->GetAttrMoveMode();//mov_mode; tmpPos.dir = pObjectInfoEx->GetAttrDir();//dir; if(it->first.type == ObjID_Player) { PlayerChannel* player = (PlayerChannel*)pObjectInfoEx; std::list<PlayerInfo*> listNearPlayers; player->GetNearPlayerByBlock(listNearPlayers); for(auto p : listNearPlayers) { p->m_vecTmpPos.push_back(tmpPos); listTmpNotifyPlayers.push_back(p); } } else { I_NPC* monster = (I_NPC*)pObjectInfoEx; std::list<PlayerInfo*> listNearPlayers; monster->GetNearPlayerByBlock(listNearPlayers); for(auto p : listNearPlayers) { p->m_vecTmpPos.push_back(tmpPos); listTmpNotifyPlayers.push_back(p); } } } for(auto it = listTmpNotifyPlayers.begin(); it != listTmpNotifyPlayers.end(); it++) { (*it)->m_fnSendMulPos((*it)->m_vecTmpPos);//std::vector<playerTgtPos> tmpPos (*it)->m_vecTmpPos.clear(); } this->Clear();//清空 } } 上面处理就是移动包的处理过程,就是将ObjTgtPos放到附近可视玩家的m_vecTmpPos容器中,然后将该玩家放到待通知的玩家列表中 对于NPC也是将其位置放到其附近玩家的临时通知位置中 //然后针对每个玩家通知其附近玩家或者怪物的移动状态 for(auto it = listTmpNotifyPlayers.begin(); it != listTmpNotifyPlayers.end(); it++) { (*it)->m_fnSendMulPos((*it)->m_vecTmpPos);//std::vector<playerTgtPos> tmpPos (*it)->m_vecTmpPos.clear(); } //注意npc或者玩家移动的发送的移动包 struct ObjTgtPos { unsigned int seq; // 顺序值(同一时刻移动包顺序值) short mov_face; // 移动朝向,对应枚举:EnumMoveFace,(朝前、朝后、跳跃) short move_mode; // 移动方式,对应枚举值EnumMoveMode(转向、走、跑、传送、冲撞) ObjID objId; float speed; // 移动速度 MoveTgtPos player; // 相对位置 int dir; // 对应枚举Direction,方向 0-7 从上开始顺时针, -1为NULL }; 客户端已经保存玩家的objInfo然后发送ObjTgtPos就能够确定是哪个玩家,然后就能够播放相应玩家的动画
对于玩家移动是把玩家移动记录放到其附近玩家的临时位置中,将玩家标记为待通知玩家,而移动通知是在一个定时器里面处理的,定时器为50ms遍历所有
待通知玩家将这些临时位置发给他,临时位置中包含玩家的id,而对于玩家进入和离开视野是直接发包通知的。对于怪物移动通知附近玩家也是在这个50ms的定时器中处理的。而怪物AI是在map中一个200ms的定时器中处理的。对于block格子的概念上次就这个问经理,经理说在map格子上面做block格子,这样如果玩家移动还是在原来的格子中那就不需要通知包(不包括移动),map格子更下则需要的更多