본문 바로가기

유니티/스크립트

[간식의 유니티 기초] Update와 FixedUpdate, 도대체 뭐가 다른 건가요?

이 글에는 인프런 파트너스 링크가 포함되어 있습니다. 이 링크를 통해 구매하시면 제가 수익을 받을 수 있어요. 🤗

들어가며

유니티를 처음 배우면서 가장 혼란스러웠던 것 중 하나가 바로 UpdateFixedUpdate의 차이였습니다. "둘 다 매 프레임 실행되는 거 아니야?"라고 생각했는데, 실제로 캐릭터 이동을 구현하다가 물리 연산이 이상하게 동작하는 걸 보고 나서야 제대로 공부하게 되었습니다.

이 글은 유니티를 시작한 지 얼마 안 된 분들, 특히 "Update에 다 넣으면 되는 거 아닌가?"라고 생각하시는 분들을 위한 글입니다.


게임 루프란?

기본 개념

게임은 영화와 비슷합니다. 영화가 초당 24프레임의 이미지를 빠르게 보여주듯, 게임도 매 프레임마다 화면을 갱신합니다. 이 과정을 반복하는 것이 게임 루프(Game Loop)입니다.

유니티에서는 이 게임 루프를 직접 작성할 필요가 없습니다. 엔진이 알아서 처리해주고, 우리는 특정 타이밍에 호출되는 함수들만 구현하면 됩니다.

유니티의 실행 순서

유니티는 매 프레임마다 정해진 순서로 함수를 호출합니다:

Awake() → OnEnable() → Start() → FixedUpdate() → Update() → LateUpdate()

여기서 핵심은 FixedUpdate, Update, LateUpdate 세 가지입니다.


Update() - 매 프레임 호출

특징

Update매 프레임마다 한 번 호출됩니다. 프레임 레이트에 따라 호출 간격이 달라집니다.

  • 60fps → 초당 60번 호출
  • 30fps → 초당 30번 호출
  • 프레임 드랍 시 → 호출 간격 불규칙

언제 사용하나요?

  • 키보드/마우스 입력 처리
  • 애니메이션 트리거
  • UI 갱신
  • 물리와 무관한 이동 (Transform 직접 조작)
void Update()
{
    // 입력 처리는 Update에서
    if (Input.GetKeyDown(KeyCode.Space))
    {
        Jump();
    }

    // 비물리 이동
    float horizontal = Input.GetAxis("Horizontal");
    transform.Translate(Vector3.right * horizontal * speed * Time.deltaTime);
}

Time.deltaTime이 필요한 이유

여기서 Time.deltaTime을 곱하는 게 중요합니다. 이전 프레임과 현재 프레임 사이의 시간 간격인데, 이걸 곱하지 않으면 60fps 컴퓨터에서는 빠르게, 30fps 컴퓨터에서는 느리게 움직이는 문제가 생깁니다.

(저도 처음에 이걸 몰라서 "내 컴퓨터에선 잘 되는데 왜 다른 컴퓨터에선 이상하지?" 했던 기억이 있습니다)


FixedUpdate() - 고정 간격 호출

특징

FixedUpdate고정된 시간 간격으로 호출됩니다. 기본값은 0.02초(초당 50번)입니다.

프레임 레이트와 무관하게 일정한 간격을 유지합니다:

  • 60fps든 30fps든 → 초당 50번 호출 (기본 설정)

언제 사용하나요?

  • Rigidbody를 이용한 물리 이동
  • 힘(Force) 적용
  • 물리 기반 점프
private Rigidbody rb;

void Start()
{
    rb = GetComponent<Rigidbody>();
}

void FixedUpdate()
{
    // 물리 이동은 FixedUpdate에서
    float horizontal = Input.GetAxis("Horizontal");
    rb.AddForce(Vector3.right * horizontal * force);
}

왜 물리는 FixedUpdate인가요?

물리 엔진은 시간 간격이 일정해야 정확한 계산이 가능합니다. Update처럼 불규칙한 간격으로 물리 연산을 하면, 프레임 드랍 시 캐릭터가 벽을 뚫고 지나가거나 이상한 방향으로 튀는 현상이 발생합니다.

유니티의 물리 엔진(PhysX)이 FixedUpdate 직후에 실행되기 때문에, 물리 관련 코드는 여기에 넣어야 합니다.


LateUpdate() - Update 이후 호출

특징

LateUpdate는 모든 Update가 끝난 후에 호출됩니다.

언제 사용하나요?

주로 카메라 추적에 사용합니다.

// 카메라가 플레이어를 따라다니는 스크립트
void LateUpdate()
{
    // 플레이어의 Update 이동이 끝난 후 카메라 위치 갱신
    transform.position = player.position + offset;
}

Update가 아니라 LateUpdate일까요? 카메라가 Update에서 위치를 갱신하면, 플레이어보다 먼저 실행될 수 있습니다. 그러면 플레이어는 이동했는데 카메라는 이전 위치를 따라가서 화면이 덜덜 떨리는 현상이 생깁니다.


정리: 언제 뭘 써야 하나요?

함수 호출 타이밍 용도
Update 매 프레임 (불규칙) 입력 처리, UI, 비물리 로직
FixedUpdate 고정 간격 (0.02초) Rigidbody 물리 연산
LateUpdate Update 이후 카메라 추적

실수하기 쉬운 패턴

// ❌ 잘못된 예: 물리 이동을 Update에서
void Update()
{
    rb.AddForce(Vector3.forward * force);  // 프레임 드랍 시 불안정
}

// ✅ 올바른 예: 물리 이동은 FixedUpdate에서
void FixedUpdate()
{
    rb.AddForce(Vector3.forward * force);
}
// ❌ 잘못된 예: 입력 처리를 FixedUpdate에서
void FixedUpdate()
{
    if (Input.GetKeyDown(KeyCode.Space))  // 입력 누락 가능
    {
        Jump();
    }
}

// ✅ 올바른 예: 입력은 Update에서 받고, 물리는 FixedUpdate에서
void Update()
{
    if (Input.GetKeyDown(KeyCode.Space))
    {
        shouldJump = true;
    }
}

void FixedUpdate()
{
    if (shouldJump)
    {
        rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
        shouldJump = false;
    }
}

마무리

정리하면:

  • Update: 입력과 일반 로직
  • FixedUpdate: 물리 연산
  • LateUpdate: 카메라 등 후처리

사실 저도 지금까지 "대충 Update에 다 넣어도 돌아가긴 하니까"라는 마음으로 코드를 짠 적이 많습니다. 하지만 물리 기반 게임을 만들거나, 다양한 사양의 기기에서 테스트해보면 이 차이가 확실히 느껴집니다.

다음 글에서는 Time.deltaTimeTime.fixedDeltaTime의 차이, 그리고 FixedUpdate의 호출 주기를 조정하는 방법을 알아보겠습니다.

 


더 공부하고 싶다면?

이 글에서 다룬 내용을 영상으로 더 깊이 배우고 싶으시다면, 아래 강의를 추천드립니다:

완전 처음이라면:

체계적으로 MMORPG까지 도전하고 싶다면:


Reference