Godot タイルマップ入門:TileMapLayer、TileSet、衝突、Codex による補助開発

Godot 4.x 初心者向けのタイルマップ入門です。TileSet、TileMapLayer、レイヤー分け、タイル衝突、Terrain の自動接続、Codex によるマップ操作コードの支援を整理します。

Godot のタイルマップは、草地、道路、壁、水面などの小さな画像を含む素材シートを規則的なグリッドに分割し、それを絵を描くように並べてステージを作る仕組みです。

Godot 4.x、特に 4.3 以降の新規プロジェクトでは TileMapLayer を使うのがおすすめです。古い TileMap ノードは非推奨になっています。現在は、1 つのマップレイヤーに 1 つの 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

まず理解したい 2 つの概念

タイルマップで重要なのは次の 2 つです。

  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

素材の 1 タイルが 32 × 32 なら、TileSetTile Size32 × 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.pngTileSet パネルにドラッグし、自動タイル作成を選びます。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
Debug
→ Visible Collision Shapes

壁とプレイヤーの衝突形状を直接確認できるので、デバッグがかなり楽になります。

マップは 3 レイヤーに分ける

初心者のうちは、次の 3 レイヤーがおすすめです。

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. TileSetTerrain Set を追加する。
  2. 辺や角で一致させるなど、マッチングモードを設定する。
  3. GrassDirtWater などの Terrain を作る。
  4. 各タイルに地形接続位置を設定する。
  5. TileMapLayerTerrains 描画モードに切り替える。
  6. Connect または Path ツールでマップを描く。

Godot は周囲のセルを見て、直線、角、端のタイルを自動選択します。公式ドキュメントでも Terrain の描画モードとして ConnectPath が説明されています。Connect は始めやすく、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 には実際のノード構造に沿ってマップ操作、生成ロジック、ゲームプレイスクリプトを書かせることです。

最初の練習

最初の練習では、20 × 15 の小さなマップだけを作るのがおすすめです。

  1. Ground に草地を敷く。
  2. Walls に壁を一周描く。
  3. 壁タイルに衝突を設定する。
  4. Player を配置する。
  5. プレイヤーが壁に遮られるようにする。
  6. その後で Coin シーンを追加する。

この練習が終わると、Godot のタイルマップの基本的な流れが理解できます。

1
2
3
4
5
TileSet はタイル素材とルールを持つ
TileMapLayer は 1 つのマップレイヤーを描画する
衝突は TileSet 内のタイルに設定する
振る舞いを持つオブジェクトは独立シーンにする
Codex は実際のノード構造に沿ってスクリプトを書く

まずはこの最小マップを動かしましょう。その後で Terrain、Y Sort、プロシージャル生成、大規模マップ読み込みに進む方が安定します。

记录并分享
Hugo で構築されています。
テーマ StackJimmy によって設計されています。