Libgdx 具有多个倾斜集的 AtlasTmxMapLoader
Libgdx AtlasTmxMapLoader with multiple tilsets
我正在开发一款加载 Tiled 地图的 Libgdx 游戏。我正在处理的当前地图使用了 2 个图块集,一个用于 shadow/light,另一个用于地形和建筑物。我所做的一般过程一直运行良好,是我从艺术家那里收到精灵 sheet,设计地图,然后获取精灵 sheet 文件并使用 ImageMagick 将其拆分。从那里我获取分割图像并使用 TexturePacker 创建优化的 png 和 atlas 文件。
然而,这是我制作的第一张使用多个图块集的地图。我遇到的问题是在使用 AtlasTmxMapLoader 加载地图时,它依赖于地图中的单个地图集文件 属性。我的阴影和照明被分成单独的图像和图集,我宁愿不将它们全部合并为一个平铺(并且必须重新制作地图的一部分)。
也许我遗漏了一些简单的东西。我如何处理多个图块集?
我不是 LibGDX 专家,但我见过的几乎所有 tilemap 渲染器都依赖于 1 个 tileset。原因是它们是使用 OpenGL 渲染的。渲染器设置纹理并使用 1 个绘制调用绘制所有图块。您不能在两者之间切换纹理。
最好的方法是创建 2 个(或更多)单独的图层。每层使用 1 个 tileset。例如。 1 个用于背景,1 个用于阴影,1 个用于前景(例如墙壁)。
因此,在详细了解如何读取 .tmx 文件后,我能够解决我的问题。
以下是在使用多个图块集并在 TexturePacker 中重新打包您的 spritesheet 时如何正确执行此操作。首先,使用像 ImageMagick 这样的实用程序切割 tileset 图像并确保它们被索引(由文件名中的下划线和数字指定)。您可以像这样使用 ImageMagick 中的裁剪命令执行此操作:
convert.exe "shrine_tileset.png" -crop 16x16 "shrine_tileset_%02d.png"
其次,将所有图块集中的所有图块重新打包到 TexturePacker 中的单个图集中。如果它工作正常,您将在图集文件中看到每个 tileset 的名称以及基于 tile id 的关联索引。例如:
shrine_tileset
rotate: false
xy: 382, 122
size: 16, 16
orig: 16, 16
offset: 0, 0
index: 703
最后(这是我无法弄清楚的部分),确保每个 tileset 的 tile 索引从 .tmx 文件中的 "firstgid" 值开始。例如,我的第二个瓷砖 sheet 从 2049 开始,因为它们在第一个 sheet 中是 2048 个瓷砖。这应该在每个 tileset 的 .tmx 文件的顶部指出。
<tileset firstgid="2049" source="shadow_light.tsx"/>
因此,在为我的 tileset "shadow_light" 切割图块时,我会从索引 2048 开始,比 gid 小 1,例如:"shadow_light_2048.png".
希望这对某人有所帮助!
此问题已在 1.9.11 中修复。如果您使用的是早期版本,您可以通过修复覆盖 AtlasTmxMapLoader。
MyAtlasTmxMapLoader.Java
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.maps.ImageResolver;
import com.badlogic.gdx.maps.MapProperties;
import com.badlogic.gdx.maps.tiled.AtlasTmxMapLoader;
import com.badlogic.gdx.maps.tiled.TiledMapTile;
import com.badlogic.gdx.maps.tiled.TiledMapTileSet;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.GdxRuntimeException;
import com.badlogic.gdx.utils.SerializationException;
import com.badlogic.gdx.utils.XmlReader.Element;
public class MyAtlasTmxMapLoader extends AtlasTmxMapLoader {
/**
* Same as AtlasTmxMapLoader, but fixed to get the firstid attribute from the tileset element in the TMX file, not tsx file.
*/
@Override
protected void loadTileSet(Element mapElement, FileHandle tmxFile, ImageResolver imageResolver) {
if (mapElement.getName().equals("tileset")) {
String imageSource = "";
int imageWidth = 0;
int imageHeight = 0;
FileHandle image = null;
Element element = null;
String source = mapElement.getAttribute("source", null);
if (source != null) {
FileHandle tsx = getRelativeFileHandle(tmxFile, source);
try {
element = xml.parse(tsx);
Element imageElement = element.getChildByName("image");
if (imageElement != null) {
imageSource = imageElement.getAttribute("source");
imageWidth = imageElement.getIntAttribute("width", 0);
imageHeight = imageElement.getIntAttribute("height", 0);
image = getRelativeFileHandle(tsx, imageSource);
}
} catch (SerializationException e) {
throw new GdxRuntimeException("Error parsing external tileset.");
}
} else {
Element imageElement = mapElement.getChildByName("image");
if (imageElement != null) {
imageSource = imageElement.getAttribute("source");
imageWidth = imageElement.getIntAttribute("width", 0);
imageHeight = imageElement.getIntAttribute("height", 0);
image = getRelativeFileHandle(tmxFile, imageSource);
}
}
String name = element.get("name", null);
// Get the firstid attribute from the tileset element in the TMX file, not tsx file.
int firstgid = mapElement.getIntAttribute("firstgid", 1);
int tilewidth = element.getIntAttribute("tilewidth", 0);
int tileheight = element.getIntAttribute("tileheight", 0);
int spacing = element.getIntAttribute("spacing", 0);
int margin = element.getIntAttribute("margin", 0);
Element offset = element.getChildByName("tileoffset");
int offsetX = 0;
int offsetY = 0;
if (offset != null) {
offsetX = offset.getIntAttribute("x", 0);
offsetY = offset.getIntAttribute("y", 0);
}
TiledMapTileSet tileSet = new TiledMapTileSet();
// TileSet
tileSet.setName(name);
final MapProperties tileSetProperties = tileSet.getProperties();
Element properties = element.getChildByName("properties");
if (properties != null) {
loadProperties(tileSetProperties, properties);
}
tileSetProperties.put("firstgid", firstgid);
// Tiles
Array<Element> tileElements = element.getChildrenByName("tile");
addStaticTiles(tmxFile, imageResolver, tileSet, element, tileElements, name, firstgid, tilewidth,
tileheight, spacing, margin, source, offsetX, offsetY, imageSource, imageWidth, imageHeight, image);
for (Element tileElement : tileElements) {
int localtid = tileElement.getIntAttribute("id", 0);
TiledMapTile tile = tileSet.getTile(firstgid + localtid);
if (tile != null) {
addTileProperties(tile, tileElement);
addTileObjectGroup(tile, tileElement);
addAnimatedTile(tileSet, tile, tileElement, firstgid);
}
}
map.getTileSets().addTileSet(tileSet);
}
}
}
然后调用:
new MyAtlasTmxMapLoader().load(pathname)
我正在开发一款加载 Tiled 地图的 Libgdx 游戏。我正在处理的当前地图使用了 2 个图块集,一个用于 shadow/light,另一个用于地形和建筑物。我所做的一般过程一直运行良好,是我从艺术家那里收到精灵 sheet,设计地图,然后获取精灵 sheet 文件并使用 ImageMagick 将其拆分。从那里我获取分割图像并使用 TexturePacker 创建优化的 png 和 atlas 文件。
然而,这是我制作的第一张使用多个图块集的地图。我遇到的问题是在使用 AtlasTmxMapLoader 加载地图时,它依赖于地图中的单个地图集文件 属性。我的阴影和照明被分成单独的图像和图集,我宁愿不将它们全部合并为一个平铺(并且必须重新制作地图的一部分)。
也许我遗漏了一些简单的东西。我如何处理多个图块集?
我不是 LibGDX 专家,但我见过的几乎所有 tilemap 渲染器都依赖于 1 个 tileset。原因是它们是使用 OpenGL 渲染的。渲染器设置纹理并使用 1 个绘制调用绘制所有图块。您不能在两者之间切换纹理。
最好的方法是创建 2 个(或更多)单独的图层。每层使用 1 个 tileset。例如。 1 个用于背景,1 个用于阴影,1 个用于前景(例如墙壁)。
因此,在详细了解如何读取 .tmx 文件后,我能够解决我的问题。
以下是在使用多个图块集并在 TexturePacker 中重新打包您的 spritesheet 时如何正确执行此操作。首先,使用像 ImageMagick 这样的实用程序切割 tileset 图像并确保它们被索引(由文件名中的下划线和数字指定)。您可以像这样使用 ImageMagick 中的裁剪命令执行此操作:
convert.exe "shrine_tileset.png" -crop 16x16 "shrine_tileset_%02d.png"
其次,将所有图块集中的所有图块重新打包到 TexturePacker 中的单个图集中。如果它工作正常,您将在图集文件中看到每个 tileset 的名称以及基于 tile id 的关联索引。例如:
shrine_tileset
rotate: false
xy: 382, 122
size: 16, 16
orig: 16, 16
offset: 0, 0
index: 703
最后(这是我无法弄清楚的部分),确保每个 tileset 的 tile 索引从 .tmx 文件中的 "firstgid" 值开始。例如,我的第二个瓷砖 sheet 从 2049 开始,因为它们在第一个 sheet 中是 2048 个瓷砖。这应该在每个 tileset 的 .tmx 文件的顶部指出。
<tileset firstgid="2049" source="shadow_light.tsx"/>
因此,在为我的 tileset "shadow_light" 切割图块时,我会从索引 2048 开始,比 gid 小 1,例如:"shadow_light_2048.png".
希望这对某人有所帮助!
此问题已在 1.9.11 中修复。如果您使用的是早期版本,您可以通过修复覆盖 AtlasTmxMapLoader。
MyAtlasTmxMapLoader.Java
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.maps.ImageResolver;
import com.badlogic.gdx.maps.MapProperties;
import com.badlogic.gdx.maps.tiled.AtlasTmxMapLoader;
import com.badlogic.gdx.maps.tiled.TiledMapTile;
import com.badlogic.gdx.maps.tiled.TiledMapTileSet;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.GdxRuntimeException;
import com.badlogic.gdx.utils.SerializationException;
import com.badlogic.gdx.utils.XmlReader.Element;
public class MyAtlasTmxMapLoader extends AtlasTmxMapLoader {
/**
* Same as AtlasTmxMapLoader, but fixed to get the firstid attribute from the tileset element in the TMX file, not tsx file.
*/
@Override
protected void loadTileSet(Element mapElement, FileHandle tmxFile, ImageResolver imageResolver) {
if (mapElement.getName().equals("tileset")) {
String imageSource = "";
int imageWidth = 0;
int imageHeight = 0;
FileHandle image = null;
Element element = null;
String source = mapElement.getAttribute("source", null);
if (source != null) {
FileHandle tsx = getRelativeFileHandle(tmxFile, source);
try {
element = xml.parse(tsx);
Element imageElement = element.getChildByName("image");
if (imageElement != null) {
imageSource = imageElement.getAttribute("source");
imageWidth = imageElement.getIntAttribute("width", 0);
imageHeight = imageElement.getIntAttribute("height", 0);
image = getRelativeFileHandle(tsx, imageSource);
}
} catch (SerializationException e) {
throw new GdxRuntimeException("Error parsing external tileset.");
}
} else {
Element imageElement = mapElement.getChildByName("image");
if (imageElement != null) {
imageSource = imageElement.getAttribute("source");
imageWidth = imageElement.getIntAttribute("width", 0);
imageHeight = imageElement.getIntAttribute("height", 0);
image = getRelativeFileHandle(tmxFile, imageSource);
}
}
String name = element.get("name", null);
// Get the firstid attribute from the tileset element in the TMX file, not tsx file.
int firstgid = mapElement.getIntAttribute("firstgid", 1);
int tilewidth = element.getIntAttribute("tilewidth", 0);
int tileheight = element.getIntAttribute("tileheight", 0);
int spacing = element.getIntAttribute("spacing", 0);
int margin = element.getIntAttribute("margin", 0);
Element offset = element.getChildByName("tileoffset");
int offsetX = 0;
int offsetY = 0;
if (offset != null) {
offsetX = offset.getIntAttribute("x", 0);
offsetY = offset.getIntAttribute("y", 0);
}
TiledMapTileSet tileSet = new TiledMapTileSet();
// TileSet
tileSet.setName(name);
final MapProperties tileSetProperties = tileSet.getProperties();
Element properties = element.getChildByName("properties");
if (properties != null) {
loadProperties(tileSetProperties, properties);
}
tileSetProperties.put("firstgid", firstgid);
// Tiles
Array<Element> tileElements = element.getChildrenByName("tile");
addStaticTiles(tmxFile, imageResolver, tileSet, element, tileElements, name, firstgid, tilewidth,
tileheight, spacing, margin, source, offsetX, offsetY, imageSource, imageWidth, imageHeight, image);
for (Element tileElement : tileElements) {
int localtid = tileElement.getIntAttribute("id", 0);
TiledMapTile tile = tileSet.getTile(firstgid + localtid);
if (tile != null) {
addTileProperties(tile, tileElement);
addTileObjectGroup(tile, tileElement);
addAnimatedTile(tileSet, tile, tileElement, firstgid);
}
}
map.getTileSets().addTileSet(tileSet);
}
}
}
然后调用:
new MyAtlasTmxMapLoader().load(pathname)