Tiled Map Editer
制作TiledMap的工具很多,这里推荐一个免费的工具。
Tiled Map Editer
官网网站:https://doc.mapeditor.org/
简单的Tiled Map Editer教程
安装Tiled Map Editer,打开。
点击新建地图
块大小建议为32的倍数
地图方向
正常(普通地图)
45度
等角(交错)
六角(交错)
创建地图
创建图块的图集
打开之后
会根据下面的块设置将图片分隔成小块
随便铺满之后为
坐标系
对于瓦片地图来说,坐标系为:
原点:在左上角。
单位:瓦片数量
X轴正方向:从左到右
Y轴正方向:从上到下
坐标从(0,0)开始
将指针指向地图块,编辑器会在左下角显示
坐标和GID(全局表示ID)
地图层
编辑器提供了三种:对象层、图块层和图像图层。
图块层 TMXLayer
图块层提供了一种存储填充图块数据的大区域的有效方法。该数据是简单的图块引用数组,因此不能为每个位置存储附加信息。唯一存储的额外信息是一些标志,它们允许平铺图形垂直、水平或反对角线翻转(以支持 90 度增量旋转)。
渲染每个图块层所需的信息与地图一起存储,该信息根据方向和各种其他属性指定图块的位置和渲染顺序。
尽管只能引用图块,但图块层对于定义关卡中的各种非图形信息也很有用。碰撞信息通常可以使用特殊的图块集来传达,并且任何不需要自定义属性并且始终与网格对齐的对象也可以放置在图块层上。
每一个地图层可以被表示为TMXLayer类,并设置了名称。(如下图有三个地图层:Meta、Foreground、BackGround)
每一个单一的瓦片被表示为Sprite类,父节点为TMXLayer
每一个地图层只能由一套瓦片素材组成,否则会出问题。(如下面的右图所示,有两套瓦片素材(tile、meta),但是一个地图层只能使用一套瓦片素材)
简单来说,图块层用来绘制一些地图基本要素,这些地图要素基本上是固定的,如云、鸟、树木、仙人掌和草地等,相当于背景图
对象层 TMXObjectGroup
对象层很有用,因为它们可以各种图块层存储不了的数据。对象可以被随意放置、调整大小、旋转。对象层还可以进行个性化的配置。对象层有以下几种:
- Rectangle(矩形)-用来标记自定义的矩形区域
- Ellipse(椭圆形)-用来标记自定义的椭圆形区域或者圆形区域
- Point(点)-用来标记准确的位置
- Polygon(多边形)-用来标记当矩形或者椭圆形无法表示的对象(通常是碰撞区域)
- Polyline(折线)-用来标记游戏路线或者墙的碰撞路径
- Tile(图块)-用来进行任意放置、缩放,还有旋转图块的对象
- Text(文本-用来记录自定义文字、标注)
所有的对象都可以被命名,命名之后,他们的名字就会在他们上边显示(默认情况下,当你选择对象时显示)。对象也可以被定义类型,这样可以很方便地定义它们标签的颜色,还在一些有用的自定义属性。对于图块对象来说,这种类型可以是继承而来的。
对于大多数地图类型,对象都是以普通像素定位的。唯一的例外是等距地图(不是等距交错)。对于等距地图,将其位置存储在投影坐标空间中被认为是有用的。为此,假设等轴测图块表示投影的正方形,其两侧等于图块高度。如果在等轴测游戏中对对象使用不同的坐标空间,则需要相应地转换这些坐标。
对象的宽和高绝大多数是以像素的方式存储。对于等距地图,所有对象的形状(矩形、点、椭圆形、多边形、折线)都是投射到相同的坐标空间的。这是假设所有的这些对象被用来在地图上标记区域的情况。
作用:
- 用来添加除背景以外的游戏元素信息,如道具、障碍物等对象
- 一个对象层可以添加多个对象,每个对象的区域形状的单位是:像素点。
- 对象层中的对象在TMX文件中以键值对(key-value)形式存在,因此可以直接在TMX文件中对其进行修改
总的说来,对象层中可以圈出一些区域,一个区域就是一个对象,用来设置一些属性,我们可以获得通过代码获得这些对象属性。地图中对象与场景中精灵关联
图像层
图像图层提供了一种快速包含单个图像作为地图前景或背景的方法。它们目前的功能有限,您可以考虑将图像添加为 Tileset 并将其放置为Tile Object。这样,您就可以自由缩放和旋转图像。
但是,图像层可以通过其“重复 X”和“重复 Y”属性沿各自的轴重复 。
使用图像图层的另一个优点是,它可以避免在使用“选择对象”工具时选择/拖动图像。然而,从 Tiled 1.1 开始,这也可以通过锁定包含您想要避免交互的图块对象的对象层来实现。
GID
在cocos2dx中,每一个瓦片素材都有一个对应的全局唯一标识GID。瓦片的GID是指该块使用了哪个对应的图块素材。
注意,对应的图块集中,GID是延续之前的,也就是从1开始到当前图集的最后一个,下一个图集第一个是延续上一个图集的。
属性值(Properties)
每一个图层都会有一个属性,给瓦片图块设置“碰撞检测”属性、给对象层的某一对象设置“敌人类型”属性等等……
这些自定义的属性可以在地图编辑器中进行设置,并且可以在代码中获取这些属性以及对应的属性值
代码
大概的结构是
TMXTiledMap
瓦片地图类
包含了所有的地图层、对象层、以及瓦片地图的尺寸信息。
-
MapSize :瓦片地图的尺寸。(以瓦片数量为单位)
-
TileSize :瓦片的尺寸。(以像素点为单位)
对应的函数为:
const Size& getMapSize() const { return _mapSize; }
/** Set the map's size property measured in tiles.
*
* @param mapSize The map's size property measured in tiles.
*/
void setMapSize(const Size& mapSize) { _mapSize = mapSize; }
/** The tiles's size property measured in pixels.
*
* @return The tiles's size property measured in pixels.
*/
const Size& getTileSize() const { return _tileSize; }
/** Set the tiles's size property measured in pixels.
*
* @param tileSize The tiles's size property measured in pixels.
*/
void setTileSize(const Size& tileSize) { _tileSize = tileSize; }
两个创建函数:
//使用 .tmx 格式的文件创建瓦片地图
static TMXTiledMap* create(const std::string& tmxFile);
static TMXTiledMap* createWithXML(const std::string& tmxString, const std::string& resourcePath);
获取指定名称的地图层 TMXLayer
TMXLayer* getLayer(const std::string& layerName) const;
获取 指定名称的对象层 TMXObjectGroup
TMXObjectGroup* getObjectGroup(const std::string& groupName) const;
获取指定名称的属性值
Value getProperty(const std::string& propertyName) const;
获取所有属性。(键-值对)
ValueMap& getProperties();
通过GID获取图块的属性,返回ValueMap,即(键-值对)
Value getPropertiesForGID(int GID) const;
获取所有对象层。返回对象数组 Vector
const Vector<TMXObjectGroup*>& getObjectGroups() const { return _objectGroups; }
Vector<TMXObjectGroup*>& getObjectGroups() { return _objectGroups; }
TMXLayer
TMXLayer类为地图层类。包含了该地图层中,每个瓦片格子的信息。
该类继承于SpriteBatchNode。所有TMXLayer对象具有批量渲染的能力,Tile地图层就是由大量重复的图片构成,它们需要渲染提高性能
所以,每一个瓦片(Tile)都是一个Sprite类。
//获取地图层名字
const std::string& getLayerName() { return _layerName; }
//重新设置地图层名字
void setLayerName(const std::string& layerName) { _layerName = layerName; }
//获取 地图层的propertyName属性值
Value getProperty(const std::string& propertyName) const;
// 获取 地图层的所有自定义属性字典
ValueMap& getProperties();
// 获取地图层尺寸。一般等于瓦片地图的尺寸。(单位:瓦片数量)
Size& getLayerSize() const;
// 设置瓦片尺寸的大小。一般与瓦片地图的瓦片尺寸是一样的。(单位:像素)
Size& getMapTileSize() const;
// 获取 指定tile坐标的瓦片(Sprite)
Sprite* getTileAt(const Vec2& tileCoordinate);
// 通过Tile片坐标获得像素坐标,瓦片坐标y轴方向与像素坐标y轴方向相反。
// 指定tile坐标的瓦片对应的OpenGL坐标位置
Point getPositionAt(const Point& tileCoordinate);
// 通过Tile片坐标获得GID值。指定tile坐标的瓦片,所使用的图块的GID
int getTileGIDAt(const Point& tileCoordinate);
// 设置 指定tile坐标的瓦片,将其图片变为GID的图块
void setTileGID(uint32_t gid, const Vec2& tileCoordinate);
// 删除 也可以使用removeChild(sprite, cleanup)
void removeTileAt(const Vec2& tileCoordinate);
void removeChild(Node* child, bool cleanup) override;
TMXObjectGroup
对象层中的对象组合,继承于Ref,包含了该对象层中,每个对象的信息。
每一个对象,其所有属性,被存储为ValueMap。
ValueVector类型的别名是std::vector,vector是C++的容器类,它能够存放任意类型的动态数组,std是命名空间。
ValueMap类型的别名是std::unordered_map,unordered_map也是C++的容器类,它是一种无序的map类型,map是“键-值”对类型。
// 获取 对象层的名称
std::string& getGroupName();
// 重新设置对象层名称
void setGroupName(const std::string& groupName);
// 获取 对象层的propertyName属性值
Value getProperty(const std::string& propertyName) const;
// 获取 对象层所有属性
ValueMap& getProperties();
// 获取对象层指定的objectName对象,其所有属性被存储为ValueMap
ValueMap getObject(const std::string& objectName) const;
// 获取对象层的所有对象
ValueVector& getObjects();
TileMapAtlas
除了用TMXTiledMap创建瓦片地图类,还可以用贴图地图类TileMapAtlas。
需要用到TGR,TGR类似于地图图素排列的数据,也可以由相应的编辑器编辑
// 创建 tile地图图素集的图片路径,TGR图片的路径
static TileMapAtlas * create(const std::string& tile, const std::string& mapFile, int tileWidth, int tileHeight);
目前比较通用的方法是Tile编辑器,所以这种方法现在一般不用,其函数的功能也较TMXTiledMap少。
瓦片地图的锚点
瓦片地图的锚点默认为( 0,0),每个瓦片的锚点默认也为(0,0),锚点是可以设置的。
(1)普通瓦片锚点信息
(2)斜45°瓦片锚点信息
(3)斜45°交错瓦片锚点信息
坐标转换
普通瓦片
OpenGL坐标:原点为屏幕左下角(单位:像素)
Tile坐标:原点为瓦片地图的左上角(单位:瓦片)
// OpenGL坐标 转成 格子坐标
Vec2 tileCoordForPosition(const Vec2& position) {
Size mapSize = tiledMap->getMapSize();
Size tileSize = tiledMap->getTileSize();
int x = position.x / tileSize.width;
int y = (mapSize.height * tileSize.height - position.y) / tileSize.height;
return Vec2(x, y);
}
// Tile坐标 转成 瓦片格子中心的OpenGL坐标
Vec2 positionForTileCoord(const Vec2& tileCoord) {
Size mapSize = tiledMap->getMapSize();
Size tileSize = tiledMap->getTileSize();
int x = tileCoord.x * tileSize.width + tileSize.width/2;
int y = (mapSize.height - tileCoord.y) * tileSize.height - tileSize.height/2;
return Vec2(x, y);
}
层级关系
每个地图层的 zOrder(渲染顺序)会根据在地图编辑器中设置的前后关系进行设置。由下往上设置 zOrder 值,最靠后的 zOrder = 0,随后每个图层zOrder+1
(1)地图层之间
(2)瓦片之间
渲染顺序为:从左往右,从上到下。
下边的瓦片可以遮住上边的瓦片,右边的瓦片可以遮住左边的瓦片
例子
主要看Lua的。
在官方的例子中,有TileMapTest.lua文件。
TileMapAtlas
local layer = createTileDemoLayer("Editable TileMapAtlas")
local map = cc.TileMapAtlas:create(s_TilesPng, s_LevelMapTga, 16, 16)
-- Create an Aliased Atlas
map:getTexture():setAliasTexParameters()
local s = map:getContentSize()
local function updateMap(dt)
local tilemap = layer:getChildByTag(kTagTileMap)
local c = tilemap:getTileAt(cc.p(13,21))
c.r = c.r + 1
c.r = c.r % 50
if( c.r==0) then
c.r=1
end
tilemap:setTile(c, cc.p(13,21) )
end
;;....略....
TMXTiledMap
// 创建TileMap
local map = cc.TMXTiledMap:create("TileMaps/orthogonal-test2.tmx")
layer:addChild(map, 0, kTagTileMap)
// 获取大小
local s = map:getContentSize()
cclog("ContentSize: %f, %f", s.width,s.height)
//遍历子节点
local pChildrenArray = map:getChildren()
local child = nil
local pObject = nil
local i = 0
local len = table.getn(pChildrenArray)
for i = 0, len-1, 1 do
child = pChildrenArray[i + 1]
if child == nil then
break
end
child:getTexture():setAntiAliasTexParameters()
end
// 获取 Layer层 然后获取每一个瓦片块
map:setAnchorPoint(cc.p(0, 0))
local layer = map:getLayer("Layer 0")
local s = layer:getLayerSize()
local sprite = layer:getTileAt(cc.p(0,0))
sprite:setScale(2)
sprite = layer:getTileAt(cc.p(s.width-1,0))
sprite:setScale(2)
sprite = layer:getTileAt(cc.p(0,s.height-1))
sprite:setScale(2)
sprite = layer:getTileAt(cc.p(s.width-1,s.height-1))
sprite:setScale(2)
//获取对象层
local group = map:getObjectGroup("Object Group 1")
local objects = group:getObjects()
local dict = nil
local i = 0
local len = table.getn(objects)
//对象层小块获取和操作
for i = 0, len-1, 1 do
dict = objects[i + 1]
if dict == nil then
break
end
--------cclog("object: %x", dict)
local key = "x"
local x = dict["x"]
key = "y"
local y = dict["y"]--dynamic_cast<NSNumber*>(dict:objectForKey("y")):getNumber()
key = "width"
local width = dict["width"]--dynamic_cast<NSNumber*>(dict:objectForKey("width")):getNumber()
key = "height"
local height = dict["height"]--dynamic_cast<NSNumber*>(dict:objectForKey("height")):getNumber()
local color = cc.c4f(1,1,1,1)
drawNode:drawLine( cc.p(x, y), cc.p((x+width), y), color)
drawNode:drawLine( cc.p((x+width), y), cc.p((x+width), (y+height)), color)
drawNode:drawLine( cc.p((x+width), (y+height)), cc.p(x, (y+height)), color)
drawNode:drawLine( cc.p(x, (y+height)), cc.p(x, y), color)
end
// 动态修改 Zorder层级
local function repositionSprite(dt)
local p = cc.p(m_tamara:getPosition())
p = CC_POINT_POINTS_TO_PIXELS(p)
local map = ret:getChildByTag(kTagTileMap)
-- there are only 4 layers. (grass and 3 trees layers)
-- if tamara < 48, z=4
-- if tamara < 96, z=3
-- if tamara < 144,z=2
local newZ = 4 - math.floor((p.y / 48))
newZ = math.max(newZ,0)
map:reorderChild(m_tamara, newZ)
end
参考
http://www.taodudu.cc/news/show-1950915.html?action=onClick
https://www.codenong.com/cs106334928/