LibGDX 游戏开发 之 瓦片地图 Tile maps

原文链接:https://github.com/libgdx/libgdx/wiki/Tile-maps

译者:重庆好爸爸 game4kids@163.com
谢绝转载

术语对照

  • 瓦片地图 - Tile Maps
  • 基类 - Base Classes

文章中提到的类

  • MapProperties
  • TiledMap
  • TiledMapTileLayer
  • TiledMapTile
  • TiledMapTileSet

瓦片地图

地图

LibGDX具有通用的地图API。所有与地图相关的类可以在com.badlogic.gdx.maps包中找到。com.badlogic.gdx.maps包含了基类,其下的子包包含了tile地图的其他类型地图的实现。

基类

基类,意味着是一个通用的集合,除了Tiled地图外,也支持其他2D地图格式。
地图由一系统图层组成。每个图层包含了一系统对象。地图,图层,对象都有属性,属性取决于加载方式。对于某些格式,还有专门的地图,图层和对象实现,稍后会有更多的内容。 基类的类层次结构如下所示:

image
image

属性

地图,图层,对象的属性在代码中由MapProperties类表现。MapProperties类本质上是一个Hash map。

地图,图层或者对象的属性的可用的Key-value取决于加载的格式。可以使用如下的方式去分别访问地图,图层或者uixiagn的属性:

map.getProperties().get("custom-property", String.class);
layer.getProperties().get("another-property", Float.class);
object.getProperties().get("foo", Boolean.class);

许多受支持的编辑器允许你在地图,图层和对象上指定这些属性。这些属性的类型取决于具体格式。 如有疑问,请在libgdx应用程序中使用一个地图加载程序去实际加载地图,并研究一下你感兴趣的属性。

地图图层

地图中的每个图层有时有排序和索引的,索引从编号0开始。可以用如下方法访问地图中的图层:

MapLayer layer = map.getLayers().get(0);

也可以通过名字查找图层:

MapLayer layer = map.getLayers().get("my-layer");

这些getter方法会返回一个Maplayer对象。有的图层可能被特殊化后提供了更多的功能,这种情况下,你可以进行强制转换:

TiledMapTileLayer tiledLayer = (TiledMapTileLayer)map.getLayers().get(0);

图层有几个属性,我们尝试为所有支持的地图格式归一化:(译者注:名字,透明度,可见性)

String name = layer.getName();
float opacity = layer.getOpacity();
boolean isVisible = layer.isVisible();

你也可以修改这些属性去影响图层的渲染。
除了这几个归一化的属性外,还可以用上面的方法访问更多的通用属性。

可用通过下面的方法,从图层中获取对象:

MapObjects objects = layer.getObjects();

MapObjects实例允许你通过名字,索引或者类型去获取对象,也可用通过它们去插入/删除对象。

Map 对象

API以已经提供了很多特定的MAP对象了,比如CircleMapObject, PolygonMapObject等等。

地图加载器会解析对象,并将它们放入相应的MapLayer中。

针对所有支持的格式,我们尝试针对所有对象,提取出下面归一化的属性:(译者注:名字,透明度,可见性,颜色)

String name = object.getName();
float opacity = object.getOpacity();
boolean isVisible = object.isVisible();
Color color = object.getColor();

特定的Map对象,比如PolygonMapObject,可能有额外的属性等等。

Polygon poly = polyObject.getPolygon();

改变任何一个属性都会影响到对象的渲染。

对象,和地图和和图层一样,你可以访问对象更多的通用属性。

注意: “Tiled”地图工具的tiles,不会保存为map对象。 有特别的图层可用来更有效的保存它们。 上面描述的对象一般用于定义:trigger areas, spawn points, collision shapes 等等。

地图渲染器 MapRenderer

MapRenderer接口,定义了渲染图层和对象的方法。

在你开始渲染前,你必须定义地图的View。把View想象成窗口。实现这一点的最简单的方法是告诉地图渲染器它可以使用的OrthographicCamera:

mapRenderer.setView(camera);

或者,你也可以手动指定投影矩阵和视图边界:

mapRenderer.setView(projectionMatrix, startX, startY, endx, endY);

View的边界由x/y定义,y轴朝上。所使用的单位是特定于地图和从其加载的格式。

通过调用如下方法如渲染所有图层:

mapRenderer.render();

如果你想要更多的控制渲染,你可以指定需要渲染图层的索引。比如:假设你有3个图层,2个背景和1个前景图层,你想渲染处于前景和背景之间的Sprite,可以这么做:

int[] backgroundLayers = { 0, 1 }; // don't allocate every frame!
int[] foregroundLayers = { 2 };    // don't allocate every frame!
mapRenderer.render(backgroundLayers);
renderMyCustomSprites();
mapRenderer.render(foregroundLayers);

通过单独渲染每个图层并修改每个图层的视图,您还可以实现视差效果。

Tiled Map

包含Tiles图层的地图,其处理的相关的类,在包com.badlogic.gdx.maps.tiled中。这个包里面也包括用于不同格式地图的加载器。

瓦片地图被加载到TiledMap类的实例中。TiledMap是通用地图类的子类,TiledMap中增加了额外的方法和属性。

Tiled Map的图层

包含tiles 的图层,是加载到 TiledMapTileLayer 实例。为了访问这些tiles,可以做如下转换

TiledMap tiledMap = loadMap(); // see below for this
TiledMapTileLayer layer = (TiledMapTileLayer)tiledMap.getLayers().get(0); // assuming the layer at index on contains tiles

其他通用的MapLayer有的全部属性,TiledMapTileLayer都有。除此之外,TiledMapTileLayer还有一个包含TiledMapTileLayer.cell实例的二维矩阵。

通过如下方法访问Cell:

Cell cell = tileLayer.getCell(column, row);

column和row指明了cell的位置,都是整数。tiles是在一个y轴向上的坐标系中。左下角的tile是(0,0),右上角的tile坐标为(tileLayer.getWidth()-1,tileLayer.getHeight()-1)

如果指定的位置没有tile,或者如果列/行参数超出范围,则返回null。

可以通过以下方式查询图层中的水平和垂直图块数:

int columns = tileLayer.getWidth();
int rows = tileLayer.getHeight();

Cells

Cell是 TiledMapTile 的容器。cell存储对Tile的引用,还有指定在渲染它时是否应该旋转或翻转瓦片的属性。

Tiles经常在多个cells之间共享。

Tilesets & Tiles

TiledMap包含一个或者多个 TiledMapTileSet 实例。Tileset 包含许多 TiledMapTile 实例。有多种方法实现tiles,比如:静态tiles,动画tiles等。您还可以为特殊目的创建自己的实现。

Tile图层中cells引用这些tiles。 Tile图层中的cells可以可以引用多个tileset中的tiles.为了减少纹理切换,建议每个图层只用一个tileset。

渲染Tiled Maps

为了渲染TiledMap和它的图层,你需要一个特定的MapRenderer实现。对于正交的或者自上而下视角的地图,可以使用OrthogonalTiledMapRenderer. 对于isometric地图,则使用IsometricTiledMapRenderer。其他的渲染器暂时是实验性的,不推荐。
通过下面方法创建渲染器:

float unitScale = 1 / 16f;
OrthogonalTiledMapRenderer renderer = new OrthogonalTiledMapRenderer(map, unitScale);

这个渲染器只能对传入构造函数的地图进行渲染。 这个限制允许渲染器对该特定地图执行优化,并缓存它们。

unitScale告诉渲染器一个游戏世界的unit是多少个像素。在上面的例子中,16个像素等同于1个游戏世界的unit. 如果需要1个像素和游戏世界的1个Unit对应,那么unitScale需要设置为1,以此类推。

unitScale是将渲染坐标系与游戏世界坐标系相结合的一种方式。

一个小例子:
假定你有一个tile地图,每个tile为32×32像素。在你的世界地图中,你想这些tile映射为世界地图的 1×1 unit的方块。你需要设置unitScale = 1/32f. 你现在可以将相机设置为在该单位刻度上。 假设你想在屏幕上查看地图的30x20 tile,那么你可以像这样创建你的相机:

OrthographicCamera camera = new OrthographicCamera();
camera.setToOrtho(30, 20);

如果使用的是IsometricTiledMapRenderer,则:

renderer = new IsometricTiledMapRenderer(isoMap, 1 / 32f);

再次,你必须指定地图(它应该是一个isometric tile地图)和一个unitScale。

注意:isometric渲染器是实验性的,使用风险自负,请报告您发现的任何问题。 从性能角度来看,移动设备上的渲染等距映射是非常昂贵的,因为每个瓦片必须混合。

加载TMX/Tiled地图

Loading TMX/Tiled maps


image

“Tiled”是一个通用的瓦片地图编辑器,可运行在windows/Linux/MAC OS X上,可以用来创建Tile图层和Object图层,包含用来触发区域的任意形状和其他目的。LibGGX提供一个加载器,可以用来读取“Tiled”生成的文件。

有两个方法可以加载瓦片地图:直接加载或者通过AssetManager。直接加载如下:

TiledMap map = new TmxMapLoader().load("level1.tmx");

这将会从assets文件夹加载这个叫做level1.tmx的文件。如果你想加载另外的文件类型,你必须在TmxMapLoader的构造其中提供一个FileHandleResolver。

TiledMap map = new TmxMapLoader(new ExternalFileHandleResolver()).load("level1.tmx");

我们选择这种机制,因为TmpMapLoader也可以用和AssetManager类一起使用,FileHandleResolvers统计了地址。通过AssetManager加载TMP地图的方法如下:

// only needed once
assetManager.setLoader(TiledMap.class, new TmxMapLoader(new InternalFileHandleResolver()));
assetManager.load("level1.tmx", TiledMap.class);

// once the asset manager is done loading
TiledMap map = assetManager.get("level1.tmx");

加载后,你可以把这个地图当成一个新的TiledMap.

注意:如果你通过“直接加载”的方式加载了TMX地图,你需要调用TiledMap#dispose()去释放。这个函数也会释放地图中所有加载的的textures.
注意:如果你想把TMX地图和GWT backend结合使用,你需要保证地图使用pure base64编码。由于GWT的限制,GWT不能使用压缩的TMX格式。

加载 Tide地图

image

“Tide” 是另外一个通过用的瓦片地图编辑器,只能适用于windows平台。(译者注:考虑程序员用MAC的较多,因此不推荐使用"Tide"). LibGDX也提供了支持Tide工具输出的加载器。

和TMX文件一样,你可以直接加载“Tide”文件或者通过AssetManager加载:

// direct loading
map = new TideMapLoader().load("level1.tide");

// asset manager loading
assetManager.setLoader(TiledMap.class, new TideMapLoader(new InternalFileHandleResolver()));
assetManager.load("level1.tide", TiledMap.class);

注意:如果你直接加载Tide地图,你需要调用TiledMap#dispose()去释放。这个函数也会释放地图中所有加载的的textures.

性能考虑 Performance considerations

我们希望渲染的速度越快越好,有一些东西你可以考虑下,一边加快渲染速度:

  • 仅使用图层中单个图块集中的图块。这将减少纹理绑定。
  • 将不需要混合的图块标记为不透明。 目前您只能以编程方式执行此操作,我们将提供在编辑器中自动执行的方法。
  • 不要超过层数。(Do not go overboard with the number of layers.???)

几个例子

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,980评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,422评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,130评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,553评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,408评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,326评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,720评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,373评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,678评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,722评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,486评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,335评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,738评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,283评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,692评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,893评论 2 335

推荐阅读更多精彩内容