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() ...
随机推荐
- CentOS7 安装Nginx+MySQL
首先我们需要安装nginx的yum源 [root@AD ~]# rpm -Uvh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-releas ...
- 关于在git添加远程地址的过程中遇到的问题
问题产生的过程 我根据菜鸟教程的步骤,做了如下操作: 1.打开安装文件夹中的git-bash程序 2.设置username和email 3.添加远程地址 结果如下: 之后通过百度知道要先git ini ...
- 201521123077 《Java程序设计》第7周学习总结
1. 本周学习总结 (图片来自网络) 可以看到,java的容器很多,这里讲一下这周经常用到的 ArrayList:用数组形式保存数据的容器,随机访问比较快,但是插入删除操作都比较耗时,会自动调整内部数 ...
- 201521123030《Java程序设计》 第2周学习总结
本周学习总结 String常量,创建之后不能再进行修改 使用+连接字符串会产生新字符串,要大量使用重复性连接应用StringBuilder,检测字符串相等应用equal方法. 枚举类型变量的取值在一个 ...
- 201521123074 《Java程序设计》第1周学习总结
1. 本章学习总结第一周学习总结: 学习了java语言的历史与发展,运行过程,安装了eclipse编程平台,试着编写了java程序. 2. 书面作业: Q 1.为什么java程序可以跨平台运行?执行j ...
- Linux系统文件与目录权限管理
Linux文件目录权限管理 一.Linux文件属性及权限 1.Linux文件及目录权限及属性说明 (1)权限及属性说明 (2)文件权限说明 三种权限说明:r 读 read w 写 write x ...
- java:java构造器和java方法的区别
构造函数(构造器)是一种特殊的函数.其主要功能是用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中.构造函数与类名相同,可重载多个不同的构造函数.在JA ...
- python 集合的操作
list_1 = set([1,2,3,4,5])#print(list_1,type(list_1))list_2 = set([1,2,3,6,7,8,9,10])#print(list_2,ty ...
- javaSE(九)之泛型(Generics)
前言 这几天分享了怎么搭建集群,这一篇给大家介绍的是泛型,在我们的很多java底层的源代码都是有很多复杂的泛型的!那什么是泛型呢? 泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是 ...
- Java并发之CyclicBarrier、CountDownLatch、Phaser
在Java多线程编程中,经常会需要我们控制并发流程,等其他线程执行完毕,或者分阶段执行.Java在1.5的juc中引入了CountDownLatch和CyclicBarrier,1.7中又引入了Pha ...