地图分割,怪物群体 检测 侵入者

在地图上某块区域,有数个怪物组成的 "哨兵小组",即不会移动, 但拥有警戒视野, 如图1.

这5个哨兵是不会移动, 有站立点可确定一个最小外凸包围圈, 如图2;再产生最小矩形包围圈,如图3.

typedef struct Position_t{
    INT mapid;
    INT x;
    INT y;
}Position_t, MapPieceBoundInfo_t;

typedef UINT ElementType_t;
typedef UINT GUID_t;

class GamePlayer
{
public:
    BOOL IsMoving();
    Position_t      GetPosition();
    GUID_t          GetGUID();
};

class
Monster { public: ElementType_t ElementType(); Position_t GetPosition(); BOOL FindInMyDetection( const Position_t& pos); VOID HeartBeat(); private: MonsterGroupCell* m_sharedGroupCell; };
typedef struct MonsterGroupCell
{
    // players enter this cell
    std::map< GUID_t, GamePlayer*>         m_playerList;           

    ElementType_t                   m_type;
    MapPieceBoundInfo_t             m_LeftTop;
    MapPieceBoundInfo_t             m_RightBottom;

public:
    inline size_t Size( ){ return m_playerList.size(); }

    VOID TryDetectGameObject( GamePlayer* gPlayer);

    VOID TickClearInvalidGameObjectRecord( );

};

 

按照警戒视野(r)扩宽后, 这个怪物群体的"警戒矩形"将变成 如图4.

class MapData{

public:

    const INT PIECE_SIZE = 20;

    BOOL init(); // 进行地图切割

  
    VOID WhenMonstersCreated()
    { // 怪物创建好后, 进行群组分类, 即产生最大包围矩形

      // 第一部分 对怪物进行 分类颤声道 MonsterGroupCell
        if( m_monsterList.size() > 0 )
        {
            std::map<ElementType_t, MonsterGroupCell*>  mapMonsterGroupCells;

            std::vector<Monster*>::const_iterator iter = m_monsterList.begin();
            for( ; iter != m_monsterList.end() ; iter ++ )
            {
                ElementType_t monsterType = (*iter)->ElementType();
                MonsterGroupCell* groupCell = mapMonsterGroupCells[ monsterType ];
                if( groupCell == NULL )
                {
                    MonsterGroupCell* groupCell = new MonsterGroupCell;
                    ASSERT( groupCell != NULL);

                    groupCell->m_LeftTop.x = m_col + 1;
                    groupCell->m_LeftTop.y = m_row + 1;

                    groupCell->m_RightBottom.x = 0;
                    groupCell->m_RightBottom.y = 0;

                    mapMonsterGroupCells[ monsterType ] = groupCell;
                }

                Position_t pos = (*iter)->GetPosition();
                if( pos.x < groupCell->m_LeftTop.x )
                {
                    groupCell->m_LeftTop.x = pos.x;
                }

                if( pos.y < groupCell->m_LeftTop.y )
                {
                    groupCell->m_LeftTop.y = pos.y;
                }

                if( pos.x > groupCell->m_RightBottom.x )
                {
                    groupCell->m_RightBottom.x = pos.x;
                }

                if( pos.y > groupCell->m_RightBottom.y )
                {
                    groupCell->m_RightBottom.y = pos.y;
                }
            }

            //第二部分: 非配各个 MonsterGroupCell 到 地图切片上
            // ...    
    }// eof function 

    
    VOID GameObjectPositionCheck();

private:
    std::list<GamePlayer*>  m_playerList;
    std::vector<Monster*>   m_monsterList;  
    MapPiece**               m_Pieces;

    INT m_mapid;
    INT m_row ;
    INT m_col ;

    INT m_rowPieces;
    INT m_colPieces;
};

 

一张地图上有不同的 怪物小组(此处都作为不可移动的哨兵), 有 很多不同形状,可能会相互重叠:

以玩家显示器大小 进行对地图切割:

哨兵群体 MonsterGroupCell (图中红色虚线矩形) 也会被切割成各种 形态,并且每个 群体至少属于一个地图片元MapPiece:

class MapPiece
{
public:
    VOID OnPlayerMoved( GamePlayer* gPlayer);

private:
    
    // 地图片元 所拥有的 怪物群体
    std::vector< MonsterGroupCell*> m_monsterGroupCells;    

    MapPieceBoundInfo_t             m_LeftTop;
    MapPieceBoundInfo_t             m_RightBottom;
};
BOOL  MapData::init()
{

        m_rowPieces = m_row / PIECE_SIZE + 1;
        m_colPieces = m_col / PIECE_SIZE + 1;
        m_Pieces = new MapPiece[ m_rowPieces][ m_colPieces ];
        for( INT row = 0; row < m_rowPieces; row ++ )
        {
            for( INT col = 0; col < m_colPieces; col ++)
            {
                m_Pieces[ row][ col].m_LeftTop.mapid    = m_mapid;
                m_Pieces[ row][ col].m_LeftTop.x        = col * PIECE_SIZE;
                m_Pieces[ row][ col].m_LeftTop.y        = row * PIECE_SIZE;

                m_Pieces[ row][ col].m_RightBottom.mapid    = m_mapid;
                m_Pieces[ row][ col].m_RightBottom.x        = col * PIECE_SIZE + PIECE_SIZE;
                m_Pieces[ row][ col].m_RightBottom.y        = row * PIECE_SIZE + PIECE_SIZE;
            }
        }
} // eof function init
    VOID     MapData::WhenMonstersCreated()
    { 
      // 第一部分 对怪物进行 分类颤声道 MonsterGroupCell
        ...
       
         //第二部分: 非配各个 MonsterGroupCell 到 地图切片上
        const INT DETECT_RANGE = 10;
            std::map< ElementType_t, MonsterGroupCell*>::const_iterator cellIter = mapMonsterGroupCells.begin();
            for( ; cellIter != mapMonsterGroupCells.end(); cellIter ++ )
            {
                MonsterGroupCell* cell = cellIter->second;
                cell->m_LeftTop.x = max( cell->m_LeftTop.x - DETECT_RANGE, 0);
                cell->m_LeftTop.y = max( cell->m_LeftTop.y - DETECT_RANGE, 0);

                cell->m_RightBottom.x = min( cell->m_RightBottom.x + DETECT_RANGE, m_col);
                cell->m_RightBottom.y = min( cell->m_RightBottom.y + DETECT_RANGE, m_row);


                for( INT cX = cell->m_LeftTop.x; cX <= cell->m_RightBottom.x; cX += PIECE_SIZE )
                {
                    for( INT cY = cell->m_LeftTop.y; cY <= cell->m_RightBottom.y; cY += PIECE_SIZE )
                    {
                        m_Pieces[ cX / PIECE_SIZE ][ cY / PIECE_SIZE ].m_monsterGroupCells.push_back(  cell );
                    }
                }
            }
    }// eof function

 引擎内至少有 两个线程:

1.更新游戏对象, 如玩家 或 怪物的属性状态, 诸如 breath / heartbeat / frameCome / tick ...

  进行 行走 , 地理位置变更.

2.管理类型性质的 管理类, 当玩家位置变更时, 通知其所在的 地图片元 MapPiece, MapPiece 再根据 所分配的 MonsterGroupCell(s)  和 玩家位置 检测是否进入 了 被检测区域, 如果是, 则 通知 相应的 MonsterGroupCell(s) , 这里有一点注意事项, 一个MonsterGroupCell 可能会过大, 范围超过一个 MapPiece 大小, 这种 MonsterGroupCell 会受到相同的多条 玩家移动的事件 通知.另外只需要 以 玩家所在 MapPiece 为中心的九宫格 的 九个MapPiece 事件检测:

   VOID     MapData::GameObjectPositionCheck()
    {
        std::list< GamePlayer*>::const_iterator iter = m_playerList.begin();
        // 遍历每个 该地图上的所有玩家
        for( ; iter != m_playerList.end(); iter ++ )
        {
            GamePlayer* gPlayer = (*iter);
            if( gPlayer->IsMoving() )
            {
                  // 玩家所属 九宫格的中心 地图片元
                Position_t pos = gPlayer->GetPosition();
                INT curPieceX = pos.x / PIECE_SIZE ;
                INT curPieceY = pos.y / PIECE_SIZE;

                INT checkPieceX;
                INT checkPieceY;

                //遍历九宫格
                for( INT rowOff = -1; rowOff <= 1; rowOff ++ )
                {
                    for( INT colOff = -1; colOff <= 1; colOff ++ )
                    {
                        checkPieceX = curPieceX + colOff;
                        checkPieceY = curPieceY + rowOff;

                        if( checkPieceX >= 0 && checkPieceX < m_colPieces)
                        {
                            if( checkPieceY >= 0 && checkPieceY < m_rowPieces)
                            {
                                // 通知 地图片元 piece, 玩家 移动了
                                MapPiece* piece = m_Pieces[ checkPieceY][ checkPieceX];
                                piece->OnPlayerMoved( gPlayer);
                            }
                        }
                    }
                }
            }
        }
    }

地图片元 请求 所分配的 怪物群体 MonsterGroupCell(s) 是否 玩家在其 检测范围内:

    VOID     MapPiece::OnPlayerMoved( GamePlayer* gPlayer)
    {
        if( m_monsterGroupCells.size() > 0 )
        {
            std::vector< MonsterGroupCell*>::const_iterator iter = m_monsterGroupCells.begin();
            for( ; iter != m_monsterGroupCells.end(); iter ++ )
            {
                (*iter)->TryDetectGameObject( gPlayer);
            }
        }
    }

地图片元 MonsterGroupCell 根据其检测范围 ( 由 左上角坐标 - 右下角坐标 确定), 记录侵入者信息:

VOID     MonsterGroupCell::TryDetectGameObject( GamePlayer* gPlayer)
    {
        const Position_t& pos = gPlayer->GetPosition();
        
        if( pos.x >= m_LeftTop.x && pos.y >= m_LeftTop.y 
            &&
            pos.x <= m_RightBottom.x && pos.y <= m_RightBottom.y )
        {
            m_playerList[ gPlayer->GetGUID()] = gPlayer;
        }else{
            if( m_playerList[ gPlayer->GetGUID()] != NULL )
            {
                //标记为离开, 但此时 还是有该玩家的信息, 只是对象实体为 null, 
                m_playerList[ gPlayer->GetGUID() ]= NULL;
            }
        }
    }

另外, 玩家离开后, 并不是实时从 m_playerList 删除, 而是在 其他 较为 不频繁的时刻进行删除.

在怪物的逻辑处理线程内,通过 怪物群体 共享的 MonsterGroupCell, 检测被标记的 玩家 是否确实在 各个具体怪物的 境界范围内:

    VOID     Monster::HeartBeat()
    {
        if( m_sharedGroupCell->Size() > 0 )
        {
            std::map< GUID_t, GamePlayer*>::const_iterator iter = m_sharedGroupCell->m_playerList.begin();
            for( ; iter != m_sharedGroupCell->m_playerList.end(); iter ++ )
            {
                //检测是否进入 实际 警戒范围内
                if( FindInMyDetection( iter->second->GetPosition() ))
                {
                    // do some operation
                    // 由 ai 决定
                }
            }

        }
    }
//全局 地图信息管理线程
extern std::vector< MapData*>  g_MapDataList;
static VOID MapDataManagerThreadCallback( VOID* pMapData)
{
    std::vector< MapData*>::const_iterator iter = g_MapDataList.begin();
    for( ; iter != g_MapDataList.end(); iter ++ )
    {
        (*iter)->GameObjectPositionCheck();
    }
}

 

 

posted @ 2013-08-11 14:46  Wilson-Loo  阅读(903)  评论(0编辑  收藏  举报