• cocos2dx


    接上一节内容: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地图后,现在游戏的效果:

  • 相关阅读:
    [不错]A step-by-step guide to enabling security, TLS/SSL, and PKI authentication in Elasticsearch
    asp.net core 从单机到集群
    Redlock:Redis分布式锁最牛逼的实现
    es为什么要取消type? 或者为什么一个index下多个type会有问题
    reorder-list——链表、快慢指针、逆转链表、链表合并
    c 链表之 快慢指针 查找循环节点(转)
    binary-tree-preorder-traversal——前序遍历
    binary-tree-postorder-traversal——二叉树后续遍历
    insertion-sort-list——链表、插入排序、链表插入
    sort-list——链表、快慢指针找中间、归并排序
  • 原文地址:https://www.cnblogs.com/stratrail/p/5050703.html
Copyright © 2020-2023  润新知