引言

Godot 4 彻底重写了 TileMap 系统。如果你在 Godot 3 中用过 TileMap,那么几乎一切都变了 — TileSet 编辑器、瓦片的定义方式、自动瓦片、图层以及 GDScript API。本指南将从零开始讲解这套新架构。

Godot 3 用户: 旧的 autotileset_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 资源

  1. 向场景中添加一个 TileMap 节点。
  2. 在检查器中点击 Tile Set 属性并选择 New TileSet
  3. 设置 Tile Size(例如 16x16 或 32x32)。

步骤 2:添加图集源

  1. 点击 TileSet,在底部面板打开 TileSet 编辑器。
  2. 点击 + 按钮并选择 Atlas
  3. 将你的瓦片集图像拖入 Texture 属性。
  4. 编辑器会根据你的瓦片尺寸自动将其切分为瓦片。你可以调整纹理区域和边距。

步骤 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。它会根据相邻瓦片自动选择正确的瓦片变体。

设置地形

  1. 在 TileSet 中添加一个 Terrain Set(检查器 → Terrain Sets → Add Element)。
  2. 选择地形模式:Match Corners and Sides(47 个瓦片)、Match Corners(16 个瓦片)或 Match Sides(16 个瓦片)。
  3. 在该集合内添加地形类型(例如「Grass」「Dirt」「Water」)。
  4. 在 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

瓦片上的物理

要为瓦片添加碰撞:

  1. 在 TileSet 检查器中添加一个 Physics Layer
  2. 配置此物理层所使用的碰撞层和掩码。
  3. 在 TileSet 编辑器中切换到 Physics 标签页,为每个瓦片绘制碰撞多边形。

提示: 你可以为不同的瓦片类型设置多个物理层。例如,一层用于墙壁(阻挡移动),另一层用于危险区(通过 Area2D 重叠触发伤害)。

瓦片上的自定义数据

自定义数据层让你能够为任意瓦片附加带类型的元数据。这对游戏逻辑极为有用。

设置自定义数据

  1. 在 TileSet 检查器中添加一个 Custom Data Layer
  2. 为其命名(例如「is_destructible」)并设置类型(例如 bool)。
  3. 在 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: bool
  • damage: int
  • movement_cost: float
  • tile_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()

常见错误

  1. 忘记先创建 TileSet。 在你分配 TileSet 资源之前,TileMap 节点不会显示瓦片编辑器。请在检查器中创建一个。

  2. source_id 错误。 如果你只有一个图集源,它的 source_id0。再添加第二个源则其 source_id = 1。查看 TileSet 编辑器底部面板 — 每个源标签页都会显示其 ID。

  3. 混淆 atlas_coords 与瓦片 ID。 在 Godot 4 中不再有简单的瓦片 ID。相反,你通过瓦片在图集网格中的位置以 Vector2i(列, 行) 来引用它。

  4. 未指定图层参数。 每个单元格操作都要求以图层索引作为第一个参数。忘记它会导致参数错误。

  5. 使用 set_cellv() 此方法已不再存在。请使用采用新签名的 set_cell()