TMX是基于XML的开放标准,专为存储2D游戏瓦片地图数据设计,支持正交/等距/六边形等多种取向、多图层管理、瓦片集引用及自定义属性,由Tiled编辑器原生支持。

TMX 是 XML,别手写解析器
直接用 tinyxml2 或 pugixml 解析 TMX 是最稳妥的选择。TMX 结构嵌套深、可选字段多(比如 property 可出现在 map、layer、tileset 任意层级),手写字符串匹配或正则会漏掉 encoding="UTF-8"、compression="zlib"、base64 编码的图层数据等关键分支,后期维护成本极高。
推荐 pugixml:API 清晰、错误提示明确、支持 XPath(方便定位 //layer[data] 这类节点),且对 BOM 和换行兼容性比 tinyxml2 更好。
- 编译时加
-DPUGIXML_WCHAR_MODE避免 Windows 下中文property值乱码 - 加载前检查
doc.load_file()返回值,status != pugi::status_ok时打印doc.get_parse_result().description(),常见是缩进不规范或含不可见控制字符 - 不要假设
<map>下第一个<layer>就是主地图层——Tiled 允许任意顺序,必须按name或id匹配
图层数据解码:base64 + zlib/gzip 要分两步走
Tiled 默认导出的 <data encoding="base64"> 内容,实际可能是纯 base64,也可能是 base64 后再 zlib 压缩(compression="zlib")。直接调 base64_decode 后尝试 reinterpret_cast 为 uint32_t* 会崩溃——因为 zlib 压缩后字节流长度 ≠ 原始 tile ID 数量 × 4。
正确流程是先解 base64,再判断是否需要解压:
立即学习“C++免费学习笔记(深入)”;
- 读取
node.attribute("compression").as_string(),空字符串表示未压缩,"zlib"或"gzip"才需后续解压 - zlib 解压必须用
uncompress()(不是inflate()),因 Tiled 输出的是 raw deflate 流,无 zlib header - 解压缓冲区大小不能硬写 64KB——应先用
uncompress(NULL, &destLen, src, srcLen)获取所需大小,再分配内存
示例关键逻辑:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
std::string decoded = base64_decode(data_text);
if (comp == "zlib") {
uLongf destLen = 0;
uncompress(NULL, &destLen, (const Bytef*)decoded.c_str(), decoded.size());
std::vector<uint8_t> uncompressed(destLen);
uncompress(uncompressed.data(), &destLen, (const Bytef*)decoded.c_str(), decoded.size());
// destLen 现在是真实 tile ID 总数
}
瓷砖索引映射:注意 tileset 的 firstgid 和 offset
TMX 中 tile ID 不是直接数组下标。每个 <tileset> 有 firstgid 属性,表示该 tileset 中第一个 tile 在全局 ID 空间中的起始编号;而图层里存的每个数字是全局 ID。必须遍历所有 <tileset>,按 firstgid 排序,才能把全局 ID 映射到对应 tileset 内部索引(即 local_id = global_id - firstgid)。
容易忽略的点:
-
firstgid不一定从 1 开始,Tiled 新建项目默认是 1,但合并多个 tileset 后可能跳变(如 1, 100, 500) - 某些 tileset 带
source属性(外部 TSX 文件),此时firstgid在 TSX 里定义,主 TMX 中只存引用,需额外加载解析 - 图块旋转/翻转信息藏在 ID 的高 4 位(
FLIPPED_HORIZONTALLY_FLAG = 0x80000000),提取 local_id 前必须先 & 0x7FFFFFFF
属性读取:递归查找 property 节点很危险
TMX 规范允许 <property> 出现在 <map>、<layer>、<objectgroup>、<tileset>、<tile> 甚至 <object> 下。用 XPath //property[@name='speed'] 会跨层级混搜,导致“地图级 speed=5”被“某个 object 的 speed=10”覆盖。
安全做法是限定作用域:
- 读地图属性:只查
map_node.child("properties").child("property") - 读某图层属性:先定位
layer_node,再查其子节点properties - 避免用
for_each遍历所有property——Tiled 3.0+ 支持嵌套<properties>,深度不确定
另外,type 属性决定值类型:type="int" 要用 attribute("value").as_int(),否则字符串转整数可能截断(如 "123.45" → 123);type="bool" 必须显式比较字符串值是否为 "true",不能依赖 as_bool()(pugixml 的 as_bool() 对非 "true"/"false" 字符串返回 false,但 Tiled 实际可能输出 "1" 或 "yes")。
TMX 解析真正的复杂点不在语法,而在语义一致性——Tiled 导出选项(坐标系、压缩方式、外部引用)、版本差异(1.2 vs 1.10 的 tile offset 行为)、以及美术和策划对“同一个 property 名在不同层级含义相同”的默契假设,这些都得靠运行时校验和日志兜底。别省略对 firstgid 重叠、compression 值非法、data 内容为空等边界情况的检查。


















