玩家移动

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

posted @ 2015-02-02 15:55  zzyoucan  阅读(212)  评论(0编辑  收藏  举报