TileMapLayer (Godot 4.3+)
Godot 4.3에서 TileMapLayer 노드가 추가되었으며 TileMap은 더 이상 사용되지 않습니다(Deprecated). 각 TileMapLayer는 하나의 레이어이므로, 바닥, 벽, 장식이 있는 레벨은 하나의 TileSet을 공유하는 세 개의 형제 TileMapLayer 노드로 구성됩니다. TileMap에 대해 새 코드를 작성하지 말고, Godot 3 API(set_cellv, world_to_map, set_cell(x, y, tile_id))를 절대 사용하지 마세요.
셀 API
셀은 정수 맵 좌표(Vector2i)로 식별됩니다. 타일은 세 가지 값으로 식별됩니다.
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)는 하나의 타일을 사용하는 모든 셀을 찾습니다.- 변경 사항은 프레임 끝까지 일괄 처리됩니다. 충돌 또는 씬 타일을 즉시 업데이트해야 하는 경우
update_internals()를 호출하세요.
타일별 사용자 정의 데이터
TileSet에 사용자 정의 데이터 레이어를 추가하고(Inspector > 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 셀을 하나의 바디로 병합) 셀의 좌표가 아닌 물리 사분면의 좌표를 반환합니다:
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()으로 변환하세요. - 하나의 숫자(
tile_index)로set_cell(coords, tile_index)을 호출하는 것: 두 번째 인수는 소스 ID이며atlas_coords는 기본값이Vector2i(-1, -1)이므로 셀이 지워집니다. - 물리 레이어가 없거나 칠해진 다각형이 없는 타일에서 충돌이 발생할 것으로 예상하는 것.
- 기본
physics_quadrant_size에서get_coords_for_body_rid()를 사용하여 사분면 좌표를 얻는 것. null확인 없이get_cell_tile_data()의 메서드를 호출하는 것.- 오래된 튜토리얼 때문에 새 작업에
TileMap노드를 추가하는 것. 레이어당 하나의TileMapLayer를 사용하세요.