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, яку слід зрозуміти, є різниця між фізичними тиками (іноді їх називають ітераціями або фізичними фреймами) і відтвореними фреймами. Фізика виконується з фіксованою швидкістю (встановленою в Project Settings > Physics > Common > Physics Tick per Second), яка за замовчуванням становить 60 тактів за секунду.

Однак механізм не обов’язково відтворює з однаковою швидкістю. Хоча багато моніторів оновлюються з частотою 60 Гц (циклів на секунду), багато оновлюються на зовсім інших частотах (наприклад, 75 Гц, 144 Гц, 240 Гц або більше). Навіть якщо монітор може показати новий кадр, напр. 60 разів на секунду, немає гарантії, що ЦП і ГП зможуть забезпечувати кадри з такою швидкістю. Наприклад, під час роботи з V-Sync комп’ютер може бути надто повільним для 60 і досягати кінцевих термінів лише для 30 FPS, у цьому випадку кадри, які ви бачите, змінюватимуться на 30 FPS (що призведе до заїкань).

Але тут є проблема. Що буде, якщо галочки фізики не збігаються з рамками? Що станеться, якщо фізична тактова частота не відповідає фазі частоти кадрів? Або ще гірше, що станеться, якщо частота тактів фізики буде нижчою за частоту кадрів рендерингу?

Цю проблему легше зрозуміти, якщо розглянути екстремальний скрипта. Якщо ви встановите частоту фізичних тактів на 10 тактів на секунду, у простій грі з частотою відтворення кадрів 60 FPS. Якщо ми побудуємо графік позицій об’єкта на відтворених кадрах, ви побачите, що позиції будуть «стрибати» кожну 1/10 секунди, а не рухатися плавно. Коли фізика розраховує нове положення для нового об’єкта, він рендериться в цьому положенні не лише для одного кадру, а для 6 кадрів.

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

Цей стрибок можна побачити в інших комбінаціях тиків/частоти кадрів як збої або тремтіння, спричинені цим ефектом сходів через розбіжність між часом фізичного такту та часом відтворення кадру.

Що ми можемо зробити з розсинхронізацією кадрів і позначок?

Зафіксувати тік/частоту кадрів разом?

Найбільш очевидним рішенням є позбутися проблеми, переконавшись, що галочка фізики збігається з кожним кадром. Раніше це був підхід на старих консолях і комп’ютерах зі стаціонарним обладнанням. Якщо ви знаєте, що всі гравці використовуватимуть одне й те саме обладнання, ви можете переконатися, що воно достатньо швидке для обчислення тиків і фреймів, наприклад. 50 FPS, і ви будете впевнені, що це буде чудово працювати для всіх.

Однак сучасні ігри часто більше не створюються для фіксованого обладнання. Ви часто плануєте випуск на настільних комп’ютерах, мобільних телефонах тощо. Усі вони мають величезні варіації продуктивності, а також різні частоти оновлення монітора. Нам потрібно придумати кращий спосіб вирішення проблеми.

Адаптувати тікрейт?

Замість того, щоб розробляти гру з фіксованою частотою кадрів у фізичному режимі, ми могли б дозволити частоті кадрів масштабуватися відповідно до апаратного забезпечення кінцевого користувача. Ми могли б, наприклад, використовувати фіксовану частоту кадрів, яка підходить для цього апаратного забезпечення, або навіть змінювати тривалість кожного кадру у фізичному режимі, щоб вона відповідала певній тривалості кадру.

Це працює, але є проблема. Фізика (та ігрова логіка, яка часто також виконується в _physics_process) працює найкраще та найстабільніше, коли вона працює з фіксованою, заздалегідь визначеною частотою тиків. Якщо ви спробуєте запустити фізику гоночної гри, розроблену для 60 TPS (тиків за секунду), наприклад, з частотою 10 TPS, фізика поводитиметься зовсім інакше. Керування може бути менш чутливим, зіткнення/траєкторії можуть бути зовсім іншими. Ви можете ретельно протестувати свою гру на 60 TPS, а потім виявити, що вона ламається на машинах кінцевих користувачів, коли вона працює з іншою частотою тиків.

Це може ускладнити перевірку якості через помилки, які важко відтворити, особливо в іграх AAA, де проблеми такого роду можуть коштувати дуже дорого. Це також може бути проблематичним для багатокористувацьких ігор для конкурентної цілісності, оскільки запуск гри з певними тиками може бути більш вигідним, ніж інші.

Зафіксуйте частоту тактів, але використовуйте інтерполяцію, щоб згладити кадри між фізичними тактами

Це став один із найпопулярніших підходів до вирішення проблеми, хоча він необов’язковий і вимкнений за замовчуванням.

Ми встановили, що найбажаніша логіка фізики/ігри для узгодженості та передбачуваності — це швидкість фізичних тактів, яка фіксується під час розробки. Проблема полягає в розбіжності між фізичною позицією, яку записали, і місцем, де ми «хочемо», щоб фізичний об’єкт був показаний на кадрі, щоб забезпечити плавний рух.

Відповідь виявляється простою, але спочатку може бути трохи важко зрозуміти.

Замість того, щоб відстежувати лише поточне положення фізичного об’єкта в механізмі, ми відстежуємо поточне положення об’єкта та попереднє положення на попередньому фізичному тику.

Навіщо нам потрібна попередня позиція (фактично все перетворення, включаючи обертання та масштабування)? Використовуючи трохи математичної магії, ми можемо використовувати інтерполяцію, щоб обчислити, яким буде перетворення об’єкта між цими двома точками в нашому ідеальному світі плавного безперервного руху.

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

Лінійна інтерполяція

Найпростішим способом досягти цього є лінійна інтерполяція, або lerping, яку ви, можливо, використовували раніше.

Давайте розглянемо лише позицію та ситуацію, коли ми знаємо, що попередня фізична координата Х у фізичному тику становила 10 одиниць, а поточна координата Х у фізичному тику становить 30 одиниць.

Примітка

Хоча тут пояснюється математика, вам не потрібно турбуватися про деталі, оскільки цей крок буде виконано за вас. Під капотом Godot може використовувати більш складні форми інтерполяції, але лінійна інтерполяція є найпростішою з точки зору пояснення.

Інтерполяційна частка фізики

Якщо наші фізичні тики відбуваються 10 разів на секунду (для цього прикладу), що станеться, якщо наш рендерений кадр відбудеться в момент часу 0,12 секунди? Ми можемо зробити трохи математики, щоб визначити, де буде знаходитися об’єкт, щоб отримати плавний рух між двома позначками.

Перш за все, ми повинні обчислити, на якій відстані через фізичний тік ми хочемо, щоб об’єкт був. Якщо останній фізичний тік відбувся на 0,1 секунди, ми проходимо 0,02 секунди (0,12 - 0,1) через тік, який, як ми знаємо, займе 0,1 секунди (10 тактів на секунду). Таким чином, частка через галочку дорівнює:

fraction = 0.02 / 0.10
fraction = 0.2

Це називається фізичною інтерполяційною часткою, яку зручно розраховує для вас Godot. Його можна отримати з будь-якого кадру, викликавши Engine.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 починається з координати на попередньому тику (x_prev), яка становить 10 одиниць.

  • Ми знаємо, що після повного тику різниця між поточним і попереднім тиками буде додана (x_curr - x_prev) (це 20 одиниць).

  • Єдине, що нам потрібно змінювати, це частка цієї різниці, яку ми додаємо, відповідно до того, наскільки далеко ми пройшли фізику.

Примітка

Хоча в цьому прикладі інтерполюється положення, те ж саме можна зробити з обертанням і масштабом об’єктів. Не обов'язково знати подробиці, адже все це за вас зробить Godot.

Згладжені перетворення між галочками фізики?

Поєднання всього цього разом показує, що має бути можливим мати хорошу плавну оцінку трансформації об’єктів між поточною та попередньою фізикою.

Але зачекайте, можливо, ви щось помітили. Якщо ми виконуємо інтерполяцію між поточним і попереднім тиками, ми не оцінюємо положення об’єкта зараз, ми оцінюємо положення об’єкта в минулому. Якщо бути точним, ми оцінюємо положення об'єкта між 1 і 2 тиками в минулому.

У минулому

Що це означає? Ця схема дійсно працює, але це означає, що ми фактично вводимо затримку між тим, що ми бачимо на екрані, і тим, де мають бути об’єкти.

На практиці більшість людей не помітять цю затримку, точніше, вона зазвичай не викликає заперечень. В іграх вже є значні затримки, просто ми зазвичай їх не помічаємо. Найсуттєвішим ефектом є невелика затримка введення, що може бути фактором у швидких іграх. У деяких із цих ситуацій швидкого введення ви можете вимкнути фізичну інтерполяцію та використати іншу схему або використати високу частоту тактів, що зменшить ці затримки.

Навіщо дивитися в минуле? Чому б не передбачити майбутнє?

Існує альтернатива цій схемі: замість інтерполяції між попереднім і поточним тиками ми використовуємо математику для екстраполяції на майбутнє. Ми намагаємося передбачити, де буде об’єкт, а не показувати, де він був. Це можна зробити та запропонувати як опцію в майбутньому, але є кілька суттєвих недоліків:

  • Прогноз може бути невірним, особливо якщо об’єкт стикається з іншим під час фізики.

  • Якщо передбачення було неправильним, об’єкт може екстраполюватися в «неможливе» положення, наприклад, усередині стіни.

  • Якщо швидкість руху повільна, ці неправильні передбачення можуть не становити великої проблеми.

  • Якщо передбачення виявилося неправильним, об’єкт може стрибнути або повернутися на виправлену траєкторію. Це може візуально дратувати.

Виправлена інтерполяція з кроком за часом

У Ґодо всю цю систему називають фізичною інтерполяцією, але ви також можете почути, що її називають "інтерполяцією з фіксованим кроком у часі", оскільки вона інтерполює між об’єктами, які переміщуються з фіксованим кроком у часі (фізичні тики за секунду). У певному сенсі другий термін більш точний, оскільки його також можна використовувати для інтерполяції об’єктів, які не керуються фізикою.

Порада

Хоча фізична інтерполяція зазвичай є хорошим вибором, є винятки, коли ви можете не використовувати вбудовану фізичну інтерполяцію Godot (або використовувати її обмежено). Прикладом категорії є багатокористувацькі інтернет-ігри. Багатокористувацькі ігри часто отримують від інших гравців або сервера інформацію на основі тиків або часу, і вони можуть не збігатися з локальними фізичними тиками, тому спеціальна техніка інтерполяції часто може бути кращою.