第四部分:TileMapLayer、TileSet、地图碰撞、层级和 Y-sort
这一部分开始进入 RPG 地图核心了。
你现在要先建立一个大脑模型:
TileSet = 图块资源库
TileMapLayer = 用图块画出来的一层地图
TileMap 编辑器 = 画地图的画笔工具
Collision = 地图上哪些地方不能走
Y-sort = 谁在前面、谁在后面的排序规则
在 Godot 4 现在的文档里,TileMapLayer 是用于 2D tile 地图的节点,它使用 TileSet 作为图块来源;旧的 TileMap 节点已经被标记为 deprecated,官方建议改用多个 TileMapLayer 节点来实现多层地图。(Godot Engine documentation)
1. TileSet 是什么
TileSet 可以理解成“地图素材库”。
比如你有一张图:
grass.png
里面包含:
草地
泥土地
石路
水边
墙壁
树
花
栅栏
屋顶
门
这些小格子被切出来之后,就变成一个个 tile。
TileSet 负责保存这些 tile 的信息:
这个图块显示哪一块图片
这个图块有没有碰撞
这个图块属于什么地形
这个图块有没有导航区域
这个图块有没有遮挡光照
这个图块有没有自定义数据
所以,TileSet 不是地图本身。
它更像是:
地图画笔库
前端类比:
TileSet ≈ UI 组件库 / icon sprite 图集 / CSS class 集合
你有了组件库,不代表页面已经搭好了。
你有了 TileSet,也不代表地图已经画好了。
2. TileMapLayer 是什么
TileMapLayer 才是你真正画地图的地方。
一个 TileMapLayer 代表一层地图。
比如:
GroundLayer:草地、泥土、石路
WaterLayer:水面、河流
ObjectLayer:树、石头、栅栏
DecorationLayer:花、草丛、小装饰
RoofLayer:屋顶、树冠、前景遮挡
每一层都可以使用同一个 TileSet。
官方文档也建议在合适的时候使用多个 TileMapLayer,这样可以区分前景、背景,并允许多个图块在同一个格子位置重叠。(Godot Engine documentation)
简单理解:
TileSet 是素材
TileMapLayer 是用素材画出来的一层地图
3. 为什么要分多层
不要把所有东西都画在一个 TileMapLayer 上。
这会让你后面非常痛苦。
比如你把草地、树、屋顶、墙壁、装饰全部画在一层:
WorldLayer
一开始没问题。
但后面你会遇到:
角色应该走在草地上
角色应该被树干挡住
角色应该能走到树冠后面
角色应该被屋顶遮住
水面可能要动画
地面不应该挡住角色
墙壁应该挡住角色
装饰有些能穿过,有些不能穿过
如果都在一层,管理起来会像一团毛线球,越扯越乱。
推荐结构:
World Node2D
├── Ground TileMapLayer
├── Path TileMapLayer
├── Water TileMapLayer
├── DecorationBack TileMapLayer
├── Objects TileMapLayer
├── Player CharacterBody2D
├── DecorationFront TileMapLayer
└── Roof TileMapLayer
更适合初学阶段的简化结构:
World Node2D
├── Ground TileMapLayer
├── Obstacles TileMapLayer
├── Player CharacterBody2D
└── Foreground TileMapLayer
这就够你先做一张 RPG 小地图了。
4. 地图层级推荐
对于你现在的 RPG,我建议先这样拆:
Main Node2D
└── World Node2D
├── Ground TileMapLayer
├── Objects TileMapLayer
├── Player CharacterBody2D
└── Foreground TileMapLayer
每层职责:
Ground:地面,草地、泥土、石路,一般没有碰撞
Objects:树干、墙、石头、栅栏,一般有碰撞
Player:玩家
Foreground:树冠、屋顶、前景遮挡,一般没有碰撞,只负责盖住角色
这样你就能实现:
角色站在地面上
角色被墙挡住
角色走到树后时,树冠盖住角色
这是 RPG 地图最基础但很关键的结构。
5. TileMapLayer 和 Player 的关系
玩家不是画在 TileMapLayer 里的。
玩家是一个独立节点:
Player CharacterBody2D
地图是地图,玩家是玩家。
结构一般是:
World
├── Ground
├── Objects
├── Player
└── Foreground
千万不要想着“把玩家也当作 tile 画到地图里”。
玩家需要:
输入控制
动画切换
碰撞移动
血量状态
交互检测
攻击范围
相机跟随
这些都不是普通 tile 该干的事。
tile 适合做静态地图。
角色、怪物、NPC、宝箱、门,后面大多应该做成独立 Scene。
6. TileSet 里的碰撞是什么
地图碰撞不是直接在 TileMapLayer 上画一个大碰撞。
更常见的做法是:在 TileSet 里给某些 tile 设置碰撞形状。
比如:
墙壁 tile:有碰撞
石头 tile:有碰撞
树干 tile:有碰撞
草地 tile:没有碰撞
花 tile:没有碰撞
水面 tile:看设计,可以有也可以没有
官方文档说明,如果 TileSet 里没有碰撞检测,玩家和其他对象就可以直接穿过地板或墙;要给每个 tile 定义碰撞、导航或遮挡形状,需要先在 TileSet 资源里创建对应的 physics、navigation 或 occlusion layer。(Godot Engine documentation)
你可以这样理解:
TileSet 里定义“这个图块能不能撞”
TileMapLayer 里放置“这个图块出现在哪里”
只要你在地图上画了一个带碰撞的墙 tile,角色移动时就会被挡住。
7. 怎么给 TileSet 加碰撞
大致流程是:
选中 TileMapLayer
↓
Inspector 里找到 TileSet
↓
展开 TileSet 资源
↓
找到 Physics Layers
↓
Add Element
↓
打开 TileSet 编辑器
↓
选择某个 tile
↓
给它画 Collision Polygon
如果是方块类图块,可以直接用矩形碰撞。
Godot 的 TileSet 编辑器里,创建 physics layer 后,可以进入 tile 的 Physics Layer 区域编辑碰撞形状;文档也提到可以在 TileSet 编辑器聚焦时按 F 快速创建矩形碰撞形状。(Godot Engine documentation)
比如:
墙壁 tile:整个 16x16 都碰撞
石头 tile:底部一小块碰撞
树 tile:只给树干底部碰撞
草地 tile:不设置碰撞
这里很重要:不是所有看起来“很大”的东西都应该整张图碰撞。
比如树:
树冠很大,但角色应该能走到树冠后面
树干才是真正挡路的部分
所以树的碰撞应该放在树干/底部区域,而不是整棵树。
8. 玩家为什么能被墙挡住
玩家要被墙挡住,需要两边都配置好。
玩家这边:
Player 是 CharacterBody2D
Player 下面有 CollisionShape2D
Player 移动用 velocity + move_and_slide()
地图这边:
墙壁 tile 在 TileSet 里有 collision
这个 tile 被画在 Obstacles TileMapLayer 上
Obstacles TileMapLayer 的 collision_enabled 是开启的
然后玩家移动时:
velocity = direction * speed
move_and_slide()
move_and_slide() 会处理 CharacterBody2D 和碰撞体之间的移动结果。
如果角色没被挡住,通常检查这几个地方:
Player 有没有 CollisionShape2D
CollisionShape2D 有没有设置 Shape
TileSet 里墙壁 tile 有没有画碰撞
TileMapLayer 的 collision_enabled 是否开启
Player 是不是用 move_and_slide() 移动
是不是直接 position += xxx 导致绕过碰撞
这个排查顺序非常实用。
9. 地面一般不要加碰撞
草地、泥土、石路这种地面,一般不加碰撞。
也就是说:
Ground TileMapLayer:通常没有碰撞
Objects TileMapLayer:通常有碰撞
Foreground TileMapLayer:通常没有碰撞
地面只负责显示。
障碍物才负责阻挡。
比如:
草地:能走
路:能走
花:可以穿过,除非你想挡路
小草丛:可以穿过
石头:不能穿过
墙:不能穿过
树干:不能穿过
河:通常不能穿过,除非做涉水
你以后做地图时,不要上来就给所有 tile 都加碰撞。
那样角色会像进了看不见的监狱,寸步难行,游戏会很“坐牢”。
10. TileMapLayer 的显示层级
默认情况下,Scene 树里越靠下的节点,越后绘制,也就是越容易盖在上面。
比如:
World
├── Ground
├── Objects
├── Player
└── Foreground
大致显示效果是:
Ground 在最底
Objects 在 Ground 上面
Player 在 Objects 上面
Foreground 在 Player 上面
所以 Foreground 可以盖住玩家。
但是如果你要实现“玩家在树前面时盖住树,玩家在树后面时被树盖住”,只靠节点顺序不够。
这就需要 Y-sort。
11. Y-sort 是什么
Y-sort 就是按 Y 坐标排序绘制顺序。
在 2D 俯视角或 3/4 视角游戏里,越靠屏幕下方的东西,通常越应该显示在前面;越靠屏幕上方的东西,通常越应该显示在后面。Godot 4 中可以在 CanvasItem 类型节点上启用 Y Sort Enabled,启用后子节点会按 Y 坐标参与排序。(kidscancode.org)
比如:
角色在树的上方
→ 角色应该被树挡住
角色在树的下方
→ 角色应该挡住树
这就是 RPG 里很常见的“前后遮挡”。
12. Y-sort 最容易踩的坑
很多人开了 Y-sort 还是没效果,是因为开错地方了。
Y-sort 要让“需要互相排序的节点”在同一个启用 Y-sort 的父节点下面。
比如:
World Node2D 开启 Y Sort Enabled
├── Objects TileMapLayer
├── Player CharacterBody2D
└── NPC CharacterBody2D
这样 Objects、Player、NPC 才可能互相按 Y 坐标排序。
如果结构是:
World
├── Tilemaps
│ └── Objects
└── Characters
└── Player
你只给 Tilemaps 开 Y-sort,Player 不在它下面,那它们就不是同一组排序对象,效果就可能不对。
简单记:
要互相比较前后关系的东西
必须放在同一个启用 Y-sort 的父节点下面
13. 为什么树的排序经常怪怪的
因为 Y-sort 排序看的是节点的 Y 坐标点。
很多素材的原点在图片中心。
比如一棵树:
图片高度 64px
原点在中心
但你真正希望用来排序的点是“树根”。
也就是:
树的底部
如果原点在树中心,角色走到树附近时,排序就可能提前或滞后,看起来怪怪的。
常见解决方式:
让树/角色的 position 对齐脚底
把 Sprite2D 向上偏移
碰撞体放在脚底附近
Y-sort 使用脚底位置作为排序基准
也就是说,节点位置不要放在图片中心,而是放在“它站在地面上的那个点”。
这点特别重要。
14. Player 的图片和碰撞应该怎么摆
推荐结构:
Player CharacterBody2D
├── AnimatedSprite2D
└── CollisionShape2D
让 Player 节点的位置代表脚底中心。
然后:
AnimatedSprite2D 往上偏移
CollisionShape2D 放在脚底附近
比如角色图片是 32x48。
可以这样想:
Player 节点原点:脚底中心
AnimatedSprite2D:position.y = -24 左右
CollisionShape2D:position.y = -6 左右
这样好处是:
Y-sort 用脚底判断前后
碰撞也主要发生在脚底
角色靠近墙和树会更自然
不要让碰撞体包住整个角色图片。
否则角色头发碰到墙就走不动了,体验很奇怪。
15. 树应该怎么做
树有两种做法。
第一种:树作为 tile。
Objects TileMapLayer 里画树干/树体
Foreground TileMapLayer 里画树冠
这种适合大量铺地图,简单直接。
第二种:树作为独立 Scene。
Tree StaticBody2D
├── Sprite2D
└── CollisionShape2D
这种适合需要交互的树,比如:
砍树
掉木材
季节变化
被风吹动
被火烧毁
对于你现在第一阶段,建议先用 tile 做树。
别一开始就把所有东西都做成独立 Scene,不然项目会膨胀得太快。
16. 什么时候用 tile,什么时候用独立 Scene
这个判断很重要。
适合用 tile 的东西:
草地
泥土
石路
水边
墙壁
普通树
普通石头
普通栅栏
普通装饰
适合用独立 Scene 的东西:
玩家
NPC
怪物
宝箱
门
传送点
可采集植物
可破坏木桶
任务物品
会动的平台
可以互动的机关
一句话:
只是摆在那里显示或挡路 → tile
需要逻辑、状态、交互、动画变化 → Scene
前端类比:
tile ≈ 静态背景元素
Scene ≈ 有状态组件
17. Foreground 前景层是什么
Foreground 是“盖在角色上方”的那一层。
比如:
树冠
屋顶
门框上半部分
洞口阴影
高草顶部
桥的栏杆
室内吊灯前景
它的目的不是挡路,而是制造空间感。
比如:
角色走到树后面
树冠盖住角色上半身
这会让地图立刻有 2.5D 的感觉。
虽然你的游戏本质还是 2D,但视觉上会有前后空间。
18. 地图层级的一个实用版本
现在你可以按这个来搭第一张地图:
Main Node2D
├── World Node2D
│ ├── Ground TileMapLayer
│ ├── Water TileMapLayer
│ ├── BackDecorations TileMapLayer
│ ├── YSortObjects Node2D
│ │ ├── Objects TileMapLayer
│ │ ├── Player CharacterBody2D
│ │ └── NPCs Node2D
│ └── Foreground TileMapLayer
└── UI CanvasLayer
解释一下:
Ground:最底层地面
Water:水面,可放在地面上
BackDecorations:不会遮挡角色的小装饰
YSortObjects:需要和角色排序的对象
Objects:树干、石头、墙等
Player:玩家
NPCs:NPC
Foreground:永远盖在最前面的树冠、屋顶等
UI:界面,不参与世界排序
初学阶段还可以更简单:
Main
├── Ground
├── Objects
├── Player
├── Foreground
└── UI
先别架构洁癖,能走、能撞、能遮挡,就是胜利。
19. TileMapLayer 的 Y Sort Origin
TileMapLayer 有一个很关键的属性:
Y Sort Origin
它表示这个 tile 用哪个 Y 偏移点参与 Y-sort。
官方文档说明,Y Sort Origin 是每个 tile 用于 Y-sort 的垂直偏移,只有在 CanvasItem 的 Y Sort Enabled 开启时才有效。(Godot Engine documentation)
比如树 tile 是 32x64。
如果默认按 tile 中心排序,可能不对。
你希望它按树底部排序,就可以调整 Y Sort Origin。
简单理解:
Y Sort Origin = 这个 tile 的“脚底”在哪里
对于树、房子、石头这类有高度的东西,这个属性很重要。
20. Z Index 和 Y-sort 的区别
这俩也容易混。
Z Index 是固定层级。
比如:
UI 永远在最上面
Foreground 永远盖住玩家
Ground 永远在最底下
Y-sort 是动态层级。
比如:
玩家在树上方 → 树盖住玩家
玩家在树下方 → 玩家盖住树
可以这样记:
Z Index:固定谁压谁
Y-sort:根据 Y 坐标动态决定谁压谁
实际项目里,它们会一起用。
比如:
Ground:Z Index = -10
YSortObjects:Z Index = 0,并开启 Y-sort
Foreground:Z Index = 10
UI:CanvasLayer,单独在界面层
21. CanvasLayer 为什么放 UI
UI 不应该放在 World 里和地图一起滚动。
比如血条、背包按钮、任务文本,它们应该固定在屏幕上。
所以 UI 一般放在:
CanvasLayer
结构:
Main
├── World
│ ├── Ground
│ ├── Player
│ └── Foreground
└── UI CanvasLayer
├── HPBar
├── BagButton
└── DialoguePanel
这样 Camera2D 跟随玩家移动时,地图会动,UI 不会跟着地图跑飞。
22. 地图碰撞的推荐设置
你的第一张 RPG 地图可以这样做:
Ground TileMapLayer
碰撞:关闭或不设置碰撞
Objects TileMapLayer
碰撞:开启
TileSet 中给墙、树干、石头、栅栏设置碰撞
Foreground TileMapLayer
碰撞:一般关闭
只负责显示遮挡
角色:
Player CharacterBody2D
CollisionShape2D:小一点,放脚底
移动:move_and_slide()
这个组合就能实现:
角色可以在草地上走
角色不能穿墙
角色不能穿过树干
角色可以被树冠盖住
23. 自动地形 Terrain 是什么
Terrain 是 Godot 4 里用于自动连接地形的系统。
比如你画水边、泥土边缘、草地边缘时,如果每个拐角都手动选 tile,会很累。
Terrain 的作用是:
你画一片区域
Godot 自动根据周围邻居选择合适的边、角、连接图块
官方文档里也说,Godot 4 的 terrains 是 Godot 3.x autotiling 的更强替代方案,可以根据 terrain 数据自动选择正确的 tile 变体。(Godot Engine documentation)
不过我建议你现在先别急着研究 Terrain。
你现在优先顺序应该是:
1. 能导入 TileSet
2. 能切 tile
3. 能画 Ground
4. 能画 Objects
5. 能设置碰撞
6. Player 能被挡住
7. 再研究 Terrain 自动连接
不然你会一下子被编辑器面板淹没。
Godot 的 TileSet 面板信息量很大,别硬刚,容易怀疑人生。它不是你笨,它是真有点绕。
24. Pattern 图案是什么
Pattern 可以理解成“一组 tile 的预设组合”。
比如你画了一个小房子:
墙
窗户
门
屋顶
你不想每次都重新摆一遍,就可以保存成 Pattern。
以后直接把这一组刷到地图上。
适合:
小房子
树丛组合
石头堆
篝火营地
路口装饰
固定尺寸的地毯
室内家具组合
官方文档说明,Pattern 虽然在 TileMap 编辑器中编辑,但会存储在 TileSet 资源里,因此保存为外部 TileSet 后,可以在不同 TileMapLayer 中复用。(Godot Engine documentation)
你可以把它类比成:
Figma 组件
Prefab 小组件
一组选中的 tile 保存成模板
25. 第一张 RPG 地图建议怎么画
不要一上来画大地图。
先画一个小地图:
30 x 20 格
目标:
一片草地
一条小路
几棵树
几个石头
一个小房子轮廓
一个出生点
地图结构:
Ground:整块草地
Path:铺一条泥土路
Objects:树干、石头、墙
Foreground:树冠、屋顶
Player:放在出生点
第一张地图只验证三件事:
角色能走
角色不能穿过障碍
角色遮挡关系基本正确
不要第一张地图就追求漂亮。
先把技术链路跑通。
漂亮可以后面慢慢换素材、调色、加雾、加雨、加光照。
26. 最小可用场景结构
你可以现在就按这个搭:
Main Node2D
├── World Node2D
│ ├── Ground TileMapLayer
│ ├── Obstacles TileMapLayer
│ ├── Player CharacterBody2D
│ │ ├── AnimatedSprite2D
│ │ ├── CollisionShape2D
│ │ └── Camera2D
│ └── Foreground TileMapLayer
└── UI CanvasLayer
先别加 NPC。
先别加天气。
先别加任务。
先别加卡牌。
先让这个最小场景能跑起来。
这就是项目的地基。
27. 地图制作的正确顺序
建议你按这个顺序来:
1. 准备 tileset 图片
2. 导入 Godot
3. 创建 TileSet
4. 设置 tile 尺寸,比如 16x16 或 32x32
5. 创建 Ground TileMapLayer
6. 用草地铺满一小块区域
7. 创建 Obstacles TileMapLayer
8. 放几块墙或树干
9. 在 TileSet 里给障碍 tile 设置碰撞
10. 创建 Player
11. 给 Player 加 CollisionShape2D
12. 用 move_and_slide() 移动
13. 测试是否会被障碍挡住
14. 添加 Foreground
15. 测试遮挡关系
这个顺序非常重要。
不要跳着来。
否则你会遇到一种很烦的情况:
不知道是 TileSet 错了
不知道是 TileMapLayer 错了
不知道是 Player 碰撞错了
不知道是移动代码错了
不知道是层级错了
一步一步验证,才不会迷路。
28. 常见错误 1:TileMapLayer 没有指定 TileSet
现象:
底部 TileMap 面板没有可用图块
不能画地图
原因:
TileMapLayer 的 TileSet 为空
解决:
选中 TileMapLayer
Inspector
Tile Set
新建 TileSet 或加载已有 TileSet
29. 常见错误 2:tile 尺寸设置错了
现象:
图块切得乱七八糟
一个 tile 里包含半个草地半个石头
画出来错位
原因:
TileSet 的 Tile Size 和素材实际格子不一致
比如素材是 16x16,但你设置成 32x32,就会切错。
你要确认:
素材每格到底是 16x16、24x24、32x32 还是别的
像素风 RPG 常见:
16x16
32x32
30. 常见错误 3:角色穿墙
排查:
Player 是不是 CharacterBody2D
Player 有没有 CollisionShape2D
CollisionShape2D 有没有 Shape
移动是不是用 move_and_slide()
障碍 tile 有没有 collision polygon
TileMapLayer 的 collision_enabled 有没有开
Player 的 Collision Layer / Mask 有没有和地图匹配
初学时先用默认 Layer/Mask,不要上来就改一堆碰撞层。
等你理解了再细分:
World
Player
Enemy
Hitbox
Interactable
31. 常见错误 4:角色被树冠挡得不对
原因通常是:
树冠放错层
Y-sort 没开
Y-sort 开在错误父节点
树的排序原点不对
Player 的原点不在脚底
Sprite 没有向上偏移
最简单解决:
树干放 Obstacles
树冠放 Foreground
Foreground 永远盖住 Player
这是简单粗暴但稳定的做法。
等后面你想做更自然的遮挡,再研究 Y-sort。
32. 常见错误 5:整个角色碰撞太大
现象:
角色还没碰到墙,看起来就被挡住
角色靠近树很别扭
角色头顶碰到东西也走不动
原因:
CollisionShape2D 包住了整张角色图
解决:
只让脚底附近有碰撞
比如:
角色图:32x48
碰撞:16x10,放在脚底附近
这对俯视角 RPG 特别关键。
33. 这一部分最重要的记忆点
TileSet 是图块资源库
TileMapLayer 是地图的一层
Godot 4 新项目建议用多个 TileMapLayer
Ground 通常没碰撞
Objects 通常有碰撞
Foreground 通常只负责遮挡
碰撞是在 TileSet 里给 tile 设置的
Player 是独立 CharacterBody2D,不是 tile
Y-sort 用 Y 坐标决定前后遮挡
Y-sort 要开在共同父节点上
角色和树的原点最好放在脚底
34. 你现在的小练习
做一个最小地图:
Main
├── Ground TileMapLayer
├── Obstacles TileMapLayer
├── Player CharacterBody2D
└── Foreground TileMapLayer
实现目标:
Ground 铺草地
Obstacles 放几块墙或树干
给障碍 tile 设置碰撞
Player 能移动
Player 不能穿过障碍
Foreground 放一个树冠或屋顶,能盖住 Player
这一步完成之后,你就真的有“RPG 地图雏形”了。
第五部分预告:Camera2D、地图边界、玩家出生点、场景组织
下一部分适合继续讲:
Camera2D 怎么跟随玩家
为什么相机不能无限看到地图外
地图边界怎么限制
Marker2D 怎么当玩家出生点
Main、World、Player、UI 应该怎么组织
如何为后面切换地图做准备如果您觉得这篇文章有帮助,请点个赞吧~
评论
请登录后发表评论
去登录