一、问题背景与核心矛盾

很多做2D游戏的开发者,不管是做平台跳跃、解谜还是开放世界RPG,都会用到Tilemap(瓦片地图)来铺场景。比如铺草地、墙壁、台阶,甚至是半透明的玻璃、可以踩的浮台,往往会按不同的功能分层,比如一层铺基础地形,一层铺装饰,一层铺可以碰撞的实体。但很多人会遇到一个头疼的问题:明明按顺序铺了层,结果显示的时候顺序乱了——比如本应在前面的玻璃跑到了后面,本应挡住角色的浮台反而被角色挡住;更麻烦的是,碰撞体也跟着乱套,比如角色能穿过本应挡住的墙,或者卡在不该卡的地方。 其实这个问题的核心,就是两个需求没分开:一个是“视觉上谁先显示谁后显示”(也就是渲染顺序),一个是“物理上谁能和谁碰”(也就是碰撞规则)。很多人习惯用同一个Z-index(层级值)同时管这两件事,结果就会顾此失彼——比如为了让玻璃显示在角色前面,把玻璃的Z-index设得比角色高,结果物理引擎会认为玻璃在角色前面,角色会被玻璃挡住没法穿过去;如果为了让角色能穿过玻璃,把玻璃的Z-index设得比角色低,玻璃又会显示在角色后面,完全看不到。

二、核心技巧拆解

要解决这个问题,就得把“渲染顺序”和“物理碰撞”彻底分开,各管各的,互不干扰。

2.1 先搞懂两个核心概念的本质

先不用记专业名词,用生活化的例子说:渲染顺序就像你排队买奶茶,谁站在前面谁先被看到,谁的位置就“靠前”;物理图层就像奶茶店的排队规则,比如有的队是给奶茶点的,有的是给咖啡点的,不同队的人不会互相插队(也就是不会碰撞)。以前的做法是用同一个“排队位置”同时管“谁站前面”和“谁能和谁碰”,结果自然乱;现在要做的是,给每个元素设两个独立的属性:一个管“站在第几排”(渲染顺序),一个管“在哪个队伍”(物理图层)。

2.2 具体操作步骤

首先,给所有需要区分渲染顺序的元素,单独设置渲染层级(Z-index),只用来控制谁显示在谁前面,和物理碰撞完全没关系;然后,给所有需要碰撞的元素,设置物理图层,只用来控制谁能和谁碰撞,和显示顺序完全没关系。 举个实际的例子,比如你做一个有草地、墙壁、玻璃、角色、敌人的场景:

  • 渲染顺序(Z-index)从低到高:草地(1)→墙壁(2)→玻璃(5)→角色(3)→敌人(4)。这样就能保证玻璃显示在角色和敌人前面,角色显示在敌人前面,符合视觉需求。
  • 物理图层分为:无碰撞(草地、玻璃)、角色碰撞(墙壁、角色、敌人)、敌人碰撞(墙壁、敌人)。这样就能保证角色能穿过玻璃,但会被墙壁挡住,敌人也会被墙壁挡住,角色和敌人能互相碰撞,符合物理需求。

三、完整示例演示

为了让大家更清楚,我们用Unity引擎来做一个完整的示例,因为Unity是目前做2D游戏最常用的引擎之一,大部分操作逻辑也能迁移到其他引擎。 技术栈:Unity 2D(2022.3 LTS版本)

3.1 准备工作

首先打开Unity,新建一个2D项目,然后导入一张草地、墙壁、玻璃、角色的瓦片图(可以用Unity自带的测试图,或者自己画简单的图形)。

3.2 设置渲染顺序(Sorting Layer)

Unity里的渲染顺序是用Sorting Layer来管理的,比直接改Z-index更方便。 步骤1:点击顶部菜单的Edit → Project Settings,找到Sorting Layers。 步骤2:点击加号,添加四个Sorting Layer,从上到下(从上到下代表渲染层级从高到低,也就是越靠上的层越先显示):

  • 玻璃层(最高)
  • 敌人层
  • 角色层
  • 墙壁层
  • 草地层(最低) 步骤3:点击每个层后面的数字,调整顺序,确保玻璃层在最上面,草地层在最下面。

3.3 设置物理图层(Physics Layer)

Unity里的物理图层是用Layer来管理的,用来控制碰撞规则。 步骤1:点击顶部菜单的Edit → Project Settings,找到Tags and Layers。 步骤2:点击加号,添加四个Layer:

  • 无碰撞层
  • 角色碰撞层
  • 敌人碰撞层
  • 墙壁碰撞层 步骤3:点击Edit → Project Settings,找到Physics 2D,点击Layer Collision Matrix(图层碰撞矩阵),设置碰撞规则:
  • 无碰撞层:和所有层都不碰撞
  • 角色碰撞层:和墙壁碰撞层、敌人碰撞层碰撞
  • 敌人碰撞层:和墙壁碰撞层、角色碰撞层碰撞
  • 墙壁碰撞层:和角色碰撞层、敌人碰撞层碰撞

3.4 给Tilemap设置属性

步骤1:右键点击Hierarchy面板,选择2D Object → Tilemap,新建一个Tilemap,命名为草地Tilemap。 步骤2:选中草地Tilemap,在Inspector面板里,找到Sorting Layer,选择草地层;找到Layer,选择无碰撞层。 步骤3:重复上面的步骤,新建其他Tilemap:

  • 墙壁Tilemap:Sorting Layer选墙壁层,Layer选墙壁碰撞层
  • 玻璃Tilemap:Sorting Layer选玻璃层,Layer选无碰撞层
  • 角色Tilemap:Sorting Layer选角色层,Layer选角色碰撞层
  • 敌人Tilemap:Sorting Layer选敌人层,Layer选敌人碰撞层

3.5 给Tilemap添加碰撞体

步骤1:选中墙壁Tilemap,点击Add Component,添加Tilemap Collider 2D组件,勾选Use By Composite;再添加Composite Collider 2D组件,勾选Is Trigger(如果需要角色可以踩在上面而不是穿过去,就不勾选Is Trigger)。 步骤2:选中角色Tilemap,添加Tilemap Collider 2D组件,勾选Use By Composite;再添加Composite Collider 2D组件,勾选Is Trigger。 步骤3:选中敌人Tilemap,添加Tilemap Collider 2D组件,勾选Use By Composite;再添加Composite Collider 2D组件,勾选Is Trigger。 步骤4:草地和玻璃Tilemap不需要添加碰撞体,因为它们的Layer是无碰撞层。

3.6 测试效果

现在你可以在Scene面板里移动各个Tilemap的位置,或者在Game面板里运行游戏,就能看到:玻璃显示在角色和敌人前面,角色显示在敌人前面,角色能穿过玻璃,但会被墙壁挡住,敌人也会被墙壁挡住,角色和敌人能互相碰撞,完全符合我们的需求。

四、应用场景、优缺点与注意事项

4.1 应用场景

这个技巧几乎适用于所有2D游戏的开发,尤其是以下场景:

  • 有半透明元素的游戏:比如玻璃、水面、烟雾、魔法特效等,需要显示在角色前面,但不需要碰撞的场景。
  • 有多层地形的游戏:比如平台跳跃游戏里的浮台、解谜游戏里的可移动平台、开放世界游戏里的高低地形等,需要不同的渲染顺序和碰撞规则。
  • 有大量装饰元素的游戏:比如草地、花朵、石头等装饰元素,需要显示在角色后面,但不需要碰撞的场景。
  • 有多个角色或敌人的游戏:比如RPG游戏里的NPC、敌人、宠物等,需要不同的渲染顺序和碰撞规则。

4.2 技术优缺点

优点:

  • 彻底解决了渲染顺序和碰撞规则的冲突,让视觉和物理逻辑都能按需求实现。
  • 方便维护和扩展,比如添加新的元素时,只需要给它设置对应的渲染层级和物理图层,不需要修改其他元素的属性。
  • 提高了开发效率,减少了因为顺序错乱而产生的bug,节省了调试时间。 缺点:
  • 增加了一定的开发成本,需要开发者额外设置渲染层级和物理图层,比以前用同一个Z-index管两个事要多花一些时间。
  • 容易出错,比如如果把物理图层设置错了,会导致碰撞规则混乱;如果把渲染层级设置错了,会导致显示顺序错乱,需要仔细检查。
  • 对新手不友好,新手可能会混淆渲染层级和物理图层的概念,不知道怎么设置。

4.3 注意事项

  • 渲染层级和物理图层要分开管理,不要用同一个属性同时管两个事。
  • 渲染层级的顺序要按视觉需求设置,越靠前的元素渲染层级越高;物理图层的碰撞规则要按物理需求设置,需要碰撞的图层要互相勾选。
  • 给Tilemap添加碰撞体时,要勾选Use By Composite,这样可以把多个Tile的碰撞体合并成一个,提高性能。
  • 要定期检查渲染层级和物理图层的设置,避免因为修改其他元素的属性而导致顺序错乱。
  • 如果遇到顺序错乱的问题,可以先检查渲染层级的设置,再检查物理图层的设置,逐步排查问题。

五、文章总结

Tilemap多层碰撞体渲染顺序错乱的问题,本质上是渲染顺序和物理碰撞的需求没有分开导致的。通过把渲染顺序和物理图层彻底隔离,用不同的属性分别管理视觉和物理逻辑,就能彻底解决这个问题。 在实际开发中,我们可以先明确场景的视觉需求和物理需求,然后按需求设置渲染层级和物理图层,再给Tilemap添加对应的碰撞体,最后测试效果。这个技巧不仅适用于Unity引擎,也适用于其他引擎,比如Godot、Cocos Creator等,只要引擎支持独立设置渲染顺序和物理图层,就能用这个方法解决问题。 最后要提醒大家,开发过程中要养成良好的习惯,比如给渲染层级和物理图层起有意义的名字,定期检查设置,避免因为粗心导致的bug。只要掌握了这个技巧,就能轻松解决Tilemap多层碰撞体渲染顺序错乱的问题,让你的游戏场景更符合需求。