zoukankan      html  css  js  c++  java
  • 地图分割,怪物群体 检测 侵入者

    在地图上某块区域,有数个怪物组成的 "哨兵小组",即不会移动, 但拥有警戒视野, 如图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();
        }
    }
  • 相关阅读:
    java框架
    MVC编程模式
    java各版本简单对比
    java设计模式
    ES中TF-IDF算法
    es分词器
    java应用零停机,时间索引重建(reindex)
    Spring源码由浅入深系列一 简介
    Spring源代码解析(收藏)
    spring源码读书笔记
  • 原文地址:https://www.cnblogs.com/Wilson-Loo/p/3251142.html
Copyright © 2011-2022 走看看