Godot 瓦片地图入门:TileMapLayer、TileSet、碰撞和 Codex 辅助开发

面向 Godot 4.x 初学者的瓦片地图教程,讲清 TileSet、TileMapLayer、分层地图、瓦片碰撞、Terrain 自动连接,以及如何让 Codex 辅助编写地图交互代码。

Godot 系列导航

想按顺序学习,可以先看合集,再根据当前任务跳到对应主题:

在 Godot 4.x,尤其是 4.3 之后的新项目中,建议使用 TileMapLayer。旧的 TileMap 节点已经被标记为弃用;现在更推荐一个地图层对应一个 TileMapLayer 节点。这样层级更清楚,也更符合 Godot 的节点组织方式。

官方文档也明确说明:TileMapLayer 是用于 2D tile-based maps 的节点,它使用 TileSet 创建网格地图;如果需要多层效果,可以使用多个 TileMapLayer

官方文档:

https://docs.godotengine.org/en/stable/classes/class_tilemaplayer.html

https://docs.godotengine.org/en/latest/tutorials/2d/using_tilemaps.html

先理解两个核心概念

瓦片地图最重要的是两个东西:

  1. TileSet
  2. TileMapLayer

TileSet 相当于瓦片素材库,里面保存:

  1. 草地、土地、墙壁、水面、道路等瓦片。
  2. 每个瓦片的碰撞形状。
  3. Terrain 自动连接规则。
  4. 导航、遮挡和自定义数据。

TileMapLayer 相当于画布。它使用 TileSet 里的瓦片来绘制地图。

推荐从这种节点结构开始:

1
2
3
4
5
6
World (Node2D)
├─ Ground (TileMapLayer)       # 草地、道路、地板
├─ Decoration (TileMapLayer)   # 花草、裂纹、阴影
├─ Walls (TileMapLayer)        # 墙壁、障碍物
├─ Objects (Node2D)            # 宝箱、门、NPC、金币
└─ Player (CharacterBody2D)

分层的好处很明显:

  1. 地面不会和墙壁混在一起。
  2. 可以单独设置显示顺序。
  3. 可以单独隐藏某一层。
  4. 只给墙壁层添加碰撞,地面层保持简单。
  5. 后续让 Codex 写脚本时,节点职责更清楚。

创建第一张瓦片地图

先准备一张瓦片图片,例如:

1
tileset.png

图片内部每个格子大小要一致,常见尺寸有:

1
2
3
16 × 16
32 × 32
64 × 64

假设素材每个瓦片是 32 × 32,那么 TileSetTile Size 也应该设置成 32 × 32

添加 TileMapLayer

在场景里添加:

1
2
Node2D
└─ TileMapLayer

TileMapLayer 改名为:

1
Ground

选中 Ground,在 Inspector 中找到:

1
Tile Set

点击:

1
2
<empty>
→ New TileSet

然后点击刚创建的 TileSet 资源,把 Tile Size 设置为素材格子大小,例如:

1
32 × 32

加入瓦片素材

选中 Ground 后,底部会出现 TileMap / TileSet 编辑面板。

tileset.png 拖到 TileSet 面板中,选择自动创建瓦片。Godot 会按照 Tile Size 把图片切成一块块小瓦片。

这一步完成后,TileSet 就拥有了可绘制的瓦片,Ground 这个 TileMapLayer 就可以使用它来画地图。

绘制地图

切换到底部的 TileMap 面板:

  1. 选择一个草地瓦片。
  2. 使用铅笔工具绘制。
  3. 用橡皮擦删除。
  4. 用矩形工具快速铺一片区域。
  5. 用填充工具快速铺满连续区域。

刚开始不要急着做大地图。先画一个 20 × 15 的小地图,只要有草地、墙壁和玩家出生点就够了。

给墙壁添加碰撞

不要给所有地面瓦片都加碰撞。通常只给这些不可通行内容添加碰撞:

  1. 墙壁。
  2. 石头。
  3. 栅栏。
  4. 水域边界。
  5. 悬崖。

选中 TileSet 资源,在 Inspector 中找到:

1
Physics Layers

点击:

1
Add Element

常见配置是:

1
2
Collision Layer: 1
Collision Mask: 1

然后在底部 TileSet 面板中:

  1. 切换到碰撞或物理绘制相关工具。
  2. 选择墙壁瓦片。
  3. 给墙壁区域绘制矩形或多边形碰撞。

这样,同一个 TileSet 中的不同瓦片就可以拥有不同碰撞形状。

玩家如何被瓦片墙挡住

玩家场景可以这样搭:

1
2
3
Player (CharacterBody2D)
├─ Sprite2D
└─ CollisionShape2D

玩家移动脚本可以从最小版本开始:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
extends CharacterBody2D

@export var speed: float = 200.0


func _physics_process(_delta: float) -> void:
	var direction := Input.get_vector(
		"move_left",
		"move_right",
		"move_up",
		"move_down"
	)

	velocity = direction * speed
	move_and_slide()

只要 Player 的碰撞层、遮罩与 TileSet 的物理层匹配,玩家移动时就会被墙壁瓦片挡住。

运行游戏时可以打开:

1
2
调试
→ 可见碰撞形状

这样能直接看到墙壁和玩家的碰撞形状,排错会轻松很多。

地图建议分成三层

初学阶段,推荐使用三层:

1
2
3
4
World
├─ Ground
├─ Walls
└─ Decoration

每个节点都是独立的 TileMapLayer

Ground 放:

  1. 草地。
  2. 泥土。
  3. 道路。
  4. 地板。

通常没有碰撞。

Walls 放:

  1. 墙壁。
  2. 悬崖。
  3. 水域边界。
  4. 不可穿越的石头。

通常有碰撞。

Decoration 放:

  1. 小花。
  2. 草叶。
  3. 地面裂纹。
  4. 阴影。

通常没有碰撞。

可以使用 z_index 控制基本显示顺序:

1
2
3
4
Ground: 0
Walls: 1
Decoration: 2
Player: 3

如果有树木、屋顶、桥洞这类需要遮挡玩家的对象,后面再考虑 Y Sort,或者把对象拆成上下两部分。第一张地图不必一开始就把遮挡系统做复杂。

使用 Terrain 自动连接道路和墙壁

如果你希望画地图时自动生成边缘和转角,例如:

  1. 草地自动连接泥土边缘。
  2. 道路自动生成转角。
  3. 墙壁自动判断上下左右。
  4. 水面自动生成岸边。

就需要使用 Godot 的 Terrain Set

基本流程是:

  1. TileSet 中添加 Terrain Set
  2. 设置匹配模式,例如按边和角匹配。
  3. 创建 Terrain,例如 GrassDirtWater
  4. 给各个瓦片标注对应的地形连接位置。
  5. TileMapLayer 中切换到 Terrains 绘制模式。
  6. 使用 ConnectPath 工具画地图。

Godot 会根据周围格子自动选择直线、转角、边缘瓦片。官方文档中也提到,Terrain 连接模式包括 ConnectPathConnect 更容易上手,Path 更适合需要更多人工控制的道路或路径。

刚开始不必立刻学习 Terrain。先手动画一张小地图,理解 TileSetTileMapLayer 和碰撞之后,再做自动连接。

在代码中操作瓦片

假设场景结构是:

1
2
World
└─ Ground

可以在 World 脚本中引用 Ground

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
extends Node2D

@onready var ground: TileMapLayer = $Ground

const SOURCE_ID: int = 0
const GRASS_TILE: Vector2i = Vector2i(0, 0)


func place_grass(cell: Vector2i) -> void:
	ground.set_cell(
		cell,
		SOURCE_ID,
		GRASS_TILE
	)

这里的 cell 是地图格子坐标。例如:

1
place_grass(Vector2i(5, 3))

表示在第 5 列、第 3 行放置一个草地瓦片。

注意:SOURCE_IDGRASS_TILE 的 atlas 坐标必须以你实际的 TileSet 为准,不能直接照搬示例。

世界坐标和格子坐标互转

鼠标点击的位置是世界坐标,而瓦片地图使用格子坐标。通常需要先转成本层的局部坐标,再转成地图格子。

世界坐标转格子坐标:

1
2
var local_position := ground.to_local(get_global_mouse_position())
var cell := ground.local_to_map(local_position)

格子坐标转回地图位置:

1
var position_in_layer := ground.map_to_local(cell)

如果要做鼠标点击放置瓦片,可以这样写:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
extends Node2D

@onready var ground: TileMapLayer = $Ground

const SOURCE_ID: int = 0
const GRASS_TILE: Vector2i = Vector2i(0, 0)


func _unhandled_input(event: InputEvent) -> void:
	if event is InputEventMouseButton:
		if event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
			var local_position := ground.to_local(
				get_global_mouse_position()
			)
			var cell := ground.local_to_map(local_position)

			ground.set_cell(
				cell,
				SOURCE_ID,
				GRASS_TILE
			)

如果还想支持右键删除瓦片,可以使用:

1
ground.erase_cell(cell)

set_cell()erase_cell()local_to_map()map_to_local() 都是 TileMapLayer 常用 API。

宝箱、门、金币不要都画成普通瓦片

这些对象通常不适合只画成普通瓦片:

  1. 金币。
  2. 宝箱。
  3. 可以打开的门。
  4. NPC。
  5. 敌人。
  6. 传送点。
  7. 机关。
  8. 可以破坏的箱子。

它们有脚本、动画、碰撞和信号,更适合做成独立场景。

例如金币:

1
2
3
Coin (Area2D)
├─ Sprite2D
└─ CollisionShape2D

然后放进:

1
2
World
└─ Objects

普通瓦片适合静态地图;有行为的对象适合独立 Scene。Godot 也支持 Scene Collection 类型的瓦片,但初学阶段手动放置独立场景更容易理解,也更容易排错。

Codex 最适合帮你做什么

Codex 很适合辅助这些工作:

  1. 写玩家移动脚本。
  2. 读取地图格子。
  3. 程序化生成地图。
  4. 随机铺地面。
  5. 鼠标放置和删除瓦片。
  6. 寻找出生点。
  7. 读取自定义瓦片数据。
  8. 检查 TileMapLayer API 用法。
  9. 实现金币、门、宝箱逻辑。

可以这样给 Codex 提示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
这是一个 Godot 4.x 项目。

场景结构:

World:Node2D
├─ Ground:TileMapLayer
├─ Walls:TileMapLayer
├─ Objects:Node2D
└─ Player:CharacterBody2D

请为 World 编写脚本:

1. 把鼠标世界坐标转换成 Ground 的格子坐标;
2. 左键放置草地瓦片;
3. 右键清除瓦片;
4. SOURCE_ID 和 atlas 坐标使用 @export 配置;
5. 使用 Godot 4.x TileMapLayer API;
6. 使用静态类型;
7. 不修改 TileSet 和场景节点名称。

Codex 不太适合这些工作:

  1. 判断素材应该切成多少像素。
  2. 视觉化绘制整张地图。
  3. 精细配置 Terrain 位掩码。
  4. 凭空猜 source_id
  5. 凭空猜 atlas 坐标。
  6. 大规模手写 .tscn 地图数据。

更合理的分工是:你在 Godot 编辑器中创建 TileSet、配置碰撞并绘制地图;Codex 负责编写地图交互、生成逻辑和 gameplay 脚本。

第一个练习怎么做

建议第一个练习只做一张 20 × 15 的小地图:

  1. Ground 铺草地。
  2. Walls 画一圈墙。
  3. 给墙壁瓦片配置碰撞。
  4. 放一个 Player
  5. 让玩家移动时被墙挡住。
  6. 再加入一个 Coin 场景。

这个练习做完,你就会真正理解 Godot 瓦片地图的基本工作流:

1
2
3
4
5
TileSet 负责瓦片素材和规则
TileMapLayer 负责绘制某一层地图
碰撞写在 TileSet 的瓦片上
有行为的对象做成独立场景
Codex 负责围绕真实节点结构写脚本

先把这个最小地图跑通,再去研究 Terrain、Y Sort、程序化生成和大地图加载,会稳很多。

记录并分享
使用 Hugo 构建
主题 StackJimmy 设计