cocos2dx - tmx地圖分層移動處理


接上一節內容:cocos2dx - 節點管理 

瓦片地圖(Tiled Map)

  在cocos2dx文檔中有簡單的介紹及使用。詳情可以看:http://www.cocos2d-x.org/docs/manual/framework/native/v2/graphic/tiled-map/zh

一、FastTMXTiledMap & TMXTiledMap選擇

  在cocos2dx有2種實現加載Tmx地圖的方法,分別是FastTmxTiledMap和TmxTiledMap。

主要區別:

    FastTmxTiledMap 的繪制層TMXLayer繼承Node節點,直接利用opengl 的索引(indices)一次繪制所有的格子紋理。

        TmxTiledMap       的繪制層TMXLayer則通過繼承 SpriteBatchNode節點,利用cocos2dx中封裝好的批量繪制圖片節點的功能實現一次繪制。

性能區別:

         FastTmxTiledMap 在繪制效率上相對於 TmxTiledMap  有顯著提高,因為SpriteBatchNode實際流程是創建了批量的Node,通過SpriteBatchNode來管理這些Node的索引及統一繪制調用,

這樣相對於一個FastTmxTiledMap中一個Node的調用多了頂點的消耗及內存的消耗。

GL verts 和  GL calls 對比

     (FastTmxTiledMap)                     (TmxTiledMap)

通過對比,可以看到同樣的效果,繪制回調次數一致,但是頂點數 FastTmxTiledMap 僅占 TmxTileMap的 1/3多一點。

 

二、實際應用

  功能: 實現通用的循環地圖,同時讓地圖分層同屏移動。

首先,需要設計一個結構體SMapStruct來管理同一層的地圖實現循環,同時利用一個map來管理不同層級的SMapStruct

    class CMapScreen; // 實際顯示移動地圖的管理

    // 管理同一層級的地圖
    struct SMapStruct
    {
        SMapStruct() :nIdx(0), nLastIdx(-1){};
        size_t                        nIdx;        // 當前地圖索引
        size_t                        nLastIdx;    // 上一層的索引
        std::vector<int>            vCircle;    // 循環索引列表
        std::vector<CMapScreen*>    vScreen;    // 實際地圖列表
    };
   std::map<int, SMapStruct>  m_mMapList; // 層級到SMapStruct列表

這里的索引可以指向csv讀取出來的 SMapConfig 配置

// 地圖配置
struct SMapConfig
{
    int                    nID;            // 索引
    std::string            strFile;        // 地圖資源
    int                    nIdx;            // 層級
    int                    nSpeed;            // 地圖移動速度 px/s
    bool                   bMoveY;            // 是否Y移動
};

這樣在游戲開始,對配置的地圖信息進行加載,存到 m_mMapList列表中。

    // 地圖設置
    {
        for (size_t i = 0; i < m_vMap.size(); i++)
        {
            SMapConfig* pConfig = m_vMap[i];
            if (pConfig)
            {
                SMapStruct& sMapStruct = m_mMapList[pConfig->nIdx];
                sMapStruct.vCircle.push_back(i);
            }
        }
    }

這樣m_mMapList列表中就存了當前游戲每一個層級需要的地圖列表。然后在update對其進行更新顯示,如下:

void CMapMgr::update(float dt)
{
    auto it = m_mMapList.begin();
    while (it != m_mMapList.end())
    {
        SMapStruct& sStruct = it->second;
        // 判斷當前層級地圖列表是否存在
        if (sStruct.vCircle.size() <= sStruct.nIdx)
        {
            CCLOG("地圖列表更新錯誤!!");
            break;
        }
        // 獲取上一張顯示的地圖
        if (CMapScreen* pMap = GetScreen(sStruct, sStruct.nLastIdx))
        {
            pMap->update(dt);// 更新坐標
            // 顯示出視口
            if (pMap->IsOutViewPort())
            {
                pMap->Sleep();    //隱藏該地圖
                sStruct.nLastIdx = -1;
            }
        }
        // 獲取當前顯示的地圖
        if (CMapScreen* pMap = GetScreen(sStruct, sStruct.nIdx))
        {
            pMap->update(dt);
            // 判斷是否需要顯示下一張地圖
            if (pMap->IsNeedNextScreen())
            {
                sStruct.nLastIdx = sStruct.nIdx;
                // 顯示出視口
                if (pMap->IsOutViewPort())
                {
                    pMap->Sleep();
                    sStruct.nLastIdx = -1;
                }
                if (++sStruct.nIdx >= sStruct.vCircle.size())
                {
                    sStruct.nIdx = 0;
                }
                if (CMapScreen*pNextMap = GetScreen(sStruct, sStruct.nIdx))
                {
                    // 顯示新地圖
                    pNextMap->Active(pMap->GetConnetPoint());
                }
            }
        }
        ++it;
    }
}


以上實現了地圖分層移動的管理,可以實現不同層級不同速度移動,或者靜止等,也可以往不同方向移動。

SMapStruct類的實現,不再這里詳細描述了。主要實現以下方法:

// 每個地圖層
class CMapScreen : public Node
{
public:
    static CMapScreen*  create(const SMapConfig* pConfig);
     
    void    Release();

    void    Active(const Vec2& pt);        // 啟用update循環移動
    
    void    Sleep();                    // 隱藏停止update
 
    bool    IsNeedNextScreen() const;

    Vec2    GetConnetPoint() const;    // 獲取連接點

    void    update(float dt);

    bool    IsOutViewPort() const ;    // 出了視口an
}

 

三、黑縫處理

      Tmx地圖在cocos2dx移動的時候會偶爾出現黑線的現象。主要原因是底層頂點坐標取到了紋理之外導致顏色值取不到。

解決辦法

  1、移動的偏移坐標用整數。

  2、銜接處重疊1個像素。

  3、采用Director::Projection::_2D的方式繪制游戲。

1、如下:

    m_nDelta+= m_pConfig->nSpeed* dt;
    // 取整數
    int nDelta = int(m_nDelta);
    m_nDelta -= nDelta;
    // 移動對應的距離
    m_pConfig->bMoveY ? setPositionY(getPositionY() + nDelta) : setPositionX(getPositionX() + nDelta);

 2、代碼如下: 

Vec2 CMapScreen::GetConnetPoint() const { Vec2 pt; if (!m_pConfig) { return pt; } Vec2 origin = Director::getInstance()->getVisibleOrigin(); if (m_pConfig->bMoveY) { pt = m_pConfig->nSpeed>0 ? getPosition() : Vec2(getPositionX(), getPositionY() + getContentSize().height); pt.y = m_pConfig->nSpeed > 0 ? pt.y + 1: pt.y - 1; //重疊1個像素 防止黑縫出現  } else { pt = m_pConfig->nSpeed>0 ? getPosition() : Vec2(getPositionX() + getContentSize().width, getPositionY()); pt.x = m_pConfig->nSpeed > 0 ? pt.x + 1 : pt.x - 1;//重疊1個像素 防止黑縫出現  } return pt; }

3、代碼如下:

director->setProjection(Director::Projection::_2D); 

另,3在cocos2dx-3.9中用2D方式繪制FastTmxMap有bug,需要回溯之前版本的配置如下:

void TMXLayer::draw(Renderer *renderer, const Mat4& transform, uint32_t flags)
{
    updateTotalQuads();

    //正交方式處理紋理,防止地圖切換黑線
    if (Director::getInstance()->getProjection() == Director::Projection::_2D)
    {
        if (flags != 0 || _dirty || _quadsDirty)
        {
            Size s = Director::getInstance()->getWinSize();
            auto rect = Rect(0, 0, s.width, s.height);

            Mat4 inv = transform;
            inv.inverse();
            rect = RectApplyTransform(rect, inv);

            updateTiles(rect);
            updateIndexBuffer();
            updatePrimitives();
            _dirty = false;
        }
    }
    else
    {
        bool isViewProjectionUpdated = true;
        auto visitingCamera = Camera::getVisitingCamera();
        auto defaultCamera = Camera::getDefaultCamera();
        if (visitingCamera == defaultCamera) {
            isViewProjectionUpdated = visitingCamera->isViewProjectionUpdated();
        }

        if (flags != 0 || _dirty || _quadsDirty || isViewProjectionUpdated)
        {
            Size s = Director::getInstance()->getVisibleSize();
            auto rect = Rect(Camera::getVisitingCamera()->getPositionX() - s.width * 0.5,
                Camera::getVisitingCamera()->getPositionY() - s.height * 0.5,
                s.width,
                s.height);

            Mat4 inv = transform;
            inv.inverse();
            rect = RectApplyTransform(rect, inv);

            updateTiles(rect);
            updateIndexBuffer();
            updatePrimitives();
            _dirty = false;
        }
    }
    
    if(_renderCommands.size() < static_cast<size_t>(_primitives.size()))
    {
        _renderCommands.resize(_primitives.size());
    }
    
    int index = 0;
    for(const auto& iter : _primitives)
    {
        if(iter.second->getCount() > 0)
        {
            auto& cmd = _renderCommands[index++];
            cmd.init(iter.first, _texture->getName(), getGLProgramState(), BlendFunc::ALPHA_NON_PREMULTIPLIED, iter.second, _modelViewTransform, flags);
            renderer->addCommand(&cmd);
        }
    }
}
View Code

 

附上幾張加了Tmx地圖后,現在游戲的效果:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM