如何在libGDX中改写Tiled的.tmx文件?地图编辑保存咨询
嗨,这个需求我在做libGDX独立游戏的时候正好实现过!核心就是绕开中间的JSON转换,直接基于Tiled的.tmx格式做读写——用Tiled做初始地图,游戏里编辑TiledMap对象,最后把修改后的内容序列化回标准的.tmx XML文件就行。下面给你一步步拆解实现思路和代码:
核心逻辑梳理
libGDX的TmxMapLoader能轻松读取.tmx,但原生没提供直接写.tmx的API,不过没关系:.tmx本质是符合Tiled规范的XML文件,我们可以用libGDX自带的XmlWriter,把TiledMap里的图层、tileset、tile数据转换成对应的XML结构,直接写入文件就搞定了。
步骤1:读取并编辑TiledMap
先按你原来的方式加载.tmx,然后直接操作地图的图层数据就行,比如修改某个位置的tile ID:
// 加载初始Tiled地图 TiledMap tiledMap = new TmxMapLoader().load("tilemap/moontilemap.tmx"); // 获取要编辑的图层(比如第一个图层,你也可以按名称获取) if (tiledMap.getLayers().get(0) instanceof TiledMapTileLayer editLayer) { // 举个例子:修改x=5、y=3位置的tile为ID 10 TiledMapTileLayer.Cell targetCell = editLayer.getCell(5, 3); if (targetCell != null) { targetCell.setTileId(10, false); } }
步骤2:把修改后的TiledMap保存回.tmx
下面是我封装的保存方法,已经处理了坐标转换、tileset、CSV编码这些细节,直接用就行:
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.maps.MapLayer; import com.badlogic.gdx.maps.MapProperties; import com.badlogic.gdx.maps.tiled.TiledMap; import com.badlogic.gdx.maps.tiled.TiledMapTileLayer; import com.badlogic.gdx.maps.tiled.TiledMapTileSet; import com.badlogic.gdx.utils.XmlWriter; import java.io.IOException; public void saveTmxToFile(TiledMap map, String savePath) { // 先提取地图的基础属性 MapProperties mapProps = map.getProperties(); int mapWidth = mapProps.get("width", Integer.class); int mapHeight = mapProps.get("height", Integer.class); int tileWidth = mapProps.get("tilewidth", Integer.class); int tileHeight = mapProps.get("tileheight", Integer.class); // 用XmlWriter构建.tmx的XML结构 StringBuilder xmlContent = new StringBuilder(); XmlWriter xmlWriter = new XmlWriter(xmlContent); try { // 写入根节点<map>,符合Tiled的XML规范 xmlWriter.element("map") .attribute("version", "1.0") .attribute("orientation", "orthogonal") .attribute("width", mapWidth) .attribute("height", mapHeight) .attribute("tilewidth", tileWidth) .attribute("tileheight", tileHeight); // 处理Tileset(这里默认单个tileset,多个的话遍历map.getTileSets()即可) TiledMapTileSet mainTileSet = map.getTileSets().getTileSet(0); if (mainTileSet != null) { MapProperties tileSetProps = mainTileSet.getProperties(); xmlWriter.element("tileset") .attribute("firstgid", mainTileSet.getFirstGid()) .attribute("name", mainTileSet.getName()) .attribute("tilewidth", tileWidth) .attribute("tileheight", tileHeight); // 写入tileset的图片资源(如果是图片集类型的tileset) if (tileSetProps.containsKey("image")) { xmlWriter.element("image") .attribute("source", tileSetProps.get("image", String.class)) .attribute("width", tileSetProps.get("imagewidth", Integer.class)) .attribute("height", tileSetProps.get("imageheight", Integer.class)) .pop(); } xmlWriter.pop(); } // 遍历所有图层,写入每个图层的tile数据 for (MapLayer layer : map.getLayers()) { if (layer instanceof TiledMapTileLayer tileLayer) { xmlWriter.element("layer") .attribute("name", layer.getName()) .attribute("width", tileLayer.getWidth()) .attribute("height", tileLayer.getHeight()); // 用CSV格式存储tile数据(Tiled完全支持,比base64更易读) xmlWriter.element("data").attribute("encoding", "csv"); StringBuilder csvData = new StringBuilder(); // 关键:Tiled的y轴原点在顶部,libGDX在底部,所以要倒序遍历y for (int y = mapHeight - 1; y >= 0; y--) { for (int x = 0; x < mapWidth; x++) { TiledMapTileLayer.Cell cell = tileLayer.getCell(x, y); int tileId = cell != null ? cell.getTileId() : 0; csvData.append(tileId).append(","); } // 去掉每行末尾的逗号,换行 if (csvData.length() > 0) { csvData.setLength(csvData.length() - 1); } csvData.append("\n"); } xmlWriter.text(csvData.toString().trim()); xmlWriter.pop(); // 闭合<data> xmlWriter.pop(); // 闭合<layer> } } xmlWriter.pop(); // 闭合<map> } catch (IOException e) { Gdx.app.error("MapSaver", "保存.tmx文件失败", e); } // 把生成的XML写入文件 FileHandle saveFile = Gdx.files.local(savePath); saveFile.writeString(xmlContent.toString(), false); }
关键注意点
- 坐标转换:一定要注意Tiled和libGDX的y轴方向差异,保存时倒序遍历y值,不然地图会上下颠倒。
- 多Tileset支持:如果你的地图用了多个tileset,只要遍历
map.getTileSets(),逐个生成<tileset>节点就行,注意firstgid不能错。 - 扩展功能:如果你的地图有对象层(比如碰撞区域、NPC点位)或者tile自定义属性,只要在XML里对应加上
<objectgroup>或<properties>节点就行,逻辑和图层处理类似。 - 编码选择:上面用的是CSV编码,适合调试;如果地图很大,换成base64压缩编码会更省空间,只要把
<data>的encoding改成base64,再把tile数据转成base64字符串即可。
整合你的工作流
现在你可以这样用:
- 用Tiled制作初始地图,导出为.tmx放入游戏资源目录。
- 游戏启动时用
TmxMapLoader加载该.tmx。 - 玩家编辑地图时,直接操作
TiledMap的图层cell数据。 - 保存时调用
saveTmxToFile方法,把修改后的TiledMap写回原.tmx文件(或者新文件)。
这样就完全不需要中间的JSON转换了,直接在.tmx格式上完成读写,完美匹配你的需求!
内容的提问来源于stack exchange,提问作者werner_b
相关产品推荐
相关产品推荐

