引言
Godot 4 彻底重写了 TileMap 系统。如果你在 Godot 3 中用过 TileMap,那么几乎一切都变了 — TileSet 编辑器、瓦片的定义方式、自动瓦片、图层以及 GDScript API。本指南将从零开始讲解这套新架构。
Godot 3 用户: 旧的 autotile、set_cellv() 和 cell_size 都已移除。请参阅页面底部的从 Godot 3 迁移一节。
全新的 TileMap 架构
Godot 4 的 TileMap 系统由两个主要组件构成:
- TileMap 节点 — 你添加到场景中的节点。它包含一个或多个图层,并引用一个 TileSet 资源。
- TileSet 资源 — 定义瓦片本身。包含源(图集、场景集合等)、物理层、导航层、自定义数据层以及地形集。
关键变化: 在 Godot 3 中,瓦片尺寸是在 TileMap 节点上设置的。在 Godot 4 中,瓦片尺寸定义在 TileSet 资源里,TileMap 只是引用它。
TileSet 源
一个 TileSet 可以拥有多个源,每个源由一个 source_id(整数)标识。源的类型包括:
- TileSetAtlasSource — 最常见的类型。一张包含多个瓦片、按网格排列的纹理图像。
- TileSetScenesCollectionSource — 每个瓦片都是一个打包场景,适合用于带动画或可交互的瓦片。
设置 TileSet
步骤 1:创建 TileSet 资源
- 向场景中添加一个 TileMap 节点。
- 在检查器中点击 Tile Set 属性并选择 New TileSet。
- 设置 Tile Size(例如 16x16 或 32x32)。
步骤 2:添加图集源
- 点击 TileSet,在底部面板打开 TileSet 编辑器。
- 点击 + 按钮并选择 Atlas。
- 将你的瓦片集图像拖入 Texture 属性。
- 编辑器会根据你的瓦片尺寸自动将其切分为瓦片。你可以调整纹理区域和边距。
步骤 3:配置瓦片属性
在 TileSet 编辑器中,你可以添加应用于所有瓦片的各种层类型:
- Physics Layers — 瓦片的碰撞形状
- Navigation Layers — 用于寻路的可行走多边形
- Custom Data Layers — 任意的带类型数据(bool、int、float、String 等)
- Terrain Sets — 用于自动瓦片(Godot 3 autotile 的替代品)
TileMap 图层
单个 TileMap 节点可以包含多个图层。相比 Godot 3 这是一大改进 — 在 Godot 3 中你需要为背景和前景分别使用不同的 TileMap 节点。每个图层都可以有自己的 z-index、y-sort 和调制(modulate)设置。
常见的图层配置:
- Layer 0 — 背景(地面、地板) — z-index: -1
- Layer 1 — 地形(墙壁、障碍物) — z-index: 0
- Layer 2 — 前景(显示在玩家上方的装饰) — z-index: 1
图层的 GDScript API
# Set a cell on layer 0
tilemap.set_cell(0, Vector2i(5, 3), source_id, atlas_coords)
# Get cell info
var source = tilemap.get_cell_source_id(0, Vector2i(5, 3))
var coords = tilemap.get_cell_atlas_coords(0, Vector2i(5, 3))
# Erase a cell
tilemap.erase_cell(0, Vector2i(5, 3))
# Check if a cell is occupied
if tilemap.get_cell_source_id(0, Vector2i(5, 3)) != -1:
print("Cell has a tile")
API 说明: set_cell() 的第一个参数始终是图层索引(0, 1, 2...)。第二个是以 Vector2i 表示的单元格位置。第三个是 source_id,第四个是以 Vector2i 表示的 atlas_coords。
地形系统(自动瓦片)
地形系统取代了 Godot 3 的 autotile。它会根据相邻瓦片自动选择正确的瓦片变体。
设置地形
- 在 TileSet 中添加一个 Terrain Set(检查器 → Terrain Sets → Add Element)。
- 选择地形模式:Match Corners and Sides(47 个瓦片)、Match Corners(16 个瓦片)或 Match Sides(16 个瓦片)。
- 在该集合内添加地形类型(例如「Grass」「Dirt」「Water」)。
- 在 TileSet 编辑器中切换到 Select 模式,选择一个瓦片,然后在每个瓦片上绘制地形连接位(peering bits)。
绘制地形
地形配置完成后,切换到 TileMap 编辑器并选择 Terrains 标签页。选择你的地形类型并直接在地图上绘制,Godot 会自动选择正确的瓦片变体。
# Set terrain programmatically
tilemap.set_cells_terrain_connect(0, [Vector2i(5, 3)], 0, 0)
# Parameters: layer, cells array, terrain_set, terrain index
瓦片上的物理
要为瓦片添加碰撞:
- 在 TileSet 检查器中添加一个 Physics Layer。
- 配置此物理层所使用的碰撞层和掩码。
- 在 TileSet 编辑器中切换到 Physics 标签页,为每个瓦片绘制碰撞多边形。
提示: 你可以为不同的瓦片类型设置多个物理层。例如,一层用于墙壁(阻挡移动),另一层用于危险区(通过 Area2D 重叠触发伤害)。
瓦片上的自定义数据
自定义数据层让你能够为任意瓦片附加带类型的元数据。这对游戏逻辑极为有用。
设置自定义数据
- 在 TileSet 检查器中添加一个 Custom Data Layer。
-
为其命名(例如「is_destructible」)并设置类型(例如
bool)。 - 在 TileSet 编辑器中切换到 Custom Data 标签页,为每个瓦片设置值。
在代码中读取自定义数据
var tile_data = tilemap.get_cell_tile_data(0, Vector2i(5, 3))
if tile_data:
var is_destructible = tile_data.get_custom_data("is_destructible")
var damage = tile_data.get_custom_data("damage")
if is_destructible:
destroy_tile(Vector2i(5, 3))
常见的自定义数据示例:
is_destructible: booldamage: intmovement_cost: floattile_type: String(例如 "water"、"lava"、"ice")spawn_chance: float
程序化放置瓦片
使用新的 API,以编程方式放置瓦片非常简单:
# Fill a rectangular area
for x in range(20):
for y in range(10):
tilemap.set_cell(0, Vector2i(x, y), 0, Vector2i(0, 0))
# Random tile placement
for x in range(50):
for y in range(50):
if randf() > 0.7:
tilemap.set_cell(0, Vector2i(x, y), 0, Vector2i(1, 0))
else:
tilemap.set_cell(0, Vector2i(x, y), 0, Vector2i(0, 0))
# Get all used cells on a layer
var used_cells: Array[Vector2i] = tilemap.get_used_cells(0)
print("Layer 0 has ", used_cells.size(), " tiles")
# Clear all tiles on a layer
tilemap.clear_layer(0)
# Clear everything
tilemap.clear()
世界坐标与地图坐标的转换
# Convert world position to map coordinates
var map_pos: Vector2i = tilemap.local_to_map(world_position)
# Convert map coordinates to world position (center of tile)
var world_pos: Vector2 = tilemap.map_to_local(Vector2i(5, 3))
# Example: check what tile the player is standing on
var player_tile = tilemap.local_to_map(tilemap.to_local(player.global_position))
var tile_data = tilemap.get_cell_tile_data(0, player_tile)
if tile_data:
var tile_type = tile_data.get_custom_data("tile_type")
print("Player is on: ", tile_type)
从 Godot 3 迁移
| Godot 3 | Godot 4 |
|---|---|
TileMap.cell_size |
TileSet.tile_size |
set_cellv(pos, tile_id) |
set_cell(layer, pos, source_id, atlas_coords) |
get_cellv(pos) |
get_cell_source_id(layer, pos) |
autotile |
地形系统(Terrain Sets) |
| 每个 TileMap 节点仅一个图层 | 一个 TileMap 中可有多个图层 |
TileMap.tile_set = preload(...) |
在检查器中或通过代码分配 TileSet 资源 |
world_to_map() |
local_to_map() |
map_to_world() |
map_to_local() |
常见错误
-
忘记先创建 TileSet。 在你分配 TileSet 资源之前,TileMap 节点不会显示瓦片编辑器。请在检查器中创建一个。
-
source_id 错误。 如果你只有一个图集源,它的
source_id是0。再添加第二个源则其source_id = 1。查看 TileSet 编辑器底部面板 — 每个源标签页都会显示其 ID。 -
混淆 atlas_coords 与瓦片 ID。 在 Godot 4 中不再有简单的瓦片 ID。相反,你通过瓦片在图集网格中的位置以
Vector2i(列, 行)来引用它。 -
未指定图层参数。 每个单元格操作都要求以图层索引作为第一个参数。忘记它会导致参数错误。
-
使用
set_cellv()。 此方法已不再存在。请使用采用新签名的set_cell()。