news 2026/10/1 5:30:56

双网格自动瓦片:把47张地形瓦片压缩到9张的像素游戏工作流

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
双网格自动瓦片:把47张地形瓦片压缩到9张的像素游戏工作流

做独立游戏最折磨人的环节之一,就是画地形瓦片。我第一款横版像素demo做到一半就被卡住了:草地区域要加一圈泥土过渡边,美术朋友给我拉了一张表——上、下、左、右四条边,四个角,加上内角外角,各种邻接组合,最后精确到47张。当时我没觉得这个数字离谱,毕竟素材网上的地形包都是这么卖的。直到一个做引擎的朋友随口说了句“有种双网格方案,47张能压到9张”,我才发现被传统工作流按在地上摩擦了这么多年。这篇就把这套方法完整拆开:双网格(Dual Grid Autotiling)到底怎么回事、开源免费的工具链怎么选、在Tiled里怎么配置、Godot里怎么实现,以及我实际项目里踩过的一堆坑。如果你也在做像素风独立游戏,尤其是地形探索、建造类玩法,这篇应该能帮你省下大量重复劳动。

1. 47张地形瓦片是怎么变成行业痛点的

1.1 自动瓦片:不画满全图,就必须面对组合爆炸

要理解双网格的价值,得先知道47张这个数字怎么来的。最暴力的做法是压根不搞自动瓦片,直接在地图编辑器里一格一格铺满带边缘的手绘贴图。小场景勉强能扛,地图超过几百格时美术直接崩溃,而且后期改地形等于重画。

于是业界搞出了自动瓦片(AutoTile):地图引擎根据某个格子周围邻居的状态,自动挑选对应的瓦片贴上去。程序员只要告诉引擎“这格是草地、那格是水”,引擎自己判断哪里该出现过渡边缘。听起来很美好,但代价从底层就开始了——判断逻辑通常以中心格为基准,看它周围8个邻居各自是实心还是空地。

这个判断的规模是2的8次方,总共256种组合。去掉旋转对称和镜像翻转的重复项,剩下的唯一边缘形态大约在45到48种之间。所以你看素材网站上出售的地形瓦片包,一套完整的就是47或48张,中间那张基础填充,周围一圈全是各种过渡。很多引擎的地形集设计,包括一些成熟引擎里的自动瓦片规则,至今还是按这个标准来的。47张不是谁拍脑袋定的,是8邻接组合爆炸之后的自然产物。

1.2 手绘47张的实际情况:一个地形吃一整天

如果你只有一张32x32的过渡瓦片,画起来大概5到10分钟。这个速度已经算乐观了,像素画讲究边缘处理、颜色过渡、明暗变化,要画出自然纹理远比填充一个色块麻烦。47张逐张手绘,最顺利也要四五个小时,中间画烦了返工一下,一个工作日就没了。

更难受的是,现在的游戏不会只有一个地形。草地旁边有泥土,泥土旁边有水域,沙漠和岩石也接壤,每多一种地形类型就要再来一套过渡瓦片。场景里有五六种地形的时候,光边缘素材就攒了两百多张。而且这些瓦片之间风格必须统一,否则草地的泥土边和沙地的泥土边看起来像两个游戏。我现在一看到素材包里那一长排地形预览图还会条件反射地头疼。

1.3 现有替代思路为什么总是顾此失彼

面对47张的负担,大家不是没想办法。首先想到的是把8邻接降成4邻接,只判断上下左右四个正交方向。这样组合数从256降到了16,瓦片数确实锐减。但牺牲也很直接:对角方向的过渡没法表达,草地边缘在45度方向会出现断裂、台阶感,一眼看过去像马赛克拼错位了。像素游戏本来就在吃像素红利,这种瑕疵特别扎眼。

另一条路是程序化生成边缘,写算法根据地形数据实时画过渡带。这种方法胜在素材少、修改方便,但程序生成的边缘很难碰撞上人工手绘的质感,尤其是自然生态类游戏的石头、草丛、水面边缘,程序画出来总有一种“标准答案”的生硬感。美术想要的是手绘曲线,算法给的是直线和固定噪声,两者经常打架。

还有一类思路偏管理层面:把47张瓦片打包成模板,团队里所有人共用一套,靠规范和复用摊薄成本。这确实能缓解协作问题,但解决不了本质——只要你的地形判定是“中心格看8个邻居”,瓦片量的瓶颈就一直在那里。

2. 双网格思路:把地形判定和贴图画法拆开

2.1 核心机制:每块视觉瓦片只裁决2x2的逻辑格

双网格方案绕开了“中心格看8个邻居”这个前提。它的做法是把一套网格拆成两套来考虑:一套是逻辑地形网格(solid grid),它记录“这格有没有地形”;另一套是视觉渲染网格(render grid),它和逻辑网格错开半格,瓦片不再铺在逻辑格中心,而是铺在逻辑格的顶点位置。

听起来抽象,说具体一点。传统方案里,一张32x32的瓦片代表“一个逻辑格及其周围一圈邻居的状态”;双网格里,一张32x32的瓦片只负责“逻辑格四个顶点交汇处”那一小片区域。这个顶点周围只涉及四个逻辑格:左上、右上、左下、右下。

这样一来,每个视觉瓦片需要判断的邻接数量从8个降到了4个。判断范围缩小,组合规模也跟着缩小——2的4次方是16种组合,比256种少了一个数量级。而对玩家来说,画面上的边缘依然完整:因为每个瓦片都卡在逻辑格的顶点,它天然覆盖了四种地形状态的交界点。边缘过渡不是被取消了,是被拆分到了顶点上。

2.2 从47到9:这份减负账是怎么算出来的

16种组合听起来还是不少,但旋转和翻转能帮你消灭大量重复。四个逻辑格全实心,需要1张内部填充瓦片;四格全空,什么都不用画;两格实心,根据实心方向需要横向边、纵向边各1张;三格实心,剩下一个空角,需要4张内角缺口;对角两格实心,需要外角或者说角点瓦片,也通过旋转覆盖。

实操下来,一套基础地形通常只需要9张左右瓦片:1张全实心内部填充、4张边过渡、4张角过渡。有些实现会再加1张纯角点素材,总共10张。这和47张对比,降幅接近80%。更划算的是,后续每增加一种新地形,只需要再为这种地形补一小组顶点瓦片,而不是再背一遍47张。

这里要插一句:双网格渲染出来的画面并不是“模糊版”传统方案。单个顶点瓦片的分辨率和原先完全一样,只是它描述的空间范围从“一个逻辑格+一圈邻居”变成了“一个顶点的四种状态”。换句话说,双网格的瓦片数量少,不代表单张瓦片信息量下降,只是组合逻辑挪到了顶点层面。画质不打折,工作量打折。

2.3 常见的误解:双网格不是砍分辨率

有朋友第一次接触双网格时问,是不是等于把瓦片做成透明拼图,然后用更小的格子贴出来?其实不是。瓦片尺寸没变,内部纹理精度没变,变的只是“一张瓦片放在哪里”和“它代替哪些格子的边缘”。你可以把它理解成砌墙:传统方案是把整面墙的花纹都做成预制板,每种花纹都要单独开模具;双网格是砖块和勾缝分开处理,砖摆成什么样,勾缝材料自动填进去,而勾缝材料只需要有限的几种规格。

另一个常见误解是双网格只适合方方正正的格子地形,遇到圆角、斜角就废了。我的经验是:双网格主要解决的是“大量地形过渡组合”,斜角、圆角属于额外表现层,完全可以在顶点瓦片里增加少量素材来覆盖。后面专门聊扩展时会细说,这里先有个概念:双网格是一个把基础组合爆炸问题消解掉的工作框架,不是一套锁死美术风格的模板。

3. 开源免费工具链落地:Tiled + Godot + LibreSprite

3.1 选型清单与理由

原理讲清楚之后,问题就剩一个:用什么工具落地。如果你打开搜索引擎找“双网格瓦片地图绘制工具”,会发现现成的、开箱即用的单软件其实不多。我现在的做法是用三个开源组件拼成一条完整工具链,每个环节都免费且跨平台,长期维护也有社区保障。

环节工具选型理由
逻辑地形编辑Tiled老牌开源地图编辑器,对象层、自定义属性、多格式导出齐全,操作手感经过十年以上验证
运行时渲染Godot开源引擎,GDScript写顶点瓦片生成逻辑非常直接,内置TileMapLayer性能足够
像素素材绘制LibreSprite / Pixelorama开源免费的像素画工具,操作方式和Aseprite系一致,适合画顶点瓦片

当然这组不是唯一答案。你完全可以用Unity替换Godot,或者用LDTK替换Tiled,但本文后面的步骤以这套组合为准,原因是它们组合起来成本为零,而且每一环都便宜好上手。对个人开发者或两三人小团队来说,这已经是很舒服的状态了。

3.2 第一步:画好9张顶点瓦片

在LibreSprite里新建一个32x32的图集,横向排开9个格子,分别画:内部填充1张、上边缘、下边缘、左边缘、右边缘各1张、左上内角、右上内角、左下内角、右下内角各1张。图集总尺寸变成288x32,导出PNG备用。

画的时候有个小技巧:不要试图在一张瓦片里画出“完整地形边缘”,只要表达清楚“这个顶点附近的地形状态”就行。比如上边缘瓦片,实际内容就是上半部分是草地材质、下半部分是泥土过渡带,因为这张瓦片会被放在逻辑格的顶点上,旁边逻辑格的状态已经决定它该当哪条边的过渡。下意识用传统地形瓦片的画法反而会重复画好几遍渐变。

素材风格上,建议先画好一张内部填充作为基准,然后把它的边缘裁出来复制给过渡瓦片,这样能保证所有过渡纹路在视觉上连续。我一开始每张都独立画,结果相邻瓦片之间的草叶纹理根本对不上,接缝处像补丁,后来改成先搭基准纹理再裁剪叠加,就顺畅多了。

3.3 第二步:Tiled里用对象层定义逻辑地形

打开Tiled,新建地图,瓦片大小设置成逻辑格尺寸32x32。注意这里的地图网格尺寸和最终视觉瓦片尺寸一致,但地图本身只是“逻辑地形”的容器,并不直接承载视觉瓦片。

关键一步来了:新建一个对象层,命名“terrain”,用矩形工具把地图上的地形区块框出来。矩形尺寸必须是32的整数倍,保证导出后能对齐逻辑格。为什么推荐对象层而不是瓦片层?因为绝大多数地形的形状是连续区块,用矩形框选比一格一格刷瓦片快几个量级,而且这些矩形后续还能直接转成碰撞体,一份数据两处使用。

导出时选JSON格式,引擎端解析矩形数组,再按数组里的坐标把对应逻辑格子标记为实心。我的习惯是地图里每个矩形就代表一块实心地形,矩形之外的区域默认空地。这样Tiled这边不需要准备任何美术素材,它在这条链路里的角色纯粹是一个“逻辑地形编辑器”,这反而让关卡策划用起来很轻。

3.4 第三步:Godot里把逻辑地形变成渲染瓦片

运行时生成瓦片的思路非常简单,核心就是遍历逻辑顶点、读取周围四个格子、按16种组合选择图集区域。先用一个二维数组表示逻辑地形:

var solid_map: Array = [ [0, 0, 0, 0, 0, 0], [0, 1, 1, 1, 1, 0], [0, 1, 1, 1, 1, 0], [0, 1, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0], ]

生成脚本遍历每个顶点,取左上、右上、左下、右下四个格子的值,然后按状态映射到图集矩形:

func _region_for_vertex(tl: int, tr: int, bl: int, br: int) -> Rect2: # 1 表示实心,0 表示空地 if tl == 1 and tr == 1 and bl == 1 and br == 1: return Rect2(0, 0, 32, 32) # 内部填充 if tr == 1 and br == 1 and tl == 0 and bl == 0: return Rect2(32, 0, 32, 32) # 左侧过渡 if tl == 1 and bl == 1 and tr == 0 and br == 0: return Rect2(64, 0, 32, 32) # 右侧过渡 if bl == 1 and br == 1 and tl == 0 and tr == 0: return Rect2(96, 0, 32, 32) # 上侧过渡 if tl == 1 and tr == 1 and bl == 0 and br == 0: return Rect2(128, 0, 32, 32) # 下侧过渡 # 三格实心、一格空对应内角缺口 if tl == 0: return Rect2(160, 0, 32, 32) if tr == 0: return Rect2(192, 0, 32, 32) if bl == 0: return Rect2(224, 0, 32, 32) if br == 0: return Rect2(256, 0, 32, 32) return Rect2() # 四格全空:透明区域,不会调用

实现的时候记得做一步坐标换算,顶点位置要从逻辑格坐标转换出来。我的写法是对于逻辑地图宽w高h,遍历时x取0到w,y取0到h,也就是在网格交点位置生成瓦片。这里有个重要细节:顶点瓦片数量是(w+1)(h+1),而逻辑格数量是wh。换句话说,假如你在Tiled里画了一张100x100的逻辑地图,最终生成的瓦片网格是101x101,其中外圈有一圈纯粹的边界瓦片。这个特性给了一个钩子:只要逻辑地图四周再包一圈空地,视觉上就能自然生成完整的地形边缘。

为了性能,生成结果直接塞进TileMapLayer,用TileSetAtlasSource关联图集,通过set_cell批量填充,不要用循环创建Sprite2D节点。一个500x500的地图,顶点瓦片大约25万个,TileMapLayer完全扛得住,启动时生成一次,后续不再变动的话帧率没有影响。

4. 实测工程中的细节坑和应对方式

4.1 地图边缘缺半圈:忘了padding虚拟格

这是我第一次接入双网格时最头疼的Bug。逻辑地图明明画满了地形,但地图右侧和下侧边缘的过渡带不完整,草皮像被裁掉一条边。排查半天才想明白:顶点瓦片数量比逻辑格多一圈,生成时如果只遍历到w-1和h-1,最外侧一行的顶点根本没被处理,自然缺了样貌。

正确做法是在生成瓦片时,把遍历范围扩成(w+1)*(h+1),然后对逻辑格数组做padding。规则是:访问越界位置时,按“地图边界之外的格子都当作空地”处理。这样最外圈顶点会生成一圈完整的边缘过渡,让你的地形看起来有厚度而不像悬浮贴图。我把这步写进了生成函数,之后无论怎么改地形都不会再漏。

4.2 纹理滤波和贴图出血:像素风的第一隐形杀手

画好的瓦片接缝处出现一圈半透明的杂色,几乎每个像素游戏项目都会遇到,双网格方案也一样。根源通常是纹理过滤:图集在运行时被线性插值采样,瓦片边缘混入了相邻瓦片的颜色,看起来就像水彩晕染。

在Godot里,把TileSetAtlasSource关联的纹理filter设为Nearest,同时把图集每个瓦片之间留出1到2像素的间隔,或者在图集源里配置separator,就能彻底解决。LibreSprite里导出PNG前也建议给每张瓦片的四周留一点透明边距,渲染时空边距不会出画面,但能防止邻近瓦片颜色被采样进来。这个坑很蠢,但几乎所有第一次用图集做地形的人都会踩一遍。

4.3 碰撞体跟着哪套网格走:别让视觉和物理打架

双网格把视觉和逻辑拆成了两套网格,碰撞体就必须有明确归属。我的经验是:碰撞体直接从Tiled导出的矩形对象生成,而不是从视觉瓦片生成。逻辑地形是规则格子,转碰撞体最简单、最稳定,轨迹也容易预测。

但这里有一个视觉偏差要注意:视觉瓦片是铺在逻辑格顶点上的,它表现的地形边缘比逻辑实心格略微向外扩张了半个格子的视觉范围。表现在游戏里,玩家站在河边时脚尖可能会踩进“水”里一点点。解决方案是生成碰撞体时把每个矩形向外扩半格,或者在Tiled画矩形时直接把地形边界画大一些。两种方案选一种就行,关键是别让碰撞体和视觉层来回对不齐,否则玩家会觉得地面忽高忽低。

4.4 多地形、排序与光照:双网格不是只能单地形

有的朋友会问,这套方案是不是只能处理一种地形,草地和水的边界怎么办?答案是可以叠加。在Tiled里为每种地形单独建一个对象层,每个层的矩形渲染成一套顶点瓦片;然后把这些瓦片放进不同的RenderLayer,通过它们的Y坐标参与排序。比如水边地形,水的顶点瓦片画在底层,草地画在上一层,玩家站在草地后面时Y排序自然把他遮住,站到前面时显示正常。

光照阴影也有现成路径:直接用逻辑地形数据生成静态阴影,而不是拿视觉瓦片做轮廓提取。因为逻辑地形是干净的布尔数组,生成碰撞和阴影都是先天的方便。经验是不要试图让视觉瓦片、碰撞体、光照三套数据都从不同渠道生成,那样改一次地形要改三处,很容易不同步。

5. 省下的时间花在哪:工作流效率与扩展空间

5.1 直接量化:一套地形从画瓦片到跑通需要多久

我最近做一个洞穴场景,按传统方案先列瓦片清单,画到第30张左右的时候果断切到双网格。算了一下实际耗时:画9张顶点瓦片加微调,大约一小时四十分钟;写Godot生成脚本,因为之前模板现成,半小时;Tiled里框地形区块,整个洞穴两百多个矩形对象,半小时搞定。

对比之前做草地场景走传统自动瓦片流程:美术画47张地形花了整整一天,程序配自动瓦片规则又花了一个晚上。两套工作流做出来的边缘效果直观对比,差别不大,但投入时间差了四倍多。对独立开发者来说,这种压缩是直接把项目周期从“月”往“周”里推的级别。

后来我又意识到一个隐藏收益:修改地形的成本。传统方案要改地形边缘,得去素材库里翻对应瓦片,或者重新画一组过渡;双网格方案直接改Tiled里矩形对象的位置,引擎重启自动重新生成。关卡策划自己都能操作,不需要每次改版都拉着美术和程序一起排队。

5.2 斜面和圆角:双网格能不能继续扩展

能,而且扩展成本可以很平滑。双网格的瓦片映射本质上是一个“状态到图集矩形”的查表过程,你完全可以往表里加自己想要的状态。比如给顶点瓦片增加斜坡标记,就是为“上侧实心、下侧实心、左侧空、右侧空”这种特殊状态分配一张斜坡素材,同时在Tiled对象层里标记“此处是斜坡”。需要注意,斜坡通常不只是视觉问题,它还牵扯角色能否斜向站立、能否走上坡,所以碰撞体要单独处理。

圆角同理,在四个角的位置各画一张带圆弧的顶点瓦片,替换默认直角角块。对纯像素风格来说,圆角并非刚需,但一旦需要,双网格的扩展性比传统方案好得多——传统方案做一个圆角要同时画8张不同朝向的过渡瓦片,双网格只需要在现有基础上补2到3张。

5.3 哪些项目适合,哪些项目没必要折腾

如果你做的是地形密集的像素游戏,尤其是建造、探索、平台跳跃类,双网格几乎是必选项。地形过渡越频繁,看得见的信息越多,省下的工程量越大。反过来,如果你的游戏地形非常简单,比如就是一条平台或者纯背景板,或者你用程序生成的地形本身就不需要手绘质感,那确实没必要专门搭一套双网格工作流,传统方案或者干脆手绘图块就够了。

还有一个适用范围容易被忽略:换皮和多主题场景。独立游戏经常做主题皮肤系统,换一套草地换一套雪地。传统方案每换一次主题就要换47张素材,双网格每换一次只要画9张左右。这个数字差异在规模化以后非常可观,我现在每逢新主题都会庆幸当初没有坚持传统自动瓦片路线。

最后分享一个实际项目里养成的习惯:把Tiled里的对象层当作整个场景的唯一数据源。双网格瓦片生成、碰撞体生成、光照阴影、小地图标记,全部从这一层矩形派生。一开始你可能觉得绕,等地图改过三轮之后就会发现,单源数据的收益远远大于理解成本。这套方法我已经在持续维护的项目里跑了小半年,每次做新地形都是同一套流程:画9张新瓦片,框矩形,重启看效果。没有惊喜,也没有惊吓,稳定本身就是独立游戏开发里最值钱的东西。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/1 5:30:21

湖南GEO服务商哪家好 口碑服务商测评与选择指南

湖南GEO服务商哪家好?GEO服务机构找哪家、GEO服务机构排名、GEO服务商哪家好,是湖南本地实体企业、制造企业、本地服务商寻找GEO服务时最常问到的问题。很多企业在开展线上地理布局的时候,都会踩过多平台信息混乱、无效流量过多、无专人运维、效果没法量…

作者头像 李华
网站建设 2026/10/1 5:30:18

Hermes v0.10.0工具网关:从Agent自治到统一治理的实践解析

上周我把手头几个 Hermes Agent 实例从 0.9.x 升到 v0.10.0,Release 标题里Tool Gateway这个词第一眼并没有让我太兴奋——我当时的想法是,一个工具调用的聚合层能有多大事。真正升级完、把流量切过去之后,我才意识到这次 Release 的重点根本…

作者头像 李华
网站建设 2026/10/1 5:30:15

Redis Lua原子预扣实现大模型API多租户配额防透支

1. 项目概述:为什么大模型 API 配额管理不再是“加个计数器”就能解决的事最近三个月,我帮三家不同规模的 AI 应用团队做过 API 网关层的配额治理重构,其中两家都踩在同一个坑里:表面看是 Redis 计数器 每次请求前INCR再比对阈值…

作者头像 李华
网站建设 2026/10/1 5:29:54

FinalShell连接Ubuntu一直提示密码错误?一文讲透SSH认证排查

你打开FinalShell,填入ubuntu服务器的IP,用户名root,密码敲了一遍又一遍,回车之后还是弹窗:“密码错误”。你甚至把密码复制粘贴进去,确保每一个字符都没错,依然进不去。这种体验我太熟悉了&…

作者头像 李华
网站建设 2026/10/1 5:29:39

Redis 接入 AI 实战:语义缓存与向量检索落地指南

做后端的人应该都能感觉到,Redis 在我手里的角色最近一两年悄悄变了。以前接进来,要么是当缓存扛流量,要么是当分布式锁协调节点,再要么就是存个 Session、排行榜之类的临时数据。现在再看,Redis 已经被大量 AI 项目当…

作者头像 李华
网站建设 2026/10/1 5:29:25

广告牌检测数据集VOC+YOLO双格式114张,YOLOv8训练全流程

简介:面向城市管理与目标检测算法学习场景,街道乱放广告牌检测数据集提供114张真实街景图片,同时给出VOC与YOLO两种格式的标注框,类别统一为广告牌,共有165个矩形标注,适合用于违规广告牌识别模型的训练与验…

作者头像 李华