Attention: Here be dragons

This is the latest (unstable) version of this documentation, which may document features not available in or compatible with released stable versions of Godot.

소개

물리 틱 및 렌더링된 프레임

Godot에서 이해해야 할 주요 개념 중 하나는 물리 틱(때때로 반복 또는 물리 프레임이라고도 함)과 렌더링된 프레임 간의 구별입니다. 물리학은 고정된 틱 속도(:ref:`Project Settings > Physics > Common > Physics Tick per Second<class_ProjectSettings_property_physics/common/physics_ticks_per_second>`에 설정)로 진행되며 기본값은 초당 60틱입니다.

그러나 엔진이 반드시 동일한 속도로 **렌더링**되는 것은 아닙니다. 많은 모니터가 60Hz(초당 주기)로 새로 고침되지만 많은 모니터는 완전히 다른 주파수(예: 75Hz, 144Hz, 240Hz 이상)로 새로 고침됩니다. 모니터가 새로운 프레임을 표시할 수 있더라도 초당 60회, CPU와 GPU가 이 속도로 프레임을 *공급*할 수 있다는 보장은 없습니다. 예를 들어, V-Sync로 실행할 때 컴퓨터는 60FPS에서는 너무 느려지고 30FPS의 데드라인에만 도달할 수 있습니다. 이 경우 표시되는 프레임은 30FPS에서 변경됩니다(끊김이 발생함).

하지만 여기에는 문제가 있습니다. 물리 틱이 프레임과 일치하지 않으면 어떻게 되나요? 물리 틱 속도가 프레임 속도와 위상이 다르면 어떻게 되나요? 아니면 더 나쁜 경우, 물리 틱 속도가 렌더링된 프레임 속도보다 낮은 경우 어떻게 되나요?

극단적인 시나리오를 고려하면 이 문제를 이해하기가 더 쉽습니다. 렌더링된 프레임 속도가 60FPS인 간단한 게임에서 물리 틱 속도를 초당 10틱으로 설정하면 렌더링된 프레임에 대해 개체 위치의 그래프를 플롯하면 위치가 부드러운 동작을 제공하는 대신 1/10초마다 "점프"하는 것처럼 나타나는 것을 볼 수 있습니다. 물리학이 새 객체의 새 위치를 계산할 때 단 한 프레임에 대해서만 이 위치에 렌더링되지 않고 6프레임에 대해 렌더링됩니다.

../../../_images/fti_graph_fixed_ticks.webp

이러한 점프는 틱/프레임 속도의 다른 조합에서 글리치 또는 지터로 볼 수 있습니다. 이는 물리 틱 시간과 렌더링된 프레임 시간 간의 불일치로 인해 발생하는 계단식 효과로 인해 발생합니다.

프레임과 틱이 동기화되지 않는 경우 어떻게 해야 합니까?

틱/프레임 속도를 함께 고정하시겠습니까?

가장 확실한 해결책은 모든 프레임과 일치하는 물리 틱을 보장하여 문제를 제거하는 것입니다. 이는 기존 콘솔과 고정 하드웨어 컴퓨터에 대한 접근 방식이었습니다. 모든 플레이어가 동일한 하드웨어를 사용한다는 것을 알고 있다면 틱과 프레임을 계산할 수 있을 만큼 빠른지 확인할 수 있습니다. 50FPS로 모든 사람에게 훌륭하게 작동할 것이라고 확신할 수 있습니다.

그러나 현대 게임은 더 이상 고정 하드웨어용으로 제작되지 않는 경우가 많습니다. 데스크톱 컴퓨터, 모바일 등에서 출시할 계획을 세우는 경우가 많습니다. 이들 모두는 성능 면에서 큰 차이가 있을 뿐만 아니라 모니터 새로 고침 빈도도 다릅니다. 우리는 문제를 해결하는 더 나은 방법을 찾아야 합니다.

틱율을 조정하시겠습니까?

고정된 물리 틱 속도로 게임을 설계하는 대신 최종 사용자의 하드웨어에 따라 틱 속도를 조정할 수 있습니다. 예를 들어 해당 하드웨어에 작동하는 고정 틱 속도를 사용하거나 특정 프레임 지속 시간과 일치하도록 각 물리 틱의 지속 시간을 변경할 수도 있습니다.

이것은 작동하지만 문제가 있습니다. 물리(및 종종 ``_physics_process``에서도 실행되는 게임 로직)는 고정, 미리 결정된 틱 속도로 실행될 때 가장 일관되게 작동합니다. 예를 들어 60TPS(초당 틱)용으로 설계된 레이싱 게임 물리학을 실행하려고 하면 다음과 같습니다. 10TPS에서는 물리학이 완전히 다르게 작동합니다. 컨트롤의 반응성이 떨어질 수 있으며 충돌/궤적이 완전히 다를 수 있습니다. 60TPS에서 게임을 철저하게 테스트한 다음, 다른 틱 속도로 실행될 때 최종 사용자의 컴퓨터에서 게임이 중단되는 것을 확인할 수 있습니다.

이는 재현하기 어려운 버그로 인해 품질 보증을 어렵게 만들 수 있으며, 특히 이러한 종류의 문제로 인해 비용이 많이 드는 AAA 게임에서는 더욱 그렇습니다. 특정 틱 속도로 게임을 실행하는 것이 다른 것보다 더 유리할 수 있으므로 경쟁 무결성을 위한 멀티플레이어 게임의 경우 문제가 될 수도 있습니다.

틱 속도를 잠그되 보간을 사용하여 물리 틱 사이의 프레임을 부드럽게 합니다.

이는 선택 사항이고 기본적으로 비활성화되어 있지만 문제를 해결하는 가장 인기 있는 접근 방식 중 하나가 되었습니다.

우리는 일관성과 예측 가능성을 위한 가장 바람직한 물리/게임 논리 배열은 디자인 타임에 고정되는 물리 틱 속도라는 것을 확립했습니다. 문제는 기록된 물리 위치와 부드러운 움직임을 제공하기 위해 물리 객체가 프레임에 표시되기를 "원하는" 위치 사이의 불일치입니다.

대답은 간단하지만 처음에는 이해하기가 조금 어려울 수 있습니다.

엔진에서 물리 개체의 현재 위치만 추적하는 대신, 개체의 현재 위치와 이전 물리 틱의 이전 위치*를 모두 추적합니다.

이전 위치*(사실 회전 및 크기 조정을 포함한 전체 변환)*가 필요한 이유는 무엇입니까? 약간의 수학 마법을 사용하여 **보간법**을 사용하여 부드럽고 연속적인 움직임이라는 이상적인 세계에서 두 점 사이의 객체 변형이 어떻게 될지 계산할 수 있습니다.

../../../_images/fti_graph_interpolated.webp

벡터 보간

이를 달성하는 가장 간단한 방법은 이전에 사용했던 선형 보간 또는 러핑(Lerping)입니다.

위치만 고려하고, 이전 물리 틱 X 좌표가 10단위이고 현재 물리 틱 X 좌표가 30단위임을 알고 있는 상황을 생각해 보겠습니다.

참고

여기에 수학이 설명되어 있지만 이 단계가 사용자를 위해 수행되므로 세부 사항에 대해 걱정할 필요가 없습니다. 내부적으로 Godot는 더 복잡한 형태의 보간을 사용할 수 있지만 설명 측면에서는 선형 보간이 가장 쉽습니다.

물리 보간 비례

물리 틱이 초당 10번 발생하는 경우(이 예에서는) 렌더링된 프레임이 0.12초에 발생하면 어떻게 될까요? 두 틱 사이의 부드러운 움직임을 얻기 위해 객체가 어디에 있는지 알아내기 위해 몇 가지 수학을 수행할 수 있습니다.

우선, 우리는 물체가 얼마나 멀리 물리적 틱을 통과하기를 원하는지 계산해야 합니다. 마지막 물리 틱이 0.1초에 발생했다면 0.1초(초당 10틱)가 소요될 것으로 알고 있는 틱을 통과하는 데 0.02초 *(0.12 - 0.1)*가 걸립니다. 따라서 진드기를 통한 분수는 다음과 같습니다.

fraction = 0.02 / 0.10
fraction = 0.2

이것은 **물리적 보간 비율**이라고 불리며 Godot에 의해 쉽게 계산됩니다. :ref:`Engine.get_physics_interpolation_fraction<class_Engine_method_get_physics_interpolation_fraction>`를 호출하여 모든 프레임에서 검색할 수 있습니다.

템플릿 위치시키기

보간 비율을 얻으면 이를 표준 선형 보간 방정식에 삽입할 수 있습니다. 따라서 X 좌표는 다음과 같습니다.

x_interpolated = x_prev + ((x_curr - x_prev) * 0.2)

따라서 ``x_prev``를 10으로, ``x_curr``를 30으로 대체하면 다음과 같습니다.

x_interpolated = 10 + ((30 - 10) * 0.2)
x_interpolated = 10 + 4
x_interpolated = 14

이를 분석해 보겠습니다.

  • 우리는 X가 10단위인 이전 틱(x_prev)의 좌표에서 시작한다는 것을 알고 있습니다.

  • 우리는 전체 틱 후에 현재 틱과 이전 틱의 차이가 추가된다는 것을 알고 있습니다(x_curr - x_prev)(20단위).

  • 우리가 변경해야 할 유일한 것은 물리 틱을 얼마나 멀리 진행했는지에 따라 추가하는 이 차이의 비율입니다.

참고

이 예제에서는 위치를 보간하지만 객체의 회전 및 크기 조정에도 동일한 작업을 수행할 수 있습니다. Godot가 이 모든 것을 해줄 것이기 때문에 세부 사항을 알 필요는 없습니다.

물리 틱 간의 부드러운 변환이 가능합니까?

이 모든 것을 종합하면 현재와 이전 물리 틱 사이의 객체 변환을 원활하게 추정하는 것이 가능하다는 것을 알 수 있습니다.

하지만 잠깐, 뭔가 눈치채셨을 수도 있습니다. 현재와 이전 틱 사이를 보간하는 경우 현재 개체의 위치를 추정하는 것이 아니라 과거 개체의 위치를 추정하는 것입니다. 정확하게 말하면, 우리는 과거 1~2틱 사이에서 객체의 위치를 추정하고 있습니다.

행성 색칠하기

이것은 무엇을 의미합니까? 이 방식은 효과가 있지만 화면에 보이는 것과 개체가 있어야 하는 위치 사이에 지연이 효과적으로 발생한다는 의미입니다.

실제로 대부분의 사람들은 이러한 지연을 알아차리지 못하며 오히려 일반적으로 불쾌한 일이 아닙니다. 게임에는 이미 심각한 지연이 발생하고 있지만 우리는 일반적으로 이를 알아차리지 못합니다. 가장 중요한 효과는 입력이 약간 지연될 수 있다는 것인데, 이는 빠른 트위치 게임의 요인이 될 수 있습니다. 이러한 빠른 입력 상황 중 일부에서는 물리 보간을 끄고 다른 구성표를 사용하거나 높은 틱 속도를 사용하여 이러한 지연을 완화할 수 있습니다.

왜 과거를 들여다보나요? 왜 미래를 예측하지 않는가?

이 방식에 대한 대안이 있습니다. 즉, 이전 틱과 현재 틱 사이를 보간하는 대신 수학을 사용하여 미래로 외삽*합니다. 우리는 물체가 어디에 있었는지 보여주기보다는 물체가 어디에 있을지 예측하려고 노력합니다. 이는 가능하며 향후 옵션으로 제공될 수도 있지만 몇 가지 중요한 단점이 있습니다.

  • 특히 물리 틱 중에 객체가 다른 객체와 충돌하는 경우 예측이 정확하지 않을 수 있습니다.

  • 예측이 잘못된 경우 물체는 벽 내부와 같이 "불가능한" 위치로 추정될 수 있습니다.

  • 이동 속도가 느리다면 이러한 잘못된 예측은 큰 문제가 되지 않을 수 있습니다.

  • 예측이 잘못된 경우 개체는 수정된 경로로 점프하거나 다시 스냅되어야 할 수 있습니다. 이는 시각적으로 거슬릴 수 있습니다.

벡터 보간

Godot에서는 이 전체 시스템을 물리 보간이라고 부르지만, 고정된 시간 간격(초당 물리 틱)으로 이동하는 객체 사이를 보간하므로 **"고정 시간 간격 보간"**이라고도 들을 수 있습니다. 어떤 면에서는 두 번째 항이 더 정확합니다. 왜냐하면 물리학에 의해 구동되지 않는 객체를 보간하는 데에도 사용할 수 있기 때문입니다.

일반적으로 물리 보간은 좋은 선택이지만, Godot의 내장 물리 보간을 사용하지 않기로 선택할 수도 있는(또는 제한된 방식으로 사용하기로 선택한) 예외가 있습니다. 예시 카테고리는 인터넷 멀티플레이어 게임입니다. 멀티플레이어 게임은 종종 다른 플레이어나 서버로부터 틱 또는 타이밍 기반 정보를 수신하며 이러한 정보는 로컬 물리 틱과 일치하지 않을 수 있으므로 사용자 정의 보간 기술이 더 적합할 수 있습니다.