关于cocos2dx 中的瓦片地图的分析
在游戏制作当中瓦片地图的使用可以大大减少工程的大小很多特别是很多不断重复图片的使用,一个比较大的地图比如
最近流行的部落冲突游戏很明显的是使用了瓦片地图,我觉得瓦片地图的使用适用用于以下的几种游戏
1,.特别适用于很大的地图如果背景但是背景的重复性高的话,使用瓦片地图是一个很好的选择
2,.针对于很多地图不断变化的游戏,例如游戏中需要种树或者删除一小块的元
3.类似于超级马里奥那种的横版的游戏,都是又一个个小的方块组成
瓦片地图的实现需要使用TileMap这个软件可以百度一下,这个软件是专门用来做瓦片地图的
关于TileMap地图支持3种不同的视图:正交视图 六边形的视图 等轴视图
在TileMap中的你可以对你所编辑的地图场景进行分层类似于游戏中的Layer一样的,每一层和每一层之间没有
直接的联系每一个层中是由很多的瓦片组成每一个瓦片都会有一个唯一的编号所以这样的话你会精确的得到地图
中的一个很小的点但是在他中的坐标和coco2d中的坐标不一样的,他的坐标原点在左上角左上角的是(0,0)点了,
可以对地图中的每一个瓦片进行动态的添加和删除,所以这就是瓦片地图的强大之处。
在瓦片地图中还存在一种对象层可以创建一个不规则的图形一般用于处理一些碰撞什么的
具体的代码方面可以查看cocos2d 的源代码CCTMXTiledMap.cpp文件
关于cocos2dx创建方法在可以看到源代码中
TMXTiledMap * TMXTiledMap::create(const std::string& tmxFile)
{
TMXTiledMap *ret = new (std::nothrow) TMXTiledMap();
if (ret->initWithTMXFile(tmxFile))
{
ret->autorelease();
return ret;
}
CC_SAFE_DELETE(ret);
return nullptr;
}
TMXTiledMap* TMXTiledMap::createWithXML(const std::string& tmxString, const std::string& resourcePath)
{
TMXTiledMap *ret = new (std::nothrow) TMXTiledMap();
if (ret->initWithXML(tmxString, resourcePath))
{
ret->autorelease();
return ret;
}
CC_SAFE_DELETE(ret);
return nullptr;
}
创建方法有两种可以看到其区别一种是可以传入地图文件的文件路径一种是不传入
接下来是得到成功创建的map的每一层代码
</pre><p></p><p></p><pre name="code" class="cpp">// public
TMXLayer * TMXTiledMap::getLayer(const std::string& layerName) const
{
CCASSERT(layerName.size() > 0, "Invalid layer name!");
for (auto& child : _children)
{
TMXLayer* layer = dynamic_cast<TMXLayer*>(child);
if(layer)
{
if(layerName.compare( layer->getLayerName()) == 0)
{
return layer;
}
}
}
// layer not found
return nullptr;
}
可以看到通过调用getLayer传入层的文件名便可以得到返回的每一层
CC_DEPRECATED_ATTRIBUTE TMXLayer* layerNamed(const std::string& layerName) const { return getLayer(layerName); };
或者可以通过直接 layerNamed()得到也只是同理了
还有一些比如说如何得到总共有多少个瓦片每一个瓦片的大小是多少,根据数量以及大小就可以得到具体你要得到的瓦片的位置
inline const Size& getMapSize() const { return _mapSize; };
inline void setMapSize(const Size& mapSize) { _mapSize = mapSize; };
/** the tiles's size property measured in pixels */
inline const Size& getTileSize() const { return _tileSize; };
inline void setTileSize(const Size& tileSize) { _tileSize = tileSize; };
mapSize 指的是总的地图的大小但是这里的大小指的不是像素的大小这里指的是坐标多少,也就是指的是总共有多少的瓦片
tileSize 指的是每一个瓦片的大小
关于对象层
TMXObjectGroup * TMXTiledMap::getObjectGroup(const std::string& groupName) const
{
CCASSERT(groupName.size() > 0, "Invalid group name!");
if (_objectGroups.size()>0)
{
TMXObjectGroup* objectGroup = nullptr;
for (auto iter = _objectGroups.cbegin(); iter != _objectGroups.cend(); ++iter)
{
objectGroup = *iter;
if (objectGroup && objectGroup->getGroupName() == groupName)
{
return objectGroup;
}
}
}
// objectGroup not found
return nullptr;
}
可以得到对象层的引用然后在对象组里得到你所需要的对象的位置了
auto heroObject = myTiledMap->getObjectGroup("heroborn");
auto spanwnPoint = heroObject->getObject("createHero");
heroBornX = spanwnPoint["x"].asInt();
heroBornY = spanwnPoint["y"].asInt();
那么得到了层之后紧接着就是得到层中的每一个瓦片的点
Sprite * TMXLayer::getTileAt(const Vec2& pos)
{
CCASSERT(pos.x < _layerSize.width && pos.y < _layerSize.height && pos.x >=0 && pos.y >=0, "TMXLayer: invalid position");
CCASSERT(_tiles && _atlasIndexArray, "TMXLayer: the tiles map has been released");
Sprite *tile = nullptr;
int gid = this->getTileGIDAt(pos);
// if GID == 0, then no tile is present
if (gid)
{
int z = (int)(pos.x + pos.y * _layerSize.width);
tile = static_cast<Sprite*>(this->getChildByTag(z));
// tile not created yet. create it
if (! tile)
{
Rect rect = _tileSet->getRectForGID(gid);
rect = CC_RECT_PIXELS_TO_POINTS(rect);
tile = Sprite::createWithTexture(this->getTexture(), rect);
tile->setBatchNode(this);
tile->setPosition(getPositionAt(pos));
tile->setPositionZ((float)getVertexZForPos(pos));
tile->setAnchorPoint(Vec2::ZERO);
tile->setOpacity(_opacity);
ssize_t indexForZ = atlasIndexForExistantZ(z);
this->addSpriteWithoutQuad(tile, static_cast<int>(indexForZ), z);
}
}
return tile;
}
可以通过具体的坐标得到一个以这个坐标为点的精灵
那么接下来可以对每一个瓦片进行增删的操作
这段的意思就是对每一层进行增加
void TMXLayer::setTileGID(uint32_t gid, const Vec2& pos, TMXTileFlags flags)
{
CCASSERT(pos.x < _layerSize.width && pos.y < _layerSize.height && pos.x >=0 && pos.y >=0, "TMXLayer: invalid position");
CCASSERT(_tiles && _atlasIndexArray, "TMXLayer: the tiles map has been released");
CCASSERT(gid == 0 || (int)gid >= _tileSet->_firstGid, "TMXLayer: invalid gid" );
TMXTileFlags currentFlags;
uint32_t currentGID = getTileGIDAt(pos, ¤tFlags);
if (currentGID != gid || currentFlags != flags)
{
uint32_t gidAndFlags = gid | flags;
// setting gid=0 is equal to remove the tile
if (gid == 0)
{
removeTileAt(pos);
}
// empty tile. create a new one
else if (currentGID == 0)
{
insertTileForGID(gidAndFlags, pos);
}
// modifying an existing tile with a non-empty tile
else
{
int z = pos.x + pos.y * _layerSize.width;
Sprite *sprite = static_cast<Sprite*>(getChildByTag(z));
if (sprite)
{
Rect rect = _tileSet->getRectForGID(gid);
rect = CC_RECT_PIXELS_TO_POINTS(rect);
sprite->setTextureRect(rect, false, rect.size);
if (flags)
{
setupTileSprite(sprite, sprite->getPosition(), gidAndFlags);
}
_tiles[z] = gidAndFlags;
}
else
{
updateTileForGID(gidAndFlags, pos);
}
}
}
}
</pre><p></p><p>那么删除操作如下</p><p></p><pre name="code" class="cpp">void TMXLayer::removeTileAt(const Vec2& pos)
{
CCASSERT(pos.x < _layerSize.width && pos.y < _layerSize.height && pos.x >=0 && pos.y >=0, "TMXLayer: invalid position");
CCASSERT(_tiles && _atlasIndexArray, "TMXLayer: the tiles map has been released");
int gid = getTileGIDAt(pos);
if (gid)
{
int z = pos.x + pos.y * _layerSize.width;
ssize_t atlasIndex = atlasIndexForExistantZ(z);
// remove tile from GID map
_tiles[z] = 0;
// remove tile from atlas position array
ccCArrayRemoveValueAtIndex(_atlasIndexArray, atlasIndex);
// remove it from sprites and/or texture atlas
Sprite *sprite = (Sprite*)getChildByTag(z);
if (sprite)
{
SpriteBatchNode::removeChild(sprite, true);
}
else
{
_textureAtlas->removeQuadAtIndex(atlasIndex);
// update possible children
for(const auto &obj : _children) {
Sprite* child = static_cast<Sprite*>(obj);
ssize_t ai = child->getAtlasIndex();
if ( ai >= atlasIndex )
{
child->setAtlasIndex(ai-1);
}
}
}
}
}
在说一个比较常用的互相转换坐标的函数
CCPoint GameMap::tileCoordForPosition(CCPoint position)
{
int x = position.x / this->getTileSize().width;
int y = (((this->getMapSize().height) * this->getTileSize().height) - position.y) / this->getTileSize().height;
return ccp(x, y);
}
CCPoint GameMap::positionForTileCoord(CCPoint tileCoord)
{
CCPoint pos = ccp((tileCoord.x * this->getTileSize().width),
((this->getMapSize().height - tileCoord.y) * this->getTileSize().height));
return pos;
}
关于应用方面可以参考一下这个文章 http://cn.cocos2d-x.org/tutorial/lists?id=83