2D Godot 2D 게임 기초
P4 화면 연출

14강카메라2D

따라가기, 스무딩, 리미트, 데드존.

이 강의 목차 (9)
  1. 카메라는 "무엇을 보여줄지"를 정하는 연출 장치다
  2. 가장 단순한 형태
  3. 스무딩 — 부드럽게 따라가기
  4. 리미트 — 화면 밖을 안 보여주기
  5. 데드존 — 조금 움직여도 안 따라가기
  6. 화면 흔들림 (Screen Shake)
  7. 여러 대상을 한 화면에 담기
  8. 실습 — 카메라 성격 3종 비교
이번 강의의 목표
  • Camera2D로 캐릭터를 따라가게 만든다.
  • 스무딩·리미트·데드존의 차이를 안다.
  • 카메라가 게임 느낌을 얼마나 바꾸는지 체감한다.

카메라는 "무엇을 보여줄지"를 정하는 연출 장치다#

지금까지 화면은 고정되어 있었습니다. 캐릭터가 화면 밖으로 나가면 안 보였죠. Camera2D 하나를 붙이면 화면이 캐릭터를 따라옵니다.

그런데 그냥 따라가기만 하면 멀미가 납니다. 카메라는 생각보다 섬세한 장치입니다.

촬영감독

카메라는 배우가 아니라 촬영감독입니다. 초보 감독은 배우를 정확히 화면 중앙에 놓고 딱 붙어 따라다닙니다. 보는 사람은 어지럽습니다.

좋은 감독은

  • 배우가 조금 움직여도 바로 안 따라갑니다 (데드존)
  • 따라갈 때도 부드럽게 밀립니다 (스무딩)
  • 배우가 가는 방향을 미리 조금 보여줍니다 (룩어헤드)
  • 세트장 밖은 안 보여줍니다 (리미트)

가장 단순한 형태#

Player 아래에 Camera2D를 자식으로 붙이기만 하면 끝입니다.

text
Player       (CharacterBody2D)
├─ Sprite2D
├─ CollisionShape2D
└─ Camera2D

부모를 따라가는 5강의 규칙 덕분에 코드 한 줄 없이 카메라가 캐릭터를 따라옵니다.

자식으로 붙이는 방식의 한계

이 방식은 카메라가 캐릭터에 완전히 고정됩니다. 그래서 나중에 이런 걸 못 합니다.

  • 컷신에서 다른 곳을 비추기
  • 두 캐릭터의 중간을 비추기
  • 화면 흔들림을 캐릭터와 분리하기

권장 — 카메라를 씬 루트 아래 독립 노드로 두고 코드로 따라가게 만드세요.

스무딩 — 부드럽게 따라가기#

Camera2D 인스펙터에서:

항목 의미
Position Smoothing → Enabled 스무딩 켜기
Position Smoothing → Speed 따라붙는 속도. 작을수록 느긋함
Rotation Smoothing 회전도 부드럽게
스무딩 값 실험
Speed 느낌
0 (끔) 캐릭터에 딱 붙음. 픽셀 단위로 정확하지만 딱딱하고 멀미가 남
2 아주 느긋함. 액션에는 답답, 탐험 게임에는 좋음
5 대부분의 게임에 무난한 기본값
15 거의 붙어 있지만 급정지 때만 살짝 밀림

직접 5분만 바꿔가며 움직여 보세요. 숫자 하나로 게임 장르가 바뀌는 느낌이 납니다.

리미트 — 화면 밖을 안 보여주기#

스테이지 가장자리에 가면 카메라가 더 나가지 않게 막습니다.

항목
Limit → Left / Top / Right / Bottom 픽셀 좌표
Limit → Smoothed 경계에 닿을 때 부드럽게
gdscript
# 코드로 스테이지 크기에 맞춰 설정하기
func setup_limits(rect: Rect2) -> void:
	limit_left = int(rect.position.x)
	limit_top = int(rect.position.y)
	limit_right = int(rect.end.x)
	limit_bottom = int(rect.end.y)
리미트를 안 걸면 스테이지 밖의 빈 공간이 보인다

플레이어가 맵 구석에 서면 화면의 절반이 아무것도 없는 배경이 됩니다. "게임이 미완성처럼 보이는" 가장 흔한 원인입니다. 스테이지마다 반드시 설정하세요.

데드존 — 조금 움직여도 안 따라가기#

캐릭터가 화면 중앙의 작은 사각형 안에 있는 동안은 카메라가 가만히 있고, 그 밖으로 나가면 그때부터 따라가는 방식입니다.

플랫포머에서 특히 중요합니다. 점프할 때마다 화면이 위아래로 출렁이면 멀미가 납니다. 데드존을 세로로 넉넉히 잡으면 점프해도 화면이 안 움직입니다.

Godot의 Camera2D에는 데드존이 내장돼 있지 않아 직접 만듭니다.

gdscript
extends Camera2D

@export var target_path: NodePath
@export var dead_zone := Vector2(60, 90)
@export var follow_speed := 6.0
@export var look_ahead := 40.0

var _target: Node2D
var _desired: Vector2

func _ready() -> void:
	_target = get_node_or_null(target_path)
	if _target:
		_desired = _target.global_position
		global_position = _desired

func _process(delta: float) -> void:
	if _target == null:
		return

	var t := _target.global_position

	# ── 데드존: 상자를 벗어난 만큼만 목표를 민다 ──
	var d := t - _desired
	if absf(d.x) > dead_zone.x:
		_desired.x += d.x - signf(d.x) * dead_zone.x
	if absf(d.y) > dead_zone.y:
		_desired.y += d.y - signf(d.y) * dead_zone.y

	# ── 룩어헤드: 가는 방향을 조금 미리 보여준다 ──
	var vel: Vector2 = _target.get("velocity")
	if vel != null and vel.length() > 10.0:
		_desired += vel.normalized() * look_ahead * delta * 4.0

	# ── 스무딩: 프레임에 안전한 형태(4강) ──
	var t_amount := 1.0 - exp(-follow_speed * delta)
	global_position = global_position.lerp(_desired, t_amount)
왜 카메라는 _process인가

카메라는 게임 규칙이 아니라 보기입니다(8강 기준). 그리고 렌더 프레임마다 갱신해야 부드럽습니다.

_physics_process에 넣으면 144Hz 모니터에서 카메라가 60번만 갱신되어 떨려 보입니다.

#

gdscript
zoom = Vector2(2, 2)     # 2배 확대 (더 좁게 보임)
zoom = Vector2(0.5, 0.5) # 축소 (더 넓게 보임)
zoom의 방향이 헷갈린다

Godot 4에서 zoom 값이 클수록 확대됩니다(가까이 봄). Godot 3에서는 반대였습니다. 옛날 자료를 볼 때 주의하세요.

부드러운 줌은 Tween으로 만듭니다.

gdscript
func zoom_to(value: float, time := 0.4) -> void:
	var tw := create_tween()
	tw.set_ease(Tween.EASE_OUT)
	tw.set_trans(Tween.TRANS_CUBIC)
	tw.tween_property(self, "zoom",
		Vector2(value, value), time)

화면 흔들림 (Screen Shake)#

17강에서 본격적으로 다루지만, 카메라 기능이므로 여기서 기본형을 만듭니다.

gdscript
var _shake := 0.0
var _shake_decay := 5.0

func shake(amount: float) -> void:
	_shake = maxf(_shake, amount)

func _process(delta: float) -> void:
	# ... 위의 따라가기 코드 ...

	if _shake > 0.0:
		_shake = maxf(_shake - _shake_decay * delta, 0.0)
		offset = Vector2(
			randf_range(-1, 1), randf_range(-1, 1)
		) * _shake
	else:
		offset = Vector2.ZERO
offset을 쓰는 이유

global_position을 흔들면 따라가기 계산과 싸웁니다. offset은 카메라 위치와 별개로 화면만 밀어주는 값이라 안전합니다.

"기본 위치는 로직이, 흔들림은 offset이" — 역할을 나누는 게 핵심입니다.

여러 대상을 한 화면에 담기#

협동 게임이나 보스전에서 자주 필요합니다.

gdscript
func _fit_targets(targets: Array[Node2D]) -> void:
	if targets.is_empty():
		return

	var r := Rect2(targets[0].global_position, Vector2.ZERO)
	for t in targets:
		r = r.expand(t.global_position)

	# 중심으로 이동
	_desired = r.get_center()

	# 다 담기도록 줌 조절
	var view := get_viewport_rect().size
	var pad := 240.0
	var need := (r.size + Vector2(pad, pad)) / view
	var z := 1.0 / maxf(maxf(need.x, need.y), 0.001)
	zoom = zoom.lerp(Vector2(z, z), 0.06)
카메라가 여러 개일 때

씬에 Camera2D가 여러 개 있으면 enabled가 켜진 것 중 마지막이 쓰입니다. 명시적으로 바꾸려면 make_current() 를 부릅니다.

gdscript
$CutsceneCamera.make_current()
await ...
$PlayerCamera.make_current()

컷신, 미니맵, 보스 등장 연출에서 카메라를 갈아끼우는 방식이 자주 쓰입니다.

실습 — 카메라 성격 3종 비교#

같은 스테이지를 세 세팅으로 돌아보세요.

액션 탐험 퍼즐
follow_speed 12 3 999 (즉시)
dead_zone (20, 40) (100, 140) (0, 0)
look_ahead 60 20 0
zoom 1.2 0.8 1.0
직접 바꿔보기
  1. 세 세팅으로 각각 1분씩 플레이해 보세요. 같은 게임이 다른 게임처럼 느껴집니까?
  2. dead_zone.y를 0으로 만들고 점프해 보세요. 화면이 출렁여서 어지럽습니까?
  3. 리미트를 설정하지 않고 맵 구석으로 가보세요. 무엇이 보입니까?
  4. look_ahead를 200으로 극단적으로 올려보세요. 어떤 문제가 생깁니까? (방향을 바꿀 때마다 화면이 홱 뒤집힙니다 — 그래서 룩어헤드도 부드럽게 보간해야 합니다)
  • 개발자 — 카메라는 씬 루트 아래 독립 노드 + 코드로 따라가기로 만드세요. 나중에 컷신·보스전·화면 흔들림을 붙일 때 구조를 안 갈아엎습니다.
  • 기획자 — "화면이 어지럽다"는 피드백은 대개 데드존 부재 + 스무딩 부족입니다. 카메라 스펙에 따라가기 속도 / 데드존 크기 / 룩어헤드 / 기본 줌 네 항목을 넣어주세요.
  • 디자이너 — 카메라 줌 값은 아트 리소스 해상도를 결정합니다. 줌 1.5로 확대해서 보여줄 거라면 그림도 1.5배 해상도로 그려야 뭉개지지 않습니다. 이건 15강에서 이어집니다.
자주 나는 오류

"카메라가 아무 효과가 없다"Enabled 체크 확인. 또는 다른 Camera2D가 현재 카메라를 차지하고 있습니다.

"UI가 카메라를 따라 움직인다" → UI를 CanvasLayer에 넣으세요 (6강).

"카메라가 미세하게 떤다" → ① _physics_process에서 갱신 중 (→ _process로), ② 픽셀아트에서 소수점 위치 문제 (→ 아래 참고).

"픽셀아트가 카메라 이동 중에 흔들린다" → 카메라 위치가 소수점이라 픽셀이 반쯤 걸칩니다. 프로젝트 설정 → Rendering → 2D → Snap 2D Transforms to Pixel 을 켜거나, 카메라 위치를 round() 하세요. 15강에서 이어집니다.

여기까지 확인
  • 카메라를 독립 노드로 두고 코드로 따라가게 만들었다
  • 스무딩 값을 바꿔가며 느낌을 비교했다
  • 리미트를 걸어 스테이지 밖이 안 보이게 했다
  • 데드존이 왜 필요한지 안다
  • offset으로 화면 흔들림을 만들었다

다음 강의 — 카메라가 정해졌으니 화면 크기를 정할 차례입니다. 폴드의 커버 화면과 메인 화면처럼 비율이 완전히 다른 기기에 어떻게 대응할까요?