☰
Tiled 2D瓦片地图实战:图层、碰撞体与引擎导出
2026/11/4 4:08:02 网站建设 项目流程

1. 从一块空白画布开始:Tiled到底在解决什么问题

第一次打开Tiled的人,十有八九会愣住:左边一堆图层,右边一个巨大灰白格子,中间工具栏图标看着都眼熟但不知道点哪个。很多人第一反应是"我是不是该先学个教程",然后去搜视频,看博主飞快地点了一通,回来自己还是不知道从哪下手。我当年也是这样,折腾了大半天,最后做出来的地图导进引擎只有一半能显示——原因后面会讲。所以这篇不打算做那种"功能大全"式的罗列,而是老老实实按一条能走通的路,从空画布一路做到一张导出后能直接拿去做碰撞、能挂对象、能分层渲染的地图。

先把Tiled是什么说清楚。Tiled是一款开源的2D关卡/地图编辑器,核心定位是"只管编辑、不管运行"——它不负责渲染逻辑、不负责物理、不负责游戏循环,只负责把地图数据整理成结构化的文件交给你。它支持正交(Orthogonal)、等距(Isometric)、六边形(Hexagonal)三种主流瓦片排布,支持瓦片层(Tile Layer)、对象层(Object Layer)、图像层(Image Layer)混合叠加,导出格式涵盖 TMX/TSX(XML)、JSON、CSV、Lua 等,几乎所有主流 2D 引擎都有现成加载器。

它解决的核心痛点是:把"摆场景"这件事从代码里解放出来。没有它,你要么在代码里手写几百行tiles[23][45] = 7,要么做一个简陋的内部编辑器;有了它,美术出的图集直接切瓦片,策划能自己拖场景,程序只需要解析文件。这个分工对独立开发者尤其重要——你一个人干三个人的活时,工具链的顺手程度直接决定项目能不能做下去。

适合谁看:有一点引擎基础(知道 Sprite、图集、坐标系)、但没正经用过Tiled的人;或者用过但只会画不能分层、不会挂对象、导出后总要手动修数据的人。零引擎基础也能跟,因为我会把每一步在干什么、为什么这么干都说清楚。全文用一张虚构的"某横版探险Demo"地图作为主线,尺寸定在40×25 瓦片、单瓦片 32×32 像素,也就是 1280×800 像素的地图区,这是最典型的桌面端 2D 场景规格,各种参数设置都以它为准。

提示:Tiled 的版本更新比较快,界面细节会有出入,本文所有操作用的是 1.10 之后的通用布局。如果你手里的版本菜单位置不一样,按名字找即可,逻辑没变。


2. 开工前的三件事:图集规格、网格对齐与工程目录

很多人跳过这一步直接开画,画到一半发现瓦片边缘有条缝、或者吸附怎么都对不齐,然后回过头返工。这一步花十分钟,能省掉后面两小时。

2.1 图集不是随便一张图:瓦片尺寸与留边规则

Tiled 的瓦片来源有两种:基于图集的瓦片集(Tileset from Image)和单张图片瓦片集。前者是把一张大图按固定网格切开,这是绝大多数项目的做法,因为省 Draw Call、方便统一管理。

关键在三个参数:

参数含义本例取值设错的后果
Tile width / height单个瓦片像素尺寸32 × 32切片错位,图案斜切
Margin图集最外圈留白0 或 2有留白却填0,全图偏移
Spacing瓦片之间的间隙0 或 2有间隙却填0,瓦片串边

我见过最常见的翻车是:美术导出的图集每块瓦片之间留了 2px 透明间隙(为了防止相邻瓦片采样时颜色渗透),但建瓦片集时 Margin 和 Spacing 全填了 0,结果每切一格就偏一点,画到第 20 列已经完全错位。判断方法很简单:在图像软件里量一下"第一块瓦片的左上角坐标"就是 Margin,"两块瓦片左上角的距离减去瓦片宽"就是 Spacing。

还有一个隐性坑:瓦片尺寸最好与游戏内渲染尺寸一致,或者是整数倍关系。如果你的瓦片是 32×32,但游戏里以 1.5 倍缩放渲染,又没开纹理的最近邻采样,就会出现瓦片接缝处的细线。这不是 Tiled 的问题,是采样问题,但在 Tiled 阶段就该定死规格,别等到渲染阶段才发现。

2.2 网格与吸附:先让画布"对得上"

新建地图(File → New → New Map)时,这几个选项必须一次设对,因为地图的瓦片尺寸事后改起来非常麻烦:

  • Orientation:选 Orthogonal(正交)。俯视角、横版、平台跳跃都用它。Isometric 是斜 45° 的伪 3D 视角,Hexagonal 是六边形战略图,初学者别碰。
  • Tile size:32 × 32,与图集一致。
  • Map size:40 × 25(单位是瓦片数,不是像素)。
  • Infinite:不要勾。无限地图适合超大地图或程序化拼接,但它导出的是分块(chunk)结构,很多引擎的加载器不支持,或者需要额外处理。做第一张图,固定尺寸最稳。
  • Tile render order:Right Down(默认)。它决定瓦片绘制顺序,除非你在做特殊视角,否则不改。

建完后打开 View → Show Grid 和 Snapping → Snap to Grid。吸附一定要开,否则手一抖瓦片就落在非整数格上,导出后引擎里会出现"半个像素"的诡异偏移,这种 bug 排查起来极其痛苦,因为肉眼几乎看不出来,但角色走过去会突然卡一下。

2.3 目录结构:现在乱,后面必乱

Tiled 的工程文件(.tmx)会以相对路径引用瓦片集文件和图片。这意味着如果你把 tmx 挪到别的文件夹,图片就全丢了,打开是一片红叉。

我用的结构是这样的,从项目第一天就固定:

assets/ maps/ demo-level-01.tmx tilesets/ terrain.tsx props.tsx textures/ terrain-32.png props-32.png

.tsx是独立的瓦片集文件,把瓦片集从地图里拆出来单独存的好处是:多张地图可以共用同一个瓦片集,改一次全生效。如果瓦片集内嵌在 tmx 里,每张地图都得重复配置一遍,纯属自找麻烦。

注意:瓦片集引用的是图片路径,图片路径又是相对于 tsx 的。所以 tsx 和图片的相对位置也要固定。我习惯把 tsx 和对应图片放在同一层目录,路径最短,最不容易出错。


3. 第一层地面怎么铺:用图层语义而不是"想到哪画到哪"

新手最典型的画法是:所有东西全塞在一个图层里,地面、草丛、石头、装饰全在 Tile Layer 1。图能看,但一进引擎就抓瞎——你想让角色走到草丛后面,做不到;想给石头单独加碰撞,得靠坐标硬编码。

正确的做法是按"渲染顺序 + 交互属性"分层,而不是按"我画到哪了"分层。

3.1 推荐的四层骨架与它们的职责

对一张横版或俯视角地图,我基本固定用这套结构(图层从上到下即渲染从后到前,Tiled 里列表越靠上越先画):

图层名类型职责是否参与碰撞
backgroundTile Layer天空、远山、纯背景色块否
groundTile Layer地面、地板、可站立面通常否(用对象层标)
decorationTile Layer草丛、石堆、贴地装饰否
collisionObject Layer墙、障碍、触发区、出生点是

为什么把 decoration 放 ground 之上?因为角色需要能"走到草丛前面被遮住一半"的效果时,你得再拆一层 foreground。这里先留个接口意识:图层是可以随时增删和调序的,别一开始就想设计完美,先跑通,再按实际遮挡需求拆。

另一个常被忽略的点:图层的偏移(Offset)和视差(Parallax)。background 层如果做远景,可以在图层属性里设parallaxx = 0.5,这样镜头移动时它只移动一半距离,立刻就有纵深感。这个属性是 Tiled 原生支持的,导出后引擎读取即可,不需要写代码。做横版游戏时这一条能极大提升观感,成本却几乎是零。

3.2 用"地形刷"而不是一个个点:Terrain 与 Wang 瓦片

手工一格一格点地面,画一张 40×25 的图要点上千次,还容易漏格。Tiled 内置了地形工具(Terrains),本质是基于"Wang 瓦片"(也叫边缘匹配瓦片)的自动拼接:你告诉它"这块瓦片是草地中心、这块是左边缘、这块是左上角",然后在地图上刷,它会自动选择正确的边缘瓦片。

配置路径:选中瓦片集 → 点瓦片集面板下方的"Terrains"标签 → 新建一个 Terrain(比如叫 Grass)→ 然后逐块给瓦片打上"属于哪个地形 + 处于什么位置"的标记。

这一步的真正门槛不在操作,而在图集本身是否规整。如果你的草地瓦片是"随意拼的 16 格",没有标准的 9 宫格或 47 格布局,Terrain 配置会非常痛苦。业界常用的是47 瓦片布局(包含所有内外角组合),也有简化版16 瓦片布局(只处理边缘不处理内角)。做第一张图,建议用 16 格简化版,够用且好配。

如果图集不规整、或者你不想配 Terrain,还有一条路:用矩形选择 + 填充。快捷键R切矩形选择,B用图章刷,或者选中一片区域后按Ctrl+F做区域填充。画大面积纯色地面时,区域填充比刷子快十倍。

3.3 一个让我返工两次的细节:地面不是"一层"

假设你的地面只有一层瓦片,那"地面上的坑"就没法表现——坑需要露出下面的背景,也就是地面层在那几格必须是空的。这没问题。但"地面上有一块略高的台地"呢?台地的侧面需要额外瓦片,而且它要盖住下面地面的边缘。

解决办法是把地面拆成ground-base(最底层填充)和ground-top(表面与侧面装饰)两层。base 层永远填满,保证没有空洞;top 层只在需要表现高差的地方画。这样导出后,引擎里做"踩地判定"只需要读 base 层的碰撞数据,视觉和逻辑解耦。

实测下来,这个拆法让我后面接物理时省了大量事——碰撞数据永远来自一个语义明确的层,而不是"从视觉层里猜哪里是地板"。


4. 让地图"活"起来:对象层、碰撞体与自定义属性

瓦片层解决"看起来是什么",对象层解决"它是什么"。这是 Tiled 从画图工具变成关卡数据工具的分水岭,也是绝大多数新手卡住的地方。

4.1 对象层里的四种东西:矩形、多边形、点、瓦片对象

在对象层里,你可以放四种对象:

  • Rectangle(矩形):最常用,做墙体、碰撞箱、触发区域。按住Ctrl拖拽可以以中心为基准画。
  • Polygon / Polyline(多边形/折线):做斜坡、不规则地形轮廓。斜坡用多边形而不是矩形,是因为很多物理系统支持"多边形碰撞体",斜边能直接走上坡。
  • Point(点):做出生点、路径点、事件锚点。一个点就是一个坐标,轻量。
  • Tile Object(瓦片对象):把某个瓦片作为"对象"放置,可以单独设翻转、缩放。适合放独立的装饰物或需要单独碰撞的物件。

所有对象都必须有名字(Name)和类型(Type/Class),这是引擎侧识别的唯一依据。我以前偷懒不填名字,导出后一堆Rectangle 1、Rectangle 2,在代码里只能靠索引判断哪个是墙、哪个是门——改一次地图就要改一遍代码,血泪教训。

命名规范建议:type用英文小写加连字符(wall、trigger-door、spawn-player),name用于同类区分(wall-01、wall-02)。别用中文做 type,某些引擎的解析器对编码不友好,会出玄学问题。

4.2 碰撞体怎么画才不影响手感

这是最体现经验的地方。同一个墙,碰撞体画得大一点小一点,手感天差地别:

  • 不要贴着瓦片边缘画满。角色通常有"宽度",如果墙面碰撞体和视觉墙完全重合,角色贴墙时会显得"陷进去"一点点。推荐内缩 1~2 像素(对 32px 瓦片),让视觉上留一点呼吸感。
  • 地面碰撞体用"一条线"而不是"一个块"。如果你给地面每格画一个方块碰撞,角色在格与格交界处可能会卡住(经典的"接缝卡顿")。正确做法是在整个地面顶部画一条长的、连续的矩形条,跨越多个瓦片,物理引擎处理连续平面比处理多个相邻方块稳得多。
  • 斜坡必须有明确方向。画斜坡多边形时,顶点顺序要一致(建议统一从左上开始逆时针),否则某些引擎会判定法线朝反方向,角色会从坡上掉下去。
  • 触发区域宁大勿小。门、存档点、对话触发区,画大一点不会难受;画小了玩家擦边走过没触发,那是 bug。

我踩过最深的坑是:用瓦片对象做碰撞。看着直观,但每种瓦片对象的碰撞箱要么得手调,要么和瓦片本身对齐得很死,最后地图里几百个对象,想整体调整一次墙的厚度,得逐个改。结论:瓦片层负责视觉,矩形对象负责碰撞,这条边界不要模糊。

4.3 自定义属性:把"策划参数"从代码里搬出来

Tiled 最被低估的功能是属性(Properties),它能在任何层级挂键值对:地图、图层、对象、瓦片集、单块瓦片都行。

举几个我这套 Demo 里实际用到的:

挂载位置属性名值用途
地图bgmlevel-01-theme进关卡播放哪首音乐
地图gravity980该关卡重力倍率
图层parallaxx0.5该层视差系数
对象(门)target-mapdemo-level-02进门跳到哪张图
对象(门)spawn-identry-from-01出生点匹配
瓦片is-watertrue该瓦片是否是水(用于音效、物理)

好处是改配置不用改代码。策划想调重力、想让某个门通向另一张图,在 Tiled 里改属性、存盘、重开游戏就行。这个工作流一旦跑通,你会发现自己再也懒得回去写if (levelId == 1) gravity = 980;这种代码。

注意:属性值有类型(字符串、整数、浮点、布尔、颜色、文件、对象)。类型选错会导致引擎侧解析失败——比如你把gravity设成字符串"980",代码里as number可能拿到 0。养成"设值时先看类型下拉框"的习惯。


5. 从编辑器到引擎:导出配置与"导完不能直接用"的排查链路

画完、挂完对象,点 File → Export,这步看起来最简单,实际上是我见过问题最集中的环节。下面把我自己踩过的完整排查过程写出来,遇到问题可以顺着走。

5.1 TMX 还是 JSON:先看你的加载器吃什么

格式优点缺点适用
TMX (XML)支持最全,所有加载器都懂文件大,解析慢,人眼难读通用、老项目
JSON轻量、易调试、脚本语言友好部分加载器只支持子集Web / 自研引擎
CSV数据极简,便于手改只含瓦片索引,丢图层和对象极简场景
Lua部分引擎原生通用性差特定框架

关键动作:导出前先确认你的加载器支持哪些特性。比如某些轻量加载器不支持"多瓦片集"、不支持"无限地图"、不支持"瓦片翻转位",这些限制在导出后才暴露,会让人误以为是 Tiled 的问题。我的做法是:先导一张4×4 的测试图,只放一个瓦片层和一个对象层,跑通加载流程,再回去画正式图。这个小测试图我保留到现在,换引擎时还会拿出来验一遍。

5.2 三条最常见的"导出来不对"

第一条:图片全红叉 / 路径丢失。99% 是相对路径问题。tmx 里存的是相对路径,一旦你把 tmx 从maps/挪走,或者把 tsx 单独移动,引用就断了。解决办法:File → 用 "Export As" 时注意输出位置,或者干脆在项目里固定maps/和tilesets/的相对层级,永不打乱。

第二条:瓦片显示错乱、图案错位。优先怀疑两个:一是瓦片集的firstgid冲突——当一张地图用了多个瓦片集,每个瓦片集的全局起始 ID 必须不同且连续,Tiled 会自动算,但如果你手动改过或者合并过文件,就可能撞车。二是尺寸参数不一致——地图 tile size 和图集 tile size 不同,渲染时会拉伸。

第三条:对象位置偏移。这个最隐蔽。先看你的对象坐标基准:Tiled 里对象的 x/y 默认是左上角,但有些引擎的碰撞体定义是中心点。如果你没做半宽半高的偏移修正,所有碰撞体都会整体偏移 1/2 尺寸。我见过有人以为自己的角色"脚陷进地里",其实是所有碰撞箱都偏了半个身位。修正方式:要么在 Tiled 里把对象的对齐锚点改成中心(对象属性里有),要么在代码里统一减去宽高的一半。两处只能挑一处改,改两遍会翻倍偏移。

第四条(补充):透明边缘出现黑边或白边。这是图像采样/预乘 Alpha 的问题,不属于 Tiled 导出错误,但在 Tiled 阶段能预防:图集导出时确保瓦片有 1~2px 的外扩像素(padding),并在引擎里关闭纹理压缩或改用最近邻采样。

5.3 版本管理与协作:tmx 是文本,但要当二进制对待

tmx 和 tsx 都是纯文本(XML/JSON),理论上可以 diff、可以合并。但实际上,两个人同时改同一张地图几乎必然冲突——因为瓦片数据的行是超长字符串,一冲突就是整行对不上。

我的处理方式:

  1. 地图文件按人分文件,不要两人共编一张图。需要拼大场景,用"切片地图 + 运行时拼接",各自负责一个区域。
  2. 把.tmx加入版本管理,但配置.gitattributes对 tmx 用-diff或binary标记,避免无意义的行级 diff 冲突。
  3. 每次导出后立刻提交一次,让"可运行状态"永远有存档点。地图这种数据文件,回滚成本比重新画低得多。

6. 跑通第一张图之后:性能、复用与扩展的几条实操建议

地图能加载、角色能走,说明主线通了。但距离"能用"还有一段,说几个我从实际项目里攒下来的东西。

6.1 瓦片层数量与 Draw Call 的关系

每一层 Tile Layer 在多数引擎里对应一个渲染批次。四层结构本身没问题,但如果你为了遮挡效果拆出七八层,draw call 就会上去,在低端设备或大分辨率下会掉帧。

实用原则:视觉上能合并的层就合并。比如 background 里的天空、云、远山,它们是不同视差系数才需要拆;如果视差一样,合成一层。另外,空图层记得删。Tiled 不会自动清理你新建但没用过的层,而很多加载器会为每个层创建空的渲染对象,白白消耗。

还有一个细节:瓦片集拆得太碎也会增加批次。一张地图用 5 个瓦片集,渲染时可能被切成 5 批。合理的做法是把"同屏高频出现的瓦片"放在同一个图集里(地形、常用装饰),把"低频、独立"的(BOSS 专属装饰、一次性道具)分开。这就是所谓"图集分组策略",在 Tiled 阶段规划好,比后期改省事。

6.2 一张图不够用:地图模板与复用

做第二张图的时候,不要从 File → New 重新配一遍参数——用"另存为 + 清空瓦片"。流程是:打开已经配好瓦片集、图层、属性的第一张图 → Save As 成demo-level-02.tmx→ 选中瓦片层用Ctrl+A全选后删除 → 保留图层结构和属性 → 开始画新内容。

这样能保证所有关卡的结构、属性键名、图层顺序完全一致,加载器代码一份就够。我甚至会做一个_template.tmx,里面所有图层都空着、属性都填好默认值、对象层里放好标准的出生点和出口对象,新关卡全部从它复制。这个习惯让我后面做十几个关卡时,代码侧一次都没改过。

6.3 三个我反复遇到的细节坑

坑一:翻转瓦片在部分引擎里失效。Tiled 支持对瓦片做水平/垂直翻转(快捷键X/Y),它通过给瓦片 ID 加高位标记实现。但一些简易加载器只取低位 ID,翻转信息直接丢掉,于是你在编辑器里看到的镜像瓦片,进游戏全变回原样。验证方法:放一块翻转瓦片,导出后看文件里的数值是否明显大于正常 ID(通常是加了0x80000000这种高位)。如果加载器不处理,就在图集里另存一份镜像瓦片,别依赖翻转。

坑二:透明度与混合模式。Tiled 图层支持自定义透明度和混合模式。用的时候要知道,不是所有引擎都支持"叠加/正片叠底"这类混合模式,很多时候只支持普通 Alpha。做发光、雾效时,优先在图集里画好半透明像素,而不是指望图层混合模式,这样跨引擎最稳。

坑三:单元格坐标和像素坐标的换算。Tiled 里所有对象坐标单位是像素,而瓦片层数据是格子索引。写代码时两套坐标混用是 bug 温床。我的习惯是在加载器里统一转成像素坐标(格子 × 瓦片尺寸),游戏逻辑全部用像素,只有绘制瓦片时才反算格子。写死一个转换函数,全项目只调它。

6.4 有些事不要在 Tiled 里做

最后说个反向经验:Tiled 不是万能的,有些东西硬塞进去只会让工作流变重。

  • 动画瓦片:Tiled 支持瓦片动画(给瓦片配多帧),但很多加载器不支持,或者支持得不完整。做水面、火焰这类循环动画,我更倾向在引擎里用 shader 或精灵动画做,Tiled 里只放一帧占位。
  • 大量逻辑:不要试图用对象属性表达复杂逻辑(比如"这个门需要三把钥匙才能开")。属性适合放静态配置,逻辑判断放代码。属性里塞 JSON 字符串那种做法,短期爽,长期是灾难。
  • 超大无缝地图:一张 500×500 的图在 Tiled 里编辑会很卡,导出文件也大。这种规模应该用"分块 + 运行时拼接",Tiled 只负责单块。无限地图模式虽然能做,但如前所述,加载器兼容性是硬门槛。

回到开头那句话:Tiled 只管编辑,不管运行。它的价值不在于功能多,而在于把地图数据整理成一套干净、可配置、跨引擎通用的结构。你把图层语义分对、把对象类型命名规范、把可变参数挂到属性上,这套数据就能在你换引擎、加功能、交给别人接手时都不用重做。我自己那张 40×25 的 Demo 图,从最初只能看,到后来接上物理、加视差、挂事件,中间改的全是配置和分层,瓦片一块没重画——这种"改起来不心疼"的状态,才是工具真正帮到你的地方。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询