zoukankan      html  css  js  c++  java
  • 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地图后,现在游戏的效果:

  • 相关阅读:
    第十二章学习笔记
    UVa OJ 107 The Cat in the Hat (戴帽子的猫)
    UVa OJ 123 Searching Quickly (快速查找)
    UVa OJ 119 Greedy Gift Givers (贪婪的送礼者)
    UVa OJ 113 Power of Cryptography (密文的乘方)
    UVa OJ 112 Tree Summing (树的求和)
    UVa OJ 641 Do the Untwist (解密工作)
    UVa OJ 105 The Skyline Problem (地平线问题)
    UVa OJ 100 The 3n + 1 problem (3n + 1问题)
    UVa OJ 121 Pipe Fitters (装管子)
  • 原文地址:https://www.cnblogs.com/stratrail/p/5050703.html
Copyright © 2011-2022 走看看