• 玩家移动


    玩家移动
    玩家移动包的通知是很重要的
    当玩家移动时客户端发过来的
    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格子更下则需要的更多

  • 相关阅读:
    Queue Aizu
    Stack Aizu
    Stack Aizu
    Shell Sort Aizu
    Shell Sort Aizu
    Stable Sort Aizu
    VS2013下.Net Framework4配置FineUI4.14
    VS2013下.Net Framework4配置FineUI4.14
    Ubuntu-14.04.1 desktop安装时遇到的小问题
    Ubuntu-14.04.1 desktop安装时及安装后遇到的小问题
  • 原文地址:https://www.cnblogs.com/zzyoucan/p/4267978.html
Copyright © 2020-2023  润新知