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(), nLastIdx(-){};
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 = ; 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 = -;
}
}
// 获取当前显示的地图
if (CMapScreen* pMap = GetScreen(sStruct, sStruct.nIdx))
{
pMap->update(dt);
// 判断是否需要显示下一张地图
if (pMap->IsNeedNextScreen())
{
sStruct.nLastIdx = sStruct.nIdx;
// 显示出视口
if (pMap->IsOutViewPort())
{
pMap->Sleep();
sStruct.nLastIdx = -;
}
if (++sStruct.nIdx >= sStruct.vCircle.size())
{
sStruct.nIdx = ;
}
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 != || _dirty || _quadsDirty)
{
Size s = Director::getInstance()->getWinSize();
auto rect = Rect(, , 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 != || _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 = ;
for(const auto& iter : _primitives)
{
if(iter.second->getCount() > )
{
auto& cmd = _renderCommands[index++];
cmd.init(iter.first, _texture->getName(), getGLProgramState(), BlendFunc::ALPHA_NON_PREMULTIPLIED, iter.second, _modelViewTransform, flags);
renderer->addCommand(&cmd);
}
}
}
附上几张加了Tmx地图后,现在游戏的效果:


cocos2dx - tmx地图分层移动处理的更多相关文章
- 读取.tmx地图
读取.tmx地图 m_GameMap = CCTMXTiledMap::create("map1.tmx"); this->addChild(m_GameMap,1); 读取 ...
- Cocos2d-x Tiled地图编辑器(一)基本使用
Tiled地图编辑器支持普通视角地图和45度角地图, 它生成的地图数据文件cocos2d-x完美的支持,Tiled地图编辑器是一个以普通使用为目标地图编辑器,它使用简单而且能够轻松地在不同的游戏引擎中 ...
- 忍者无敌-实例解说Cocos2d-x瓦片地图
实例比較简单,如图所看到的,地图上有一个忍者精灵,玩家点击他周围的上.下.左.右,他能够向这个方向行走. 当他遇到障碍物后是无法穿越的,障碍物是除了草地以为部分,包含了:树.山.河流等. 忍者实例地图 ...
- 忍者无敌-实例讲解Cocos2d-x瓦片地图
实例比较简单,如图所示,地图上有一个忍者精灵,玩家点击他周围的上.下.左.右,他能够向这个方向行走.当他遇到障碍物后是无法穿越的,障碍物是除了草地以为部分,包括了:树.山.河流等. 忍者实例地图(TO ...
- Cocos2d-x 2地图步行实现:SPFA算法
本文乃Siliphen原创,转载请注明出处:http://blog.csdn.net/stevenkylelee 上一节<Cocos2d-x 地图行走的实现1:图论与Dijkstra算法> ...
- cocos2dx - 创建地图及玩家(伪)
接上一节内容:cocos2dx - 环境配置,项目创建 本节主要描述cocos中精灵的创建及点击事件的使用 打开创建好的test项目,看到下图的目录结构,真正的游戏逻辑路径在src下. AppDele ...
- 关于Cocos2d-x中地图轮播的实现
播放背景,两个背景的图片是一样的,紧挨着循环播放,以下代码写在playBackground()方法中,并在GameScene.cpp的init方法中调用. void GameScene::playBa ...
- cocos2dx游戏 地图
#include "HelloWorld.h" USING_NS_CC; CCScene* MyHelloWorld::scene() { // 'scene' is an aut ...
- cocos2dx迷你地图
用CCRenderTexture就可以了,不知是否有更好的方法. if (!miniMap) { miniMap=CCSprite::create(); miniMap->setZOrder() ...
随机推荐
- 201521123018 《Java程序设计》第4周学习总结
1. 本章学习总结 2. 书面作业 Q1.注释的应用:使用类的注释与方法的注释为前面编写的类与方法进行注释,并在Eclipse中查看.(截图) Q2.面向对象设计(大作业1-非常重要) 2.1 讲故事 ...
- 201521123035《Java程序设计》第十一周作业
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多线程相关内容. 本周对多线程的冲突是从多线程的冲突开始讲起,从而提出互斥共享与互斥访问.其中,互斥访问提到了synchronize ...
- Java:设计类的继承关系时的技巧
继承设计的技巧: (1)将公共操作和域放置在超类 (2)不要使用受保护的域 有些程序员认为,将大多数的实例域定义为protected是一个不错的主意,只有这样,子类才能够在需要的时候直接访问他们.然而 ...
- 【翻译】基于web创建逼真的3D图形 | CSS技巧
个人翻译小站:http://www.zcfy.cc/article/creating-photorealistic-3d-graphics-on-the-web-css-tricks-4039.htm ...
- 搭建连接MySql的三层架构的ASP.NetCore2.0的WebApi
里我们用三层架构搭建一个连接MySql的ASP.netCore模板的WebApi项目 首先添加WebApi项目(ASP.NetCore版本) 右键解决方案>新建项目> 选择Web>A ...
- Linux文件管理_1
在Linux中,全部都是文件,所以文件管理在Linux上格外重要,在我们学习文件管理前,我们先学习几个关于文件的命令,之后才能更好的学习文件管理. 目录 pwd命令 cd命令 列出文件内容ls 查看文 ...
- bookStore第三篇【用户模块、购买模块、订单模块】
用户模块 要登陆后才能购买,因此我们先写购买模块 设计实体 private String id; private String username; private String password; p ...
- JDBC操作数据库之查询数据
以数据库中查找图书信息,并将信息显示在jsp页面当中为例,下面贴上代码片段: (1)在index.jsp页面代码body中只要添加如下一段代码: <a href="FindServle ...
- 常见注入手法第二讲,APC注入
常见注入手法第二讲,APC注入 转载注明出处 首先,我们要了解下什么是APC APC 是一个简称,具体名字叫做异步过程调用,我们看下MSDN中的解释,异步过程调用,属于是同步对象中的函数,所以去同步对 ...
- bzoj 3212 Pku3468 A Simple Problem with Integers
3212: Pku3468 A Simple Problem with Integers Time Limit: 1 Sec Memory Limit: 128 MB Description You ...