[Unity] FixedUpdate

shin0112·2025년 10월 24일

Unity

목록 보기
4/13
post-thumbnail

✨ 들어가며

오늘은 Unity에서 볼 수 있는 FixedUpdate()Time.fixedDeltaTime이 도대체 왜 따로 존재하는지 궁금해서 찾아보다가 rito님의 글을 읽게 됐다.

지금까지는 단순히

“FixedUpdate는 컴퓨터 성능과 관계없이 일정한 간격으로 호출되는 함수다.”

정도로만 알고 있었다.

그런데 글을 읽고 나니, Unity 내부에서 물리 루프가 렌더 루프와는 완전히 다른 방식으로 돌아간다는 걸 알게 되었다.


1️⃣ Update vs FixedUpdate — 단순한 구분

  • Update() → 매 프레임마다 실행 (렌더링 속도에 따라 달라짐)
  • FixedUpdate() → 일정 시간 간격(기본 0.02초)마다 실행

지금까지 “물리 연산은 고정된 시간 간격으로 처리해야 한다”고 알고 있었지만,
이건 Unity 루프 전체 구조를 보면 표면적인 차이에 불과하다.


2️⃣ 물리 루프는 렌더 루프와 완전히 독립적이다

Unity 내부에는 두 개의 루프가 존재한다.

  • 렌더 루프(Render Loop) : 화면을 그리는 주기
  • 물리 루프(Physics Loop) : 시뮬레이션을 계산하는 주기

즉, Update()는 프레임(렌더링) 기준으로 실행되고,
FixedUpdate()는 “물리 시간” 기준으로 실행된다.

예를 들어 fixedDeltaTime0.02라고 하자.
즉, 물리 연산은 게임 시간 기준으로 0.02초마다 한 번씩 계산되어야 한다.

그런데 실제로는 프레임마다 걸리는 시간이 달라진다.
어떤 컴퓨터에서는 한 프레임이 0.016초가 걸릴 수도 있고,
어떤 순간에는 0.05초가 걸릴 수도 있다.


🧩 예시 1. FPS가 빠른 경우

  • 한 프레임의 deltaTime0.01초
  • fixedDeltaTime0.02초

아직 물리 시간이 0.02초에 도달하지 않았으니,
이번 프레임에서는 FixedUpdate()호출되지 않는다.

즉, 렌더링은 더 자주 돌아가지만 물리 루프는 “아직 다음 계산 타이밍이 안 됐다”고 판단한 것.


🧩 예시 2. FPS가 느린 경우

  • 한 프레임의 deltaTime0.06초
  • fixedDeltaTime0.02초

이번엔 한 프레임이 0.06초나 걸렸기 때문에
물리 시뮬레이션 입장에서는 0.06초만큼의 시간이 지나버린 상태다.

그래서 Unity는 “0.02초 × 3회 = 0.06초”만큼의 물리 연산을 따라잡기 위해 FixedUpdate()를 세 번 연속으로 실행한다.

이렇게 해야 렌더링이 늦더라도 물리 연산이 “0.02초 단위로 일정하게 진행된 것처럼” 맞춰진다.

즉, FixedUpdate는 진짜로 “0.02초마다 따로 도는 함수”가 아니라,

매 프레임마다 누적된 물리 시간에 맞춰 필요한 만큼 여러 번 실행되는 보정 루프다.

이 구조 덕분에 Unity는

  • 프레임 속도가 빠르든 느리든
  • CPU가 버벅이든 말든

항상 물리 시뮬레이션이 같은 시간 단위로 동작하는 결과를 낸다.

결국 FixedUpdate()는 “시간이 정확히 흘러간 만큼만 물리를 진행시키는 루프”라고 볼 수 있다.


3️⃣ 직접 로그로 확인하기

이 구조를 눈으로 보기 위해 직접 로그를 찍어봤다.

using UnityEngine;

public class UpdateTest : MonoBehaviour
{
    private void Update()
    {
        Debug.Log($"[Update]  Frame: {Time.frameCount}, RealTime: {Time.realtimeSinceStartup:F3}, deltaTime: {Time.deltaTime:F3}");
    }

    private void FixedUpdate()
    {
        Debug.Log($"[FixedUpdate] Frame: {Time.frameCount}, RealTime: {Time.realtimeSinceStartup:F3}, fixedDeltaTime: {Time.fixedDeltaTime:F3}");
    }
}

실험 결과


로그 해석

[00:00:18] [Update]  Frame: 10, RealTime: 4.651, deltaTime: 0.008
[00:00:18] [FixedUpdate] Frame: 11, RealTime: 4.699, fixedDeltaTime: 0.020
[00:00:18] [FixedUpdate] Frame: 11, RealTime: 4.699, fixedDeltaTime: 0.020
[00:00:18] [Update] Frame: 11, RealTime: 4.700, deltaTime: 0.049
  • 같은 Frame 11에서 FixedUpdate()가 두 번 연속 호출됨.
  • 11 프레임(deltaTime = 0.049)이 잠시 느려지면서
    Unity가 “0.02초 단위 물리 계산 × 2회”를 실행해 누락된 물리 시간을 보정한 것이다.

즉, 렌더 루프는 한 프레임이 밀려도,
물리 루프는 “흘러간 시간만큼” 정확히 따라잡는다.


이후 프레임 변화

[00:00:18] [FixedUpdate] Frame: 12, RealTime: 4.706, fixedDeltaTime: 0.020
[00:00:18] [Update] Frame: 12, RealTime: 4.707, deltaTime: 0.007
  • 프레임이 안정화되며 FixedUpdate()는 다시 한 번만 실행된다.
  • deltaTime도 0.007로 복귀되어, 140FPS 수준으로 정상화됨.

💡 정리

항목설명
FixedUpdate()가 여러 번 호출프레임이 밀렸을 때 누적된 물리 시간을 보정하기 위한 동작
deltaTime이 0.049로 커짐렌더 루프 지연 (CPU/렌더링 부하)
이후 프레임에서 빠르게 복귀Catch-up Loop가 누락된 시간만큼 보정 완료

즉, FixedUpdate()는 단순히 “고정 주기”가 아니라, “시간 누적 보정 기반의 시뮬레이션 루프”라는 것을 이 로그를 통해 직접 확인할 수 있었다.

🎯 결론
물리 루프는 프레임 루프보다 “정확한 시간 흐름”을 유지한다.
FPS가 떨어져도 물리 계산은 틀어지지 않으며,
이때 FixedUpdate()가 여러 번 도는 건 정상적인 보정 과정이다.


4️⃣ FixedDeltaTime의 진짜 의미

Time.fixedDeltaTime은 “함수가 호출되는 실제 간격”이 아니라,
“물리 시뮬레이션이 한 스텝에서 흘러간다고 가정하는 시간 단위”다.

즉, 한 번의 FixedUpdate()가 실제로 0.02초 기다린다는 뜻이 아니라,
“이번 스텝에서 게임 내 시간 0.02초가 지났다고 계산한다”는 의미다.

공식 문서에서도 이렇게 설명한다.

The interval in seconds of in-game time at which physics and other fixed frame rate updates (like FixedUpdate) are performed.
The interval is always relative to the in-game time which Time.timeScale affects.
For example, a fixedDeltaTime of 1 second in a game with a Time.timeScale of 0.5
means fixed updates occur every 2 seconds of real time.

즉, fixedDeltaTime“실제 시간(real time)”이 아니라 “게임 시간(Time.timeScale)” 기준이다.


✍️ 마치며

결국 FixedUpdate는 단순히 “고정 주기 함수”가 아니라, 렌더 루프와 분리된 물리 시뮬레이션 루프였다.

그리고 fixedDeltaTime은 “물리 계산 한 스텝이 담당하는 게임 세계의 시간 단위”였다.

이번에 직접 로그를 찍어보니, 이론으로만 보던 “Catch-up Loop”가 실제로 어떻게 작동하는지도 한눈에 보였다.

0개의 댓글