델타 타임과 게임 루프 — 게임은 어떻게 "시간"을 셀까?
델타 타임과 게임 루프 — 게임은 어떻게 "시간"을 셀까?
게임 화면 속 캐릭터는 계속 움직입니다. 그런데 그 움직임은 저절로 일어나는 게 아니에요. 게임은 아주 빠른 속도로 같은 일을 반복하고 있습니다. 이 반복을 게임 루프라고 불러요.
1. 게임은 사실 "그림책 넘기기"입니다
공책 구석에 그림을 조금씩 다르게 그려서 촤르륵 넘기면 움직이는 것처럼 보이죠. 이걸 플립북이라고 해요. 게임도 똑같습니다.
게임은 1초에 수십 번 화면을 새로 그립니다. 이 그림 한 장을 프레임이라고 불러요. 1초에 60장을 그리면 "60프레임"입니다.
게임 루프는 이 세 가지를 계속 반복합니다.
- 입력 받기 — 사람이 어떤 키를 눌렀나?
- 계산하기 — 캐릭터를 얼마나 움직일까?
- 그리기 — 화면에 그림 한 장 뿌리기
이걸 게임이 켜져 있는 동안 끝없이 돌립니다. 그래서 "루프(고리)"예요.
2. 문제가 생겼어요: 컴퓨터마다 속도가 다르다
여기서 큰 문제가 하나 있습니다. 세상 모든 컴퓨터가 똑같이 빠르지는 않아요.
- 좋은 컴퓨터: 1초에 120번 루프를 돎
- 보통 컴퓨터: 1초에 60번 루프를 돎
- 느린 컴퓨터: 1초에 30번 루프를 돎
만약 게임을 이렇게 만들었다고 해봅시다.
"루프를 한 번 돌 때마다 캐릭터를 앞으로 1칸 옮겨라."
결과가 어떻게 될까요? 1초 뒤에…
- 좋은 컴퓨터에서는 캐릭터가 120칸 갑니다.
- 느린 컴퓨터에서는 캐릭터가 30칸밖에 못 갑니다.
같은 게임인데 비싼 컴퓨터에서는 캐릭터가 총알처럼 날아가고, 낡은 컴퓨터에서는 거북이가 됩니다. 이건 완전히 망가진 게임이에요.
3. 해결사 등장: 델타 타임
델타 타임(Delta Time)은 아주 간단한 뜻입니다.
"지난번 그림을 그린 뒤로 흘러간 시간"
'델타'는 "차이", "달라진 만큼"이라는 뜻이에요. 시계로 재는 시간의 차이라서 델타 타임입니다.
60프레임으로 도는 컴퓨터라면 델타 타임은 대략 0.016초입니다. (1초 ÷ 60 ≈ 0.016)
30프레임이라면 0.033초고요. (1초 ÷ 30 ≈ 0.033)
느린 컴퓨터는 루프를 적게 도는 대신, 한 번 돌 때 흘러간 시간이 더 깁니다. 이게 핵심이에요.
4. 마법의 공식: 시간을 곱하기
이제 게임을 이렇게 고칩니다.
"루프를 한 번 돌 때마다 캐릭터를 (속도 × 델타 타임) 만큼 옮겨라."
속도를 초당 100칸이라고 정해봅시다. 계산해볼까요?
| 컴퓨터 | 1초에 도는 횟수 | 델타 타임 | 한 번에 가는 거리 | 1초 뒤 총 거리 |
|---|---|---|---|---|
| 빠름 | 100번 | 0.01초 | 100 × 0.01 = 1칸 | 100칸 |
| 보통 | 50번 | 0.02초 | 100 × 0.02 = 2칸 | 100칸 |
| 느림 | 25번 | 0.04초 | 100 × 0.04 = 4칸 | 100칸 |
놀랍죠? 세 컴퓨터 모두 1초에 정확히 100칸을 갑니다.
느린 컴퓨터는 드문드문 크게 움직이고, 빠른 컴퓨터는 촘촘하게 조금씩 움직여요. 그래서 빠른 컴퓨터가 더 부드러워 보입니다. 하지만 도착하는 지점은 똑같습니다.
5. 걸음 비유로 다시 이해하기
친구 셋이 100m 앞 나무까지 1초 만에 도착해야 한다고 해봐요.
- 다리가 빠른 친구: 잰걸음으로 100번, 한 걸음 1m씩
- 보통 친구: 50번, 한 걸음 2m씩
- 느린 친구: 25번, 한 걸음 4m씩
걸음 수는 다르지만 셋 다 정확히 나무에 도착합니다. 왜냐하면 각자 "내가 쉰 시간만큼 크게 걷기"를 하고 있으니까요. 그게 델타 타임입니다.
6. 언리얼 엔진에서는 어떻게 쓸까?
언리얼 엔진은 매 프레임마다 Tick이라는 함수를 불러줍니다. "똑딱"이라는 뜻이에요. 시계 초침이 똑딱거리듯 매 프레임 한 번씩 불립니다.
그리고 이 Tick은 델타 타임을 선물처럼 건네줍니다.
void AMyActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
// 초당 300 단위로 앞으로 이동
float Speed = 300.0f;
AddActorLocalOffset(FVector(Speed * DeltaTime, 0, 0));
}
여기서 중요한 건 딱 하나입니다.
움직임이나 변화를 다룰 때는 항상 DeltaTime을 곱한다.
블루프린트에서도 똑같아요. Event Tick 노드에 Delta Seconds라는 출력 핀이 붙어 있습니다. 그걸 곱해서 쓰면 됩니다.
델타 타임을 곱해야 하는 것들
- 캐릭터 이동 거리
- 회전 각도
- 체력이 서서히 차오르는 양
- 타이머가 줄어드는 값
- 화면이 서서히 밝아지는 정도
공통점이 보이나요? 전부 "시간에 따라 변하는 것"입니다.
델타 타임을 곱하면 안 되는 것들
- 점프 버튼을 눌렀을 때 한 번에 주는 힘
- 총알을 한 발 발사하는 행동
- 문을 여는 것 같은 일회성 사건
이건 "순간에 딱 한 번" 일어나는 일이라 시간을 곱할 필요가 없어요.
7. 델타 타임을 잊으면 생기는 일
실제 게임 역사에도 이런 사고가 있었습니다. 옛날 게임 중에는 프레임이 빠른 컴퓨터에서 캐릭터가 벽을 뚫고 날아가거나, 물리가 폭발하듯 튕겨나가는 버그가 있었어요. 델타 타임을 제대로 안 썼기 때문입니다.
반대로 프레임이 갑자기 뚝 떨어질 때(랙이 걸릴 때)도 문제가 생겨요. 델타 타임이 갑자기 아주 커지면, 캐릭터가 한 프레임에 너무 멀리 순간이동해서 벽을 통과해버릴 수 있습니다. 그래서 엔진들은 델타 타임에 최댓값 제한을 걸어두기도 합니다. "아무리 커도 0.05초까지만 인정!" 이런 식이죠.
8. 정리
- 게임 루프는 입력 → 계산 → 그리기를 끝없이 반복하는 고리다.
- 컴퓨터마다 루프를 도는 속도가 다르다.
- 델타 타임은 지난 프레임부터 지금까지 흘러간 시간이다.
- 속도에 델타 타임을 곱하면, 어떤 컴퓨터에서도 같은 시간에 같은 거리를 간다.
- 언리얼에서는
Tick(float DeltaTime)또는 블루프린트의Delta Seconds로 받는다.
게임 개발에서 델타 타임은 "기본 중의 기본"입니다. 이걸 몸에 익히면, 여러분이 만든 게임은 친구의 낡은 노트북에서도, 최신 게임용 PC에서도 똑같이 잘 돌아갈 거예요.
출처
리깅 파이프라인 자동화 도구
반복 작업은 스크립트에게. 현업에서 쓰는 리깅 툴 모음.
리깅 도구 보기