TileMapLayer (Godot 4.3+)
Godot 4.3でTileMapLayerノードが追加され、TileMapは非推奨になりました。各TileMapLayerは1つのレイヤーを表すため、地面、壁、装飾からなるレベルは、1つのTileSetを共有する3つの兄弟TileMapLayerノードで構成されます。新しいコードをTileMapに対して書かず、Godot 3のAPI(set_cellv、world_to_map、set_cell(x, y, tile_id))も決して使用しないでください。
セルAPI
セルは整数のマップ座標(Vector2i)で識別されます。タイルは次の3つの値で識別されます。
source_id:TileSet内のソース(アトラステクスチャまたはシーンコレクション)のID。最初のアトラスは通常0です。TileSetエディタまたはTileSet.add_source()の戻り値を確認してください。atlas_coords: アトラス内でのタイルの列と行(ピクセルではなくタイル単位)。alternative_tile: 反転または回転した代替タイルを作成していない限り0。
extends TileMapLayer
const SOURCE_ID := 0
const GRASS := Vector2i(0, 0)
const WALL := Vector2i(1, 0)
func _ready() -> void:
for x in range(10):
set_cell(Vector2i(x, 5), SOURCE_ID, GRASS)
set_cell(Vector2i(3, 4), SOURCE_ID, WALL)
var source_id := get_cell_source_id(Vector2i(3, 4)) # セルが空の場合は-1
var atlas := get_cell_atlas_coords(Vector2i(3, 4)) # 空の場合はVector2i(-1, -1)
print(source_id, atlas)
erase_cell(Vector2i(3, 4))
print(get_used_cells().size(), " cells, bounds ", get_used_rect())
func cell_at_global(global_point: Vector2) -> Vector2i:
return local_to_map(to_local(global_point))
func center_of_cell(coords: Vector2i) -> Vector2:
# map_to_localはこのレイヤーのローカル空間におけるセル中心を返します。
return to_global(map_to_local(coords))
重要な事実:
- デフォルトの引数で
set_cell(coords)を呼び出すと、セルが消去されます(source_idのデフォルトは-1)。タイルを配置するときは、必ずsource_idとatlas_coordsを渡してください。 - アトラスに存在しない
atlas_coordsを設定しても、セルはエラーなく保存されます(get_cell_source_id()はソースを返します)。後でget_cell_tile_data()を呼び出すと、The TileSetAtlasSource atlas has no tile at (x, y).というログが出力され、nullを返します。 local_to_map()とmap_to_local()はレイヤーのローカル空間で動作します。レイヤーが移動または拡大縮小されている場合は、to_local()/to_global()で変換してください。get_used_cells_by_id(source_id, atlas_coords)は、1つのタイルを使用しているすべてのセルを検索します。- 変更はフレームの終わりにバッチ処理されます。衝突やシーンタイルをすぐに更新する必要がある場合は、
update_internals()を呼び出してください。
タイルごとのカスタムデータ
TileSetでカスタムデータレイヤーを追加し(インスペクター > TileSet > Custom Data Layers、例: int型のdamage)、TileSetエディタで値をペイントしてから、コードで読み取ります:
extends TileMapLayer
func damage_at(coords: Vector2i) -> int:
var data := get_cell_tile_data(coords)
if data == null:
return 0
return data.get_custom_data("damage")
get_cell_tile_data()は、空のセルやシーンコレクションタイルに対してはnullを返すため、メソッドを呼び出す前にチェックしてください。
地形(オートタイル)
Godot 3のオートタイルビットマスクは地形セットになりました。TileSetで地形セットと地形を追加し、TileSetエディタで各タイルの地形接続ビットをペイントします。コードでは、塗りつぶしたいすべてのセルを渡します:
extends TileMapLayer
const TERRAIN_SET := 0
const DIRT := 0
func paint_room(rect: Rect2i) -> void:
var cells: Array[Vector2i] = []
for x in range(rect.position.x, rect.end.x):
for y in range(rect.position.y, rect.end.y):
cells.append(Vector2i(x, y))
set_cells_terrain_connect(cells, TERRAIN_SET, DIRT)
set_cells_terrain_connect()はエッジが接続されるようにタイルを選びます。set_cells_terrain_path()はパスとして描かれる道や川のためのものです。結果に間違ったタイルや欠落したタイルが表示される場合、TileSetの接続ビットが不完全です。コードで修正することはできません。
衝突
タイルが衝突するのは、TileSetに物理レイヤーがあり(TileSetインスペクター > Physics Layers > Add Element)、そのレイヤーにタイルの衝突ポリゴンが設定されている場合のみです(TileSetエディタでタイルを選択し、Physics > Physics Layer 0; タイル全体の長方形にするにはFキーを押します)。レイヤーのcollision_enabledプロパティはデフォルトでオンです。物理レイヤーにはcollision_layerとcollision_maskもあり、これらはノードではなくTileSetで設定します。
CharacterBody2Dがどのタイルに接触したかを調べるには、衝突点を使用します。get_coords_for_body_rid()はセルの座標ではなく物理クアドラントの座標を返します(physics_quadrant_sizeが1の場合を除く。Godot 4.5+ではデフォルトで最大16x16セルが1つのボディにマージされます):
extends CharacterBody2D
@export var level: TileMapLayer
func _physics_process(delta: float) -> void:
velocity += get_gravity() * delta
move_and_slide()
for i in get_slide_collision_count():
var collision := get_slide_collision(i)
if collision.get_collider() == level:
# 接触点はタイルのエッジ上にあるため、少しタイルの内側に進む。
var inside := collision.get_position() - collision.get_normal()
var coords := level.local_to_map(level.to_local(inside))
print("Touching tile ", coords)
コードでのTileSet構築
手続き的レベルや、TileSetエディタを使用できないテストに便利です。衝突ポリゴンの点はタイル中心を基準とします。
extends Node2D
func _ready() -> void:
var tile_set := TileSet.new()
tile_set.tile_size = Vector2i(16, 16)
tile_set.add_physics_layer()
var source := TileSetAtlasSource.new()
source.texture = load("res://tiles.png") as Texture2D
source.texture_region_size = Vector2i(16, 16)
source.create_tile(Vector2i(0, 0))
var source_id := tile_set.add_source(source)
var data := source.get_tile_data(Vector2i(0, 0), 0)
data.add_collision_polygon(0)
data.set_collision_polygon_points(0, 0, PackedVector2Array([
Vector2(-8, -8), Vector2(8, -8), Vector2(8, 8), Vector2(-8, 8),
]))
var layer := TileMapLayer.new()
layer.tile_set = tile_set
add_child(layer)
layer.set_cell(Vector2i(0, 0), source_id, Vector2i(0, 0))
TileMapからの移行
エディタで: TileMapを選択し、TileMapボトムパネルを開き、ツールボックスアイコンをクリックして「Extract TileMap layers as individual TileMapLayer nodes」を選択します。その後、スクリプトを更新します。layerインデックスを受け取っていたすべてのTileMapメソッドは、そのレイヤーのノード上のメソッド(インデックスなし)になります。
| 旧コード | TileMapLayer |
|---|---|
tile_map.set_cell(0, coords, source_id, atlas_coords) | ground.set_cell(coords, source_id, atlas_coords) |
tile_map.get_cell_source_id(1, coords) | walls.get_cell_source_id(coords) |
tile_map.get_used_cells(0) | ground.get_used_cells() |
tile_map.set_layer_enabled(2, false) | decoration.enabled = false |
tile_map.set_layer_z_index(1, 5) | walls.z_index = 5 |
Godot 3 set_cellv(pos, id), world_to_map(p), map_to_world(c) | set_cell(coords, source_id, atlas_coords), local_to_map(p), map_to_local(c) |
よくある間違い
set_cell()にピクセル位置を渡す。最初にlocal_to_map()で変換してください。- 1つの数値で
set_cell(coords, tile_index)を呼び出す: 2番目の引数はソースIDであり、atlas_coordsのデフォルトはVector2i(-1, -1)のため、セルが消去されます。 - 物理レイヤーのないTileSet、またはポリゴンがペイントされていないタイルからの衝突を期待する。
- デフォルトの
physics_quadrant_sizeでget_coords_for_body_rid()を使用し、クアドラント座標を取得してしまう。 nullチェックなしでget_cell_tile_data()のメソッドを呼び出す。- 古いチュートリアルに従って新しい作業に
TileMapノードを追加する。レイヤーごとに1つのTileMapLayerを使用してください。