핵심 정리
// Update — 프레임 독립적 이동
transform.Translate(Vector3.forward * speed * Time.deltaTime);
// Update — 타이머 누적
elapsed += Time.deltaTime;
// FixedUpdate — 물리 기반 이동 (fixedDeltaTime 사용)
rb.MovePosition(rb.position + direction * speed * Time.fixedDeltaTime);구조 이해
Time.deltaTime이란
Time.deltaTime은 직전 프레임 종료 시점부터 현재 프레임 시작까지 경과한 시간(초) 입니다. 30fps 기기에서는 약 0.033, 60fps 기기에서는 약 0.016, 144fps에서는 약 0.007이 됩니다.
deltaTime을 곱하지 않으면 speed는 "초당 속도"가 아니라 "프레임당 속도" 가 됩니다. 같은 코드가 60fps 기기에서는 두 배 빠르게, 30fps 기기에서는 절반 속도로 움직이게 됩니다.
// 잘못된 예 — 기기 성능에 따라 속도가 달라짐
transform.position += Vector3.forward * speed;
// 올바른 예 — 어떤 기기에서도 초당 speed 만큼 이동
transform.position += Vector3.forward * speed * Time.deltaTime;타이머와 쿨다운 패턴
deltaTime을 매 프레임 누적하면 경과 시간을 추적할 수 있습니다. 쿨다운, 지속 시간, 대기 시간을 Update에서 관리할 때 가장 기본이 되는 패턴입니다.
[SerializeField] private float cooldown = 2f;
private float cooldownTimer;
private void Update()
{
cooldownTimer -= Time.deltaTime;
if (Input.GetKeyDown(KeyCode.Space) && cooldownTimer <= 0f)
{
Fire();
cooldownTimer = cooldown;
}
}경과 시간을 누적하는 방식도 자주 씁니다.
[SerializeField] private float duration = 3f;
private float elapsed;
private void Update()
{
elapsed += Time.deltaTime;
float t = Mathf.Clamp01(elapsed / duration); // 0 → 1 진행률
transform.position = Vector3.Lerp(startPos, endPos, t);
if (elapsed >= duration)
gameObject.SetActive(false);
}Time.fixedDeltaTime — 물리 루프의 deltaTime
FixedUpdate는 고정 타임스텝으로 돌기 때문에 deltaTime 대신 Time.fixedDeltaTime을 씁니다. 기본값은 0.02초(50Hz)이며 Project Settings → Time → Fixed Timestep 에서 바꿀 수 있습니다.
private void FixedUpdate()
{
// MovePosition은 물리 엔진 경유 이동 — fixedDeltaTime 사용
rb.MovePosition(rb.position + moveDir * speed * Time.fixedDeltaTime);
// 힘 적용도 FixedUpdate 안에서
rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
}FixedUpdate 안에서 Time.deltaTime을 써도 Unity가 자동으로 fixedDeltaTime을 반환하므로 동작은 같습니다. 하지만 의도를 명확히 하려면 fixedDeltaTime을 직접 쓰는 편이 가독성이 좋습니다.
Time.smoothDeltaTime — 스파이크 완화
Time.smoothDeltaTime은 최근 몇 프레임의 deltaTime을 평균 낸 값입니다. 일시적인 GC 스파이크나 로딩 히치가 있을 때 값이 튀는 것을 완화해 줍니다.
// 카메라 추적처럼 갑작스런 점프가 보기 싫을 때
transform.position = Vector3.Lerp(
transform.position,
target.position,
followSpeed * Time.smoothDeltaTime
);매번 쓸 필요는 없지만, 카메라 보간이나 UI 애니메이션처럼 끊김이 눈에 잘 띄는 곳에서 deltaTime이 튄다면 smoothDeltaTime으로 교체해볼 수 있습니다.
체크포인트
| 속성 | 사용 위치 | 설명 |
|---|---|---|
Time.deltaTime | Update, LateUpdate | 직전 프레임 경과 시간 |
Time.fixedDeltaTime | FixedUpdate | 고정 물리 타임스텝 (기본 0.02s) |
Time.smoothDeltaTime | Update, LateUpdate | 최근 프레임 평균 경과 시간 |
Time.unscaledDeltaTime | Update (pause 중) | timeScale 영향을 받지 않는 경과 시간 |
Time.time | 어디서나 | 게임 시작 이후 누적 시간 (timeScale 영향 받음) |
Time.realtimeSinceStartup | 어디서나 | 실제 경과 시간 (timeScale 영향 없음) |
| 상황 | 선택 |
|---|---|
| 이동, 회전, 페이드 (Update) | Time.deltaTime |
| Rigidbody 이동, 힘 적용 | Time.fixedDeltaTime |
| 카메라 추적, 보간 스무딩 | Time.smoothDeltaTime |
| pause 중 UI 타이머 | Time.unscaledDeltaTime |
| 상황 | 흔한 오해 | 더 맞는 선택 |
|---|---|---|
| 게임 일시정지 중 UI 애니메이션 | deltaTime도 계속 흐를 거라고 생각함 | unscaledDeltaTime |
| 물리 이동 | deltaTime와 fixedDeltaTime이 같다고 넘김 | 의도를 드러내려면 fixedDeltaTime |
| 순간 히치가 있는 카메라 보간 | deltaTime만 써도 충분하다고 생각함 | 시각적 스파이크가 보이면 smoothDeltaTime 검토 |
주의할 점
첫 프레임의 deltaTime은 이상하게 클 수 있습니다. 씬 로딩, 에셋 초기화 등으로 첫 프레임이 수백 ms가 되면 deltaTime도 그만큼 커져 캐릭터나 오브젝트가 순간 이동처럼 튀어나갈 수 있습니다. Mathf.Min(Time.deltaTime, maxDeltaTime) 으로 상한을 두거나, Update에서 if (Time.frameCount < 2) return; 처리로 첫 프레임을 건너뛰는 방어 코드가 도움이 됩니다.
// ❌ pause 중에도 deltaTime 기반 UI 타이머 사용
private void Update()
{
pauseBlink += Time.deltaTime;
}
// → timeScale = 0 이면 멈춤
// ✅ pause 중 UI는 unscaledDeltaTime 사용
private void Update()
{
pauseBlink += Time.unscaledDeltaTime;
}참고 링크
3 sources