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

14강카메라2D

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

이 강의의 Godot 예제 프로젝트
14강 예제 내려받기 godot-2d-lecture-14-camera.zip · 5.9 KB

압축을 풀고 Godot 4.3 이상에서 가져오기(Import) → project.godot 선택 → F5. 씬 1개 · 스크립트 2개 · 안내는 폴더 안 README.md 에 있습니다.

이 강의 목차 (9)
  1. 카메라는 "무엇을 보여줄지"를 정하는 연출 장치다
  2. 가장 단순한 형태
  3. 스무딩 — 부드럽게 따라가기
  4. 리미트 — 화면 밖을 안 보여주기
  5. 데드존 — 조금 움직여도 안 따라가기
  6. 줌
  7. 화면 흔들림 (Screen Shake)
  8. 여러 대상을 한 화면에 담기
  9. 실습 — 카메라 성격 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으로 화면 흔들림을 만들었다

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