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地图后,现在游戏的效果:

posted @ 2015-12-16 11:56  稀饭lei___  阅读(4668)  评论(2编辑  收藏  举报