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() ...
随机推荐
- 201521123052《Java程序设计》第7周学习总结
1. 本周学习总结 参考资料: XMind 2. 书面作业 1.ArrayList代码分析 1.1 解释ArrayList的contains源代码 public boolean contains(Ob ...
- 201521123010 《Java程序设计》第4周学习总结
1.本周学习总结 1.1 尝试使用思维导图总结有关继承的知识点. 1.2 使用常规方法总结其他上课内容. 这周上课主要学了继承,在打代码的时候对各个关键字(除了super关键字)的用法有一点混乱.对多 ...
- 201521123095 《Java程序设计》第2周学习总结
1. 本周学习总结 学会了使用码云管理代码,使用eclipse关联jdk源代码: 还学习了Java语言中各种数据类型以及运算符. 2. 书面作业 1.使用Eclipse关联jdk源代码,并查看Stri ...
- 201521123113《Java程序设计》第10周学习总结
1. 本周学习总结 2. 书面作业 本次PTA作业题集异常.多线程 Q1.finally 题目4-2 1.1 截图你的提交结果(出现学号) 1.2 4-2中finally中捕获异常需要注意什么? fi ...
- 201521123068《Java程序设计》第12周学习总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多流与文件相关内容. 2. 书面作业 将Student对象(属性:int id, String name,int age,doubl ...
- cxgrid学习
delphi cxgrid控件哪个属性是设置不能编辑? cxgrid控件cxgridDBTable的OptionsData可以选择操作 cxGrid1DBTableView1下选择cxGrid1DBT ...
- day2_操作系统
一.为什么要有操作系统 因为计算机系统主要是由一个或者多个处理器,主存,硬盘,键盘,鼠标,显示器,打印机,网络接口及其他输入输出设备组成.现代计算机系统复杂 每位计算机程序员不可能全部的掌 ...
- Struts2配置文件复用代码【web.xml、struts.xml、常量配置】
web.xml的分发器代码: <!-- 引入struts核心过滤器 --> <filter> <filter-name>struts2</filter-nam ...
- MyEclipse用Java语言连接Oracle数据库
在MyEclipse下Java连接Oracle数据库 第一步:新建Java项目. 填写项目名,其它设置默认,点击完成即可. 新建java类,填写包名和类名,勾选public static void m ...
- stsuts2的一些问题
1.什么是struts2? struts2是一个基于MVC设计模式的框架, 2.struts2的工作原理. 1.客户端发送一个请求 2.经过核心过滤器StrutsPrepareAndExecuteFi ...