You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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字符串即可。
整合你的工作流

现在你可以这样用:

  1. 用Tiled制作初始地图,导出为.tmx放入游戏资源目录。
  2. 游戏启动时用TmxMapLoader加载该.tmx。
  3. 玩家编辑地图时,直接操作TiledMap的图层cell数据。
  4. 保存时调用saveTmxToFile方法,把修改后的TiledMap写回原.tmx文件(或者新文件)。

这样就完全不需要中间的JSON转换了,直接在.tmx格式上完成读写,完美匹配你的需求!

内容的提问来源于stack exchange,提问作者werner_b

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 07:43:14