2D Godot 2D 게임 기초
P5 구조와 데이터

20강타일맵과 레벨 제작

TileMapLayer, 오토타일, 충돌 타일.

이 강의 목차 (8)
  1. 왜 타일맵인가
  2. 1) 타일셋 만들기
  3. 2) 맵 그리기
  4. 3) 충돌 붙이기
  5. 4) 터레인(오토타일) — 모서리 자동 처리
  6. 여러 레이어로 나누기
  7. 코드에서 타일 다루기
  8. 실습 — 스테이지 한 판
이번 강의의 목표
  • 타일셋을 만들고 TileMapLayer로 맵을 그린다.
  • 타일에 충돌을 붙인다.
  • 오토타일(터레인)로 벽 모서리를 자동 처리한다.

왜 타일맵인가#

스테이지를 Sprite2D 수백 개로 만들면 이렇게 됩니다.

  • 씬 파일이 거대해지고 에디터가 느려짐
  • 벽 하나하나에 콜라이더를 붙여야 함
  • 드로우 콜이 수백 개 (6강)
  • 맵을 고치려면 노드를 일일이 옮겨야 함

타일맵은 이 전부를 해결합니다.

타일맵

격자를 정해두고, 각 칸에 "몇 번 타일" 인지 숫자만 기록하는 방식.

  • 데이터가 작음 (좌표 + 타일 번호)
  • 한 번에 그려짐 (같은 텍스처라 배칭이 완벽)
  • 충돌도 타일 단위로 자동 생성
  • 붓처럼 칠해서 편집
Godot 4.3에서 이름이 바뀌었다
  • Godot 4.0~4.2: TileMap 노드 하나가 여러 레이어를 가짐
  • Godot 4.3+: TileMapLayer 노드가 레이어 하나씩을 담당

새 프로젝트는 TileMapLayer를 쓰세요. 옛날 튜토리얼은 TileMap 기준이라 화면이 다릅니다. TileMap 노드를 선택하면 TileMapLayer들로 자동 변환하는 버튼이 나옵니다.

1) 타일셋 만들기#

타일셋(TileSet) 은 "쓸 수 있는 타일들의 목록"입니다.

  1. TileMapLayer 노드 추가
  2. 인스펙터의 Tile SetNew TileSet
  3. 하단 TileSet 패널이 열림
  4. 좌하단 +아틀라스 추가 → 타일 이미지(.png) 지정
  5. 타일 크기 지정 (예: 16×16, 32×32)
  6. "타일을 자동으로 만들까요?" → 예
타일 크기 정하기
크기 특징
8×8 아주 세밀. 파일이 많아짐
16×16 픽셀아트 표준
32×32 픽셀아트~일반
64×64 이상 고해상도 아트

한 번 정하면 바꾸기 매우 어렵습니다. 15강의 기준 해상도와 함께 초기에 결정하세요.

2) 맵 그리기#

TileMapLayer 노드를 선택하면 하단에 TileMap 패널이 나옵니다.

도구 단축키 하는 일
그리기 D 붓으로 칠하기
지우기 E 지우기
선택 S 영역 선택 후 복사
채우기 B 페인트 통
사각형 R 사각형 영역 채우기
스포이드 P 이미 놓인 타일 집기

Shift를 누르고 드래그하면 직선, Ctrl을 누르면 지우기로 전환됩니다.

3) 충돌 붙이기#

이게 타일맵의 진짜 힘입니다.

  1. TileSet 패널 → 좌측 물리 레이어(Physics Layers)+ 로 레이어 추가
  2. 레이어의 Collision Layerworld(1번)로 설정 (11강)
  3. 상단 탭을 선택(Select) 으로 바꾸고 타일 하나를 클릭
  4. 좌측 속성에서 Physics → Polygon 항목의 편집기를 열어 타일 위에 다각형을 그림
  5. 사각형 타일이면 "사각형 자동 생성" 버튼 한 번이면 끝
이제 벽이 자동으로 생긴다

타일을 칠하기만 하면 그 자리에 콜라이더가 자동으로 생깁니다. StaticBody2D를 하나도 안 만들었는데 캐릭터가 벽에 막힙니다.

여러 타일이 붙어 있으면 엔진이 하나의 큰 콜라이더로 합쳐줍니다. 성능상으로도 유리합니다.

확인하기

디버그 → 충돌 도형 표시를 켜고 실행하세요. 칠한 벽 타일에만 파란 윤곽이 보이면 성공입니다.

4) 터레인(오토타일) — 모서리 자동 처리#

벽을 그릴 때 가장 귀찮은 게 모서리와 가장자리입니다. 왼쪽 끝, 오른쪽 끝, 안쪽 모서리, 바깥 모서리… 종류가 십수 개입니다.

터레인(Terrain) 을 설정하면 주변 타일을 보고 알맞은 모양을 자동 선택합니다.

  1. TileSet 패널 → 터레인 세트(Terrain Sets)+
  2. 모드를 Match Corners and Sides 로 설정
  3. 터레인 하나 추가하고 이름을 으로
  4. 선택에서 각 타일을 고르고 Terrains → Terrain 항목에서 그 타일의 어느 부분이 땅인지 페인트로 칠함
  5. TileMap 패널의 Terrains에서 그리면 자동으로 모양이 결정됨
터레인 설정은 오래 걸린다. 하지만 한 번이다

타일 47종의 터레인 비트를 칠하는 데 20~30분쯤 걸립니다. 하지만 그 뒤로는 맵을 그리는 시간이 5배 빨라집니다. 스테이지가 2개 이상이면 반드시 이깁니다.

여러 레이어로 나누기#

한 화면에 여러 TileMapLayer를 겹칩니다.

text
World                 (Node2D)  Y Sort Enabled
├─ Background         (TileMapLayer)  z_index -10, 충돌 없음
├─ Ground             (TileMapLayer)  z_index  0,  충돌 있음
├─ Decoration         (TileMapLayer)  z_index  5,  충돌 없음
├─ Player
├─ Enemies
└─ Foreground         (TileMapLayer)  z_index 20, 캐릭터를 가림
레이어 용도
Background 하늘, 먼 배경
Ground 바닥과 벽 (충돌)
Decoration 풀, 돌, 균열
Foreground 캐릭터 앞을 가리는 나뭇잎, 지붕

코드에서 타일 다루기#

gdscript
extends TileMapLayer

func _ready() -> void:
	# 타일 좌표 (3, 5)에 아틀라스 (0,0) 타일 놓기
	set_cell(Vector2i(3, 5), 0, Vector2i(0, 0))

	# 지우기
	erase_cell(Vector2i(3, 5))

	# 그 칸에 뭐가 있는지
	var src := get_cell_source_id(Vector2i(3, 5))
	if src == -1:
		print("빈 칸")

# 픽셀 좌표 ↔ 타일 좌표 변환
func world_to_tile(p: Vector2) -> Vector2i:
	return local_to_map(to_local(p))

func tile_to_world(t: Vector2i) -> Vector2:
	return to_global(map_to_local(t))
좌표계가 두 개다
  • 타일 좌표 Vector2i(3, 5) — 격자 칸 번호
  • 월드 좌표 Vector2(96, 160) — 픽셀 위치

local_to_map() / map_to_local() 이 둘을 오갑니다. map_to_local()타일의 중심점을 돌려줍니다. 이걸 몰라서 반 칸씩 어긋나는 일이 흔합니다.

커스텀 데이터 레이어 — 타일에 정보 붙이기

"이 타일은 물이라 느려진다", "이 타일은 용암이라 데미지"를 표현하려면 커스텀 데이터 레이어를 씁니다.

  1. TileSet 패널 → 커스텀 데이터 레이어+
  2. 이름 speed_mul, 타입 float 로 추가
  3. 각 타일을 선택해 값 지정 (물 타일 = 0.5)
gdscript
func speed_multiplier_at(pos: Vector2) -> float:
	var cell := local_to_map(to_local(pos))
	var d := get_cell_tile_data(cell)
	if d == null:
		return 1.0
	return d.get_custom_data("speed_mul")

이렇게 하면 타일 하나하나가 게임 규칙을 가집니다. 19강의 데이터 주도 설계가 타일에도 적용되는 셈입니다.

실습 — 스테이지 한 판#

이번 강의의 핵심 실습
  1. 16×16 타일셋을 만들고 바닥·벽 타일에 충돌을 붙입니다.
  2. TileMapLayer 레이어를 3개(Background / Ground / Decoration) 만듭니다.
  3. 가로 60칸, 세로 34칸 정도의 방을 하나 그립니다.
  4. 9강의 플레이어와 19강의 적을 배치합니다.
  5. 14강의 카메라 리미트를 맵 크기에 맞춰 설정합니다.
gdscript
# 타일맵 크기에서 카메라 리미트를 자동 계산
func fit_camera_to_map(map: TileMapLayer, cam: Camera2D) -> void:
	var used := map.get_used_rect()
	var cell := Vector2(map.tile_set.tile_size)
	var top_left := map.to_global(
		map.map_to_local(used.position) - cell * 0.5)
	var bottom_right := map.to_global(
		map.map_to_local(used.end) - cell * 0.5)
	cam.limit_left = int(top_left.x)
	cam.limit_top = int(top_left.y)
	cam.limit_right = int(bottom_right.x)
	cam.limit_bottom = int(bottom_right.y)
추가 실험
  1. 터레인을 설정하고 벽을 다시 그려보세요. 모서리가 자동으로 맞습니까?
  2. Y Sort를 켜고 나무 타일 앞뒤로 걸어보세요 (6강).
  3. 커스텀 데이터로 "물" 타일을 만들어 이동 속도를 절반으로 만들어보세요.
  4. Foreground 레이어에 지붕을 그리고 그 아래로 들어가 보세요.
  • 개발자 — 맵 데이터를 코드로 생성해야 한다면(랜덤 던전) set_cell() 을 반복문으로 씁니다. 대량 수정 시에는 TileMapLayer를 여러 번 갱신하지 말고 한 번에 처리하세요.
  • 기획자 — 레벨 디자인은 직접 그리는 게 가장 빠릅니다. 타일 팔레트만 준비되면 개발자 없이도 스테이지를 만들 수 있습니다. "이 방은 어렵게" 같은 요청보다 직접 그린 맵 하나가 소통 비용이 훨씬 적습니다.
  • 디자이너 — 타일셋 제작 규칙을 초기에 정하세요. 타일 크기 / 팔레트 / 이음새 규칙 / 터레인 대응 여부. 특히 타일 사이에 1px 여백(padding) 을 두면 확대 시 인접 타일 색이 새어 나오는 현상(bleeding)을 막을 수 있습니다.
자주 나는 오류

"타일을 칠했는데 충돌이 없다" → ① 물리 레이어를 추가 안 함, ② 개별 타일에 폴리곤을 안 그림, ③ Collision Layer 번호가 플레이어 마스크와 안 맞음(11강).

"타일 사이에 얇은 선이 보인다" → 텍스처 블리딩입니다. 프로젝트 설정 → Rendering → Textures → Canvas Textures → Default Texture FilterNearest 로, 그리고 타일셋 이미지에 여백을 두거나 TileSet의 Separation 값을 설정하세요.

"map_to_local 결과가 반 칸 어긋난다"map_to_local()은 타일 중심을 줍니다. 좌상단이 필요하면 타일 크기의 절반을 빼세요.

"터레인으로 그렸는데 이상한 타일이 나온다" → 터레인 비트 칠하기가 빠진 타일이 있습니다. 각 타일의 모든 모서리/변을 확인하세요.

여기까지 확인
  • 타일셋을 만들고 타일을 칠했다
  • 타일에 충돌이 붙어 캐릭터가 막힌다
  • 레이어를 나눠 배경/지형/장식을 분리했다
  • 타일 좌표와 월드 좌표를 변환할 수 있다
  • 카메라 리미트를 맵에 맞췄다

다음 강의 — 스테이지가 생겼습니다. 이제 타이틀 → 게임 → 결과 로 이어지는 게임 전체 흐름과 저장을 만듭니다. P5의 마지막입니다.