Update()
유니티엔진에서 Update()는 매 프레임 호출됩니다.
DirectX, OpenGL, Vulkan..등 그래픽스 라이브러리를 사용한 게임엔진은 기본적으로
Game Loop(게임 루프)를 통해서 프레임 기반 동작이 수행됩니다.
/* Main Function */
// Game Loop
while(...)
{
// User Inputs..
// Game Logics..
// Draw Calls..
// ...
}
이런식으로 메인 스레드에 무한 반복문을 통해 게임 루프를 작성하고, 이 게임 루프의 반복이 프레임의 진행을 뜻합니다.
즉, 게임 루프를 한번 실행하면 한 프레임이 지난 것입니다.
// Unity Game Loop
while(...)
{
// ...
foreach(var m in monoBehavioursWhoHaveUpdateMethod)
{
m.Update();
}
// ...
}
유니티 엔진은 Update()메소드가 존재하는 모든 MonoBehaviour를 찾아서 스크립팅 런타임이 내부적으로 저장해놓았다가 게임 루프에서 일괄적으로 순회하며 호출해주는 방식이라고 합니다.
그리고 많이들 Update()문에서 사용하는 Time.delTime은 게임 루프의 이전 수행과 현재 수행 간의 시간 간격을 저장한 값입니다. 이를 통해서 보정을 진행하죠 보통..
FixedUpdate()
FixedUpdate()는 잘못 이해하고 계신분들이 많습니다.
설정해놓은 일정한 주기로 실행되고(기본값 0.02초) 물리를 위한 연산이기때문에 Update()와는 별개의 루프라고 생각할 수 있습니다. 하지만 이는 틀렸습니다.
유니티의 내부 동작 구조를 살펴봅시다.(링크)
게임 루프속에 피직스 루프가 포함되어 있습니다.
피직스 루프안에는 FixedUpdate가 처음부분에서 시작됩니다.
즉, Update()와 FixedUpdate()는 완전히 분리된 것이 아닙니다.
Fixed Time Step값이 deltaTime보다 작으면, Physics Loop가 여러 번 반복하여 실행되는 원리입니다.
예를 들면,
Fixed Time Step은 0.02이고,
이번 프레임의 deltaTime이 0.1이었다면
0.1 / 0.02 = 5회 만큼 Physics Loop가 실행될 것으로 예측할 수 있습니다.
반대로, deltaTime이 0.005처럼 너무 작았다면
이번 프레임에는 Physics Loop가 한 번도 실행되지 않을 수 있습니다.
FixedUpdate()는 Physics Loop의 초입에 실행되며,
결국 게임 루프와는 별개로 일정한 주기마다 호출되는 것이 아니라
게임 루프 내에서 중첩 루프를 통해 호출된다는 것을 알 수 있습니다.
그치만 이렇게 생각하면 복잡하므로, 개발시에는 일정 주기로 FixedUpdate에서 물리계산을 진행한다~~라고 생각해도 될 것 같습니다.
Physics Loop의 필요성
그렇다면 왜 물리 업데이트는 기본적으로 0.02초의 주기를 갖고,
이 주기에 따라 계산된 횟수로 Physics Loop가 실행되는 것일까요?
물리 업데이트가 단순히 프레임 기반으로 실행되는, 반대의 상황을 가정해보면 쉽게 이해할 수 있습니다.
이런식으로 공이 벽에 부딪혀서 원래는 튕겨나갈것으로 생각을 했으나
컴퓨터가 너무 안좋아서 프레임이 낮다면??(update주기가 너무 느리다면?)
위 처럼 충돌을 감지못하고 통과해 버릴 수도 있습니다.
즉, Frame Rate에 따른 신뢰성 낮은 물리 시뮬레이션결과를 얻게 됩니다.
따라서 Fixed Time Step주기를 정하고
deltaTime이 너무 오래 걸렸으면
deltaTime과 Fixed Time Step의 관계에 따라 그만큼 Physics Loop를 반복함으로써
일정한 주기로 물리 업데이트가 진행되는 것처럼 보정하는 방식을 사용합니다.
Physics Loop를 왜 Game Loop속에 넣었을까?
이론적으로는 Physics Loop용 별도의 스레드를 통해서 일정한 주기로 수행되게 한다면 최고일텐데 왜 그랬지 않았을까요?
아마 스레드 간 데이터 동기화 문제로 보입니다.
메인스레드에서는 물리관련에 접근하지 못하게하고, FixedUpdate()및 내부 물리 연산에서는 물리와 관련없는 데이터에 접근하지 못하도록 완전히 분리하는 방식을 채택한다면, 프로그래머 입장에서 난이도가 매우 어려워지고, 물리 업데이트의 결과는 트랜스폼에 적용되어야 하므로 동기화 문제에서 자유롭지 못합니다.
그래서 메인 스레드와 물리 스레드의 공유 데이터를 모두 동기화(lock...)하자니, 게임 루프를 기반으로 동작하는 메인 스레드 특성 상 물리 스레드는 게임 루프의 동작에 종속되고, 반대로 게임 루프도 물리 스레드의 동작에 종속되는 최악의 경우가 발생할 수 있습니다.
따라서 위와같은 복잡한 문제를 가질바에 Physics Loop를 게임 루프 내에서 실행하고, deltaTime과 fixedDeltaTime의 관계에 따라 실행 횟수를 보정해주는 방식을 채택한 것으로 보입니다.
'유니티' 카테고리의 다른 글
[Unity] Awake와 Start차이 (0) | 2023.01.24 |
---|---|
[Unity] 오브젝트를 이동시키는 방법들 (2) | 2023.01.22 |
[Unity] tag, layer, sorting layer차이 (0) | 2023.01.21 |
[Unity] Rigidbody의 Gravity의 기준은? (0) | 2023.01.21 |
[Unity] 방해되는 3d 아이콘 크기 줄이기 (0) | 2023.01.21 |