1. Update, FixedUpdate, LateUpdate, deltaTime [Unity 공부]
안녕하세요! 개발자 zeroiron0입니다!
평소에 게임 개발쪽에 관심이 있어서 유니티 공부를 가끔씩 시도했었는데 이런 저런 일정에 치여 막상 제대로 시작한 적이 없었습니다.
그러다 지금에 이르러서 공부할 시간이 매우 많아졌기때문에 블로그에 포스트하면서 본격적으로 공부해보려고 합니다!
1. Update & deltaTime
Unity에서 새로운 C# 스크립트를 생성하고 편집할 때 Update 함수에 다음과 같은 주석이 달려있습니다.
Update is called once per frame (Update는 프레임마다 한번씩 불린다.)
여기에서 알 수 있는 것은 Unity는 각 프레임마다 Update를 수행한다는 것입니다. 우리는 여기서 프레임마다 필요한 캐릭터 움직임, 공격과 같은 행동이나 길찾기 알고리즘처럼 실시간으로 계산값이 달라져 프레임마다 계산해야하는 알고리즘이나 함수를 Update 안에서 실행하도록 만들어 처리할 수 있습니다.
이렇게 프레임마다 호출되는 Update에서 자주 사용되는 값이 있는데 바로 Time.deltaTime입니다. Unity Documentation에 따르면
The interval in seconds from the last frame to the current one (현재 프레임과 직전 프레임 사이의 시간 (초))
으로 정의되어있습니다. 다시 말해 프레임 하나를 처리하는데 걸린 시간입니다. 예를 들어 어떤 컴퓨터에서 FPS가 1이라면 Time.deltaTime의 값은 1이 되고, 성능이 좋은 컴퓨터에서 FPS가 60 이라면 Time.deltaTime의 값은 1/60이 됩니다.
그러면 FPS에 따라 달라지는 deltaTime을 어디에 사용할 수 있을까요? 바로 Update에서 성능 좋은 컴퓨터나 나쁜 컴퓨터나 같은 시간에 대해 같은 결과를 내고 싶을 때 사용할 수 있습니다. 아래 예제를 봅시다.
1/3s | 2/3s | 1s |
100 * 1/3 | 100 * 1/3 + 100 * 1/3 = 100 * 2/3 | 100 * 2/3 + 100 * 1/3 = 100 |
[FPS가 3일 경우]
1/2s | 1s |
100 * 1/2 | 100 * 1/2 + 100 * 1/2 = 100 |
[FPS가 2일 경우]
Update마다 결과값에 100 * deltaTime를 더해준다고 가정합시다. 그러면 FPS가 3인 경우 100 * 1/3를 한 프레임마다 더하고 결과적으로 1초에서는 100이 됩니다. FPS가 2인 경우 마찬가지로 deltaTime이 1/2이므로 100 * 1/2를 한 프레임마다 더하고 결과적으로 1초에서는 100이 됩니다. 이런 식으로 deltaTime을 사용한다면 FPS가 무엇이든지 시간이 각 deltaTime의 공배수만큼 지나면 같은 결과를 나타내게 됩니다.
개념을 알아봤으니 실제로 테스트를 진행해봅시다.
Empty 오브젝트와 Test 스크립트를 생성한 뒤 Update 함수를 이용해 대략적인 FPS와 Time.deltaTime을 구해봤습니다.
void Update()
{
Debug.Log(string.Format("FPS:{0} deltaTime: {1}", 1.0f / Time.deltaTime, Time.deltaTime));
}
위와 같이 FPS에 따라 deltaTime도 계속해서 바뀌는 것을 확인할 수 있습니다. 이제 Update에서 deltaTime을 쓰면 같은 결과를 유도하는데 유효하다는 것은 알게 됐지만 Update에는 아직 문제가 있습니다.
문제가 발생하는 예를 들어보겠습니다. 프레임마다 데미지를 주는 함수가 있다고 합시다. Update에서 이 함수를 실행한다면 FPS가 60인 경우 1초에 60번의 데미지를 입지만 FPS가 1인 경우 1초에 1번의 데미지만 입습니다. 이것처럼 성능에 따라 결과가 크게 달라지는 경우가 생깁니다. 이를 방지하기 위해 성능에 상관없이 똑같은 주기로 Update를 하는 함수가 존재하는데 바로 FixedUpdate 입니다.
2. FixedUpdate
FixedUpdate는 Update와 다르게 아래 그림과 같이 [Edit] - [Project Settings] - [Time]에 정의된 Fixed Timestep을 주기로 함수를 호출합니다.
아래와 같이 FixedUpdate를 정의하고 실행한다면
void FixedUpdate()
{
Debug.Log(string.Format("FPS:{0} deltaTime: {1}", 1.0f / Time.deltaTime, Time.deltaTime));
}
deltaTime이 설정한 Fixed Timestep과 동일한 것을 확인할 수 있습니다. 따라서 FixedUpdate에 넣는 함수의 경우, 실제 FPS에 맞는 즉각적인 반응이 필요없거나 FPS에 영향받지 않는 정확한 계산이 필요한 물리 계산을 할 때 주로 사용합니다.
3. LateUpdate
LateUpdate의 경우 Update 함수가 끝난 후 곧바로 호출되는 함수입니다. Update 함수가 호출될 동안 오브젝트의 포지션, 로테이션, 스케일 값 등이 변할 수 있습니다. 물론 이때 Update 내부에 적절히 순서에 맞게 처리 구문을 넣어 해당 값을 사용하는 함수를 호출한다면 제대로 처리할 수 있지만 Update 함수가 복잡해지고 성능이 저하할 수 있습니다. 따라서 Update가 확실히 끝난 다음 해당 프레임에서 그 값을 사용하는 함수를 LateUpdate에 호출한다면 Update 함수의 오버 헤드를 개선하고 코드를 간결하게 만들 수 있습니다. 이러한 개념을 기억한 채 아래 코드를 이용해 테스트를 진행하면
void Update()
{
Debug.Log("Update " + string.Format("FPS:{0} deltaTime: {1}", 1.0f / Time.deltaTime, Time.deltaTime));
}
void LateUpdate()
{
Debug.Log("LateUpdate " + string.Format("FPS:{0} deltaTime: {1}", 1.0f / Time.deltaTime, Time.deltaTime));
}
위의 그림과 같이 해당 프레임의 Update와 LateUpdate의 FPS와 deltaTime 값이 동일한 것을 볼 수 있습니다. 따라서 LateUpdate에서 사용할 함수는 확실하게 LateUpdate에 사용해줍시다.
4. 마무리
이렇게 게임 엔진에서 제일 기초적인 프레임마다 함수를 호출하는 Update, FixedUpdate, LateUpdate와 하나의 프레임을 처리하는데 걸리는 시간인 deltaTime에 대해서 공부해봤습니다.
다음 포스트에서는 플레이어 오브젝트를 정의해 이동과 충돌에 대해서 공부해보겠습니다.
감사합니다!!!!