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 設計