본문 바로가기
개발/Unity 내일배움캠프 TIL

Unity - Update() vs FixedUpdate()

by 석시 2023. 7. 28.

이걸 글로 따로 정리하게 된 이유는 이해가 안갔던 상황이 너무 많았기 때문이다.

바로 Time.timeScale에 관한 부분이었다.

강의에서 게임을 정지하기 위해 Time.timeScale = 0; 다음과 같이 timeScale을 0으로 만들어 줬는데, 어떤 건 정지하고, 어떤 건 정지하지 않고 여전히 움직이는 이상한 상황.

문제의 장면.

빗방울과 타이머는 멈췄지만, 캐릭터는 멈추지 않았다.

골때리는건, 내가 따라 만든 게임은 캐릭터도 멈춰있는 것이었다.

원인을 찾아보니 차이점이 있었다.

강의에서 캐릭터의 움직임을 구현할 때는 Update() 함수를 사용하였고,

내가 이것을 따라만들 때는 FixedUpdate() 함수를 사용하였던 것이다.

이걸 보고 바로 알 수 있는 점은, ”Time.timeScale은 Update()에는 영향을 주지 않지만, FixedUpdate()에는 영향을 주는구나!”겠다.

하지만 약간 틀린 말이다.

좀만 더 자세하게 알아보자.

0. Time Scale

Unity 공식 문서에는, “시간이 흐르는 속도”라고 설명해준다.

풀어서 설명하자면, ”현실 시간 대비 게임 세상의 시간이 흐르는 속도”인 것이다.

예를 들어보자면, Time Scale2.0으로 만들면 게임 세상의 시간이 두 배 빨라진다는 소리이다.

게임에서 앞으로 걸어가던 사람은 2배의 속도로 걸어가기 시작할 것이며, 떨어지는 물체는 현실 대비 2배의 가속도를 받아 떨어질 것이다.

Time Scale0으로 만들면 게임 세상의 시간은 멈춘다. 라는 뜻이 되겠다.

참고로 Time Scale의 접근 방법은 두 가지가 있다.

메뉴: Edit → Project Setting → Time 카테고리에서 Time Scale 설정

또는 코드에서 Time.timeScale로 접근이 가능하다.

1. Update()

프레임마다 호출되는 함수이다.

프레임마다 라는 것은 즉, 호출되는 시간 간격이 일정하지 않다는 소리.

호출 간격이 Time.deltaTime에 기록이 된다.

골 때리는 점이 하나 있다.

Time.timeScale을 변경해도, Update() 실행 주기에는 변함이 없다. 프레임 간격이 바뀌는 건 아니니까.

근데, Time.deltaTime의 값은 바뀐다!

프레임 간격은 현실 세계의 시간이지만, 스크립트에 넘겨지는 deltaTime이라는 것은 게임 세계의 시간 흐름이기 때문이다.

즉, 다시 말하면 Time.deltaTime의 의미는 Update()에서 현실의 두 프레임 간격동안, 게임 세계에서 흐른 시간인 것이다.

따라서 Time Scale은 Update()에 영향을 주지 않는다. 라는 말은 반은 맞고 반은 틀린 것이다.

Time Scale을 0으로 만들었을 때도 Update()에서 캐릭터가 멈추도록 만들 수 있다.

void Update()
{
    if (transform.position.x > 2.8f)
    {
        direction = -0.05f;
        toward = -1.0f;
    }
    if (transform.position.x < -2.8f)
    {
        direction = 0.05f;
        toward = 1.0f;
    }
    if (Input.GetMouseButtonDown(0))
    {
        direction *= -1;
        toward *= -1;
    }
    transform.localScale = new Vector3(toward, 1, 1);
    transform.position += new Vector3(direction, 0.0f, 0.0f);
}

해당 코드가 원래 버전 코드이다.

이렇게 작성하면 캐릭터가 멈추지 않는다.

Time.deltaTime을 전혀 사용하지 않았기 때문이다.

다음과 같이 작성하면, deltaTime이 0이 되었을 때, 캐릭터가 멈추도록 만들 수 있겠다.

transform.position += 60f * Time.deltaTime * new Vector3(direction, 0.0f, 0.0f);

하지만, 이런 식의 물리나 움직임 구현은 Update()를 사용하는 것보다는 되도록이면 FixedUpdate()를 사용하는 것이 낫겠다.

2. FixedUpdate()

Fixed Timestep에 따라 일정한 간격으로 호출되는 함수이다.

그 말은 즉슨, 이 Fixed Timestep을 임의로 수정해주면 호출 간격을 변경할 수 있다는 소리.

놀랍게도 이것 역시 Project Settings에서 변경이 가능하다.

스크립트에서는 FixedUpdate() 내부의 Time.fixedDeltaTime으로 접근이 가능하다.

참고로 FixedUpdate()Time.deltaTime 역시 Fixed Timestep을 참조한다.

Update()에서는 Time.deltaTime은 프레임 간격, Time.fixedDeltaTime은 Fixed Timestep을 참조한다는 것도 알아두자.

Time Scale을 변경하게 되면, FixedUpdate()가 실행되는 주기 역시 영향을 받는다. 라고 말하고 싶지만, 이건 우리 입장, 즉 현실 세계의 관점이다.

Time Scale을 변경해도 게임 세계에선 여전히 0.02초마다 FixedUpdate()가 실행된다.

현실 세계인 우리 눈에만 Time Scale * Fixed Timestep의 시간 간격으로 실행되는 것으로 보이는 것이다.

예를 들어보자.

불릿 타임을 구현하기 위해 Time Scale을 0.05로 설정한다고 해보자.

Fixed Timestep이 0.02이기 때문에, 총알은 1초에 50번 움직이고 있는 건데, Time Scale을 변경해도 게임세상에서는 여전히 1초에 50번 움직이는 거다.

당연히 게임 세상의 1초가 우리 눈에는? 1초처럼 보이지 않는다.

우리 눈에 1 / 0.05 = 20초동안 보인 광경이 게임 세상에선 1초가 흐른 것이다.

그러면 현실의 우리 입장에서 총알은? 50 * 0.02 = 2.5Hz 즉, 1초에 2.5번 움직이는 것으로 보이는 것이다.

참고로 Rigidbody의 물리 구현 역시 FixedUpdate()의 호출을 통해 이루어진다.

Rigidbody로 낙하를 구현한 빗방울이 Time Scale을 0으로 했을 때 멈추는 이유가 바로 그것이다.

자, 여기까지는 Time Scale에 영향 받게 하려면 FixedUpdate()를 쓰자! 라고 결론이 나지만, 희망적이지만은 않은 이야기가 하나 있다.

3. Update()와 FixedUpdate()의 종속 관계

FixedUpdate()는 반드시 Fixed Timestep마다 실행되는 것처럼 보이지만, 실제로는 그렇게 실행되는 것처럼 보이도록 보정되는 것이다.

Update()와 FixedUpdate()는 각각 별개로 호출되는 것이 아니다.

Unity - Manual: Order of execution for event functions
https://docs.unity3d.com/Manual/ExecutionOrder.html

스크립트 라이프사이클을 보면, 각종 loop들이 존재하는데, 그 중 FixedUpdate의 반복 호출을 돕는 것은 Physics Loop이다.

Update()가 실행되고, deltaTime을 얻어냈을 때, Fixed Time Step이 deltaTime보다 작으면 Physics Loop가 여러 번 실행되는 것이다.

이 말을 다시 생각해보면, FixedUpdate()의 실행이 Update()에 종속적이게 된다는 소리다.

따라서 Update()가 너무 빠르게 호출되어 FixedUpdate()가 실행되지 않는 순간도 존재할 거라는 의미.

4. 결론

결론은 그거다.

Time Scale현실 시간 대비 게임 세상의 시간 흐름 속도를 의미한다.

Update()는 현실시간의 프레임 간격마다 호출, deltaTime은 게임 시간 간격을 참조한다.

FixedUpdate()는 게임시간 간격마다 호출, fixedDeltaTime 역시 게임 시간 간격을 참조.

이러한 것들에 대해 너무나도 잘 설명해주는 글을 발견하여 첨부하고자 한다. 꼭 읽어보시길.

유니티 - FixedUpdate()와 Physics Loop에 대한 이해
Update()와 Game Loop
https://rito15.github.io/posts/unity-fixed-update-and-physics-loop/
유니티 - Time.deltaTime과 Time.fixedDeltaTime
deltaTime과 fixedDeltaTime
https://rito15.github.io/posts/unity-deltatime-and-fixeddeltatime/
유니티 - FixedUpdate()에서 이동, 회전 구현 시 버벅임 현상 해결하기
Update()와 FixedUpdate()
https://rito15.github.io/posts/unity-fixed-update-and-stuttering/

Uploaded by N2T