Students Save 30%! Learn & create with unlimited courses & creative assets Students Save 30%! Save Now
Advertisement
  1. Game Development
  2. Game Physics
Gamedevelopment

Як створити користувальницький 2D-фізичний движок: Основи та імпульсну роздільну здатність

by
Difficulty:IntermediateLength:LongLanguages:
This post is part of a series called How to Create a Custom Physics Engine.
How to Create a Custom 2D Physics Engine: The Core Engine

Ukrainian (українська мова) translation by Tanya Ira (you can also view the original English article)

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

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

До кінця цього підручника у наступних розділах будуть покриті, в двох вимірах:

  •  Просте виявлення зіткнень
  • Простий колектор покоління
  •  Імпульсна роздільна здатність

Ось короткий демо:

Примітка: хоча цей підручник написаний на C++, ви повинні бути в змозі використовувати ті ж методи і концепції практично в будь-якому середовищі розробки.


Передумови

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

  • Базове розуміння простої векторної математики
  • Уміння виконувати алгебраїчні математика

Виявлення Зіткнень

 Є чимало статей і посібників по всьому інтернету, в тому числі і тут на Tuts+, які охоплюють виявлення зіткнень.  Знаючи це, я хотів би пробігтися по темі дуже швидко, так як ця ділянка не є темою цієї статті.

Осі Вирівняні Обмежують Прямокутників

 Осі вирівняні прямокутника (ААВВ) - це вікно, яке має чотири осі поєднана з системою координат, в якій він проживає.  Це означає, що це поле, яке не може обертатися, і завжди квадрат на 90 градусів (зазвичай вирівнюється з екрану).  В цілому це називається "прямокутник", тому що AABBs використовуються для пов'язані інші, більш складні форми.

An example AABB

Приклад ААВВ.

 ААВВ складної форми можна використовувати як простий тест, щоб побачити, якщо більш складні форми всередині AABBs може бути пересічними.  Однак у випадку більшості ігор ААВВ використовується в якості фундаментальної форми, і насправді не пов'язані що-небудь інше. Структура ААВВ важливо.  Є кілька різних способів, щоб представляти ААВВ, однак це моє улюблене:

Ця форма дозволяє ААВВ бути представлені дві точки.  Мінімальна точка являє собою нижню межі X і осі Y, і Макс являє собою верхні межі - іншими словами, вони являють собою верхній лівий і нижній правий кути.  Для того, щоб сказати, чи є два ААВВ форми пересічних необхідно мати базове розуміння поділу Теорема осі (СБ).

 Ось швидкий тест, взятий з реального часу виявлення зіткнень Крістер Еріксон, яка дозволяє використовувати зб.:

Круги

Коло являє собою радіус і крапка.  Ось те, що ваша структура коло повинен виглядати так:

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

 Важливим оптимізації, щоб зробити тут, це позбутися від необхідності використання оператора квадратного кореня:

В цілому розмноження-це набагато дешевше, ніж операція взяття квадратного кореня значення.


Дозвіл Імпульс

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

 В цілому об'єкт у фізичний движок має три ступені свободи (у двох вимірах): рух в площині XY і обертання.  У цій статті ми неявно обмежити обертання і використовувати тільки AABBs та кола, так що єдина ступінь свободи, ми дійсно повинні розглянути рух вздовж площини XY.

Шляхом усунення виявлених колізій ми встановлюємо обмеження на переміщення таких об'єктів не може залишатися перетинаються. Ідея резолюції імпульс, щоб використовувати імпульс (миттєве зміна швидкості) в об'єкти зіткнення.  Для цього маса, положення і швидкість кожного об'єкта повинні бути прийняті до уваги, як-то: ми хочемо, щоб великі об'єкти стикаються з дрібними трохи рухатися під час зіткнення, і щоб відправити дрібні предмети відлітають.  Ми також хочемо, щоб об'єкти з нескінченною масою, щоб не рухатися взагалі.

Simple example of what impulse resolution can achieve

Простий приклад того, що дозвіл імпульс може досягти.

 Для досягнення таких ефектів і слідкуйте за поряд з природною інтуїцією, як ведуть себе об'єкти ми будемо використовувати тверді тіла і трохи математики.  Тверде тіло-це просто форма, визначених користувачем (тобто вами, забудовника), який неявно певними, щоб не деформуватися.  Обидва AABBs і кола в цій статті, недеформіруемой, і завжди буде або ААВВ або коло. Ніякого стиснення або розтягування допускається.

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

 Наші Об'єкти Зіткнувся - Тепер Що?

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

  • Зіткнення нормально
  • Глибина проникнення

 Для того, щоб застосувати імпульс до об'єктів, але і переміщати їх один від одного, ми повинні знати, в якому напрямку штовхати їх і на скільки. Нормальне зіткнення напрямок, в якому імпульс буде застосовуватися.  Глибина проникнення (разом з деякими іншими речами) визначити, як великі імпульсу буде використовуватися.  Це означає, що єдина цінність, яку потрібно вирішити для масштабів нашого імпульсу.

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

 Рівняння 1

\[ В^{АВ} = V і^Б - В^А \] зауважимо, що для того, щоб створити вектор з позиції a в позицію B, ви повинні зробити: кінцева точка - Вихідна позиція.  \(^{АВ}\) - відносна швидкість з А в Б. Це рівняння має бути виражена в термінах зіткнення нормальний \(н\) - тобто, ми хотіли б знати відносну швидкість від А до Б уздовж лінії зіткнення нормального напрямки:

Рівняння 2

 \[ В^{АВ} \cDOT на Н = (^Б - В^А) \cDOT на п \]

Ми зараз використовуємо скалярний добуток. Скалярний добуток простих; це сума по компонентно продуктів:

 Рівняння 3

 \[ В_1 = завжди \begin{bmatrix}x_1 місця \\y_1\кінець{bmatrix}, В_2 = завжди \begin{bmatrix}x_2 \\y_2\кінець{bmatrix} \\ В_1 \cDOT на В_2 = x_1 місця * x_2 + y_2 * y_2 \]

Наступний крок-ввести так званий коефіцієнт реституції. Реституція-це термін, який означає еластичність, або bounciness. Кожен об'єкт в свій фізичний движок буде реституції представляється як десяткове значення.  Проте тільки одне десяткове значення буде використовуватися при розрахунку імпульсу.

 Щоб вирішити, що реституція використовувати (позначається \Е\) для Епсилон), ви повинні завжди використовувати найнижчою реституції, залучені в конфлікт, для інтуїтивного результати:

 Після того як \Е\) є придбаним, ми можемо розмістити його в нашому вирішуючи рівняння для величини імпульсу.

Закон Ньютона про реституцію говориться наступне:

Рівняння 4

 \[В' = Е * \у]

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

Рівняння 5

 \[ В^{АВ} \cDOT на Н = -Е * ^Б - В^А) \cDOT на п \]

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

Досі так добре.  Тепер ми повинні бути в змозі висловити ці швидкості в той час як під впливом імпульсу. Ось просте рівняння для зміни вектора яким-імпульсу скалярного \(\) уздовж певної \напрям(Н\):

Рівняння 6

 \[ У' = у + J В * Н \]

 Сподіваюся, що наведене вище рівняння має сенс, так як це дуже важливо розуміти.  У нас є одиничний вектор \(Н\), який являє собою напрям.  У нас є скаляр \(\), яка представляє як довго наші \(Н\) вектор буде.  Потім ми додаємо наш масштабується \(Н\) вектор \(\v), щоб привести в \(\).  Це просто додавання одного вектора на інший, і ми можемо використовувати цей невеликий рівняння, щоб застосувати імпульс від одного вектора до іншого.

Є трохи більше роботи, щоб бути зроблено тут. Формально, імпульс визначається як зміна імпульсу. Імпульс-це маса * швидкість.  Знаючи це, ми можемо уявити імпульс як формально визначається так:

Рівняння 7

 \[ Імпульс = маса * швидкість \\ швидкість = \фрац{імпульс}{маса} \отже, у' = у + \фрац{Дж * Н}{маса}\]

Три крапки в маленький трикутник (\(\тому\)) можна читати Як "тому". Він використовується, щоб показати, що справа заздалегідь може бути використаний, щоб зробити висновок, що все, що далі вірно.

Хороший прогрес був досягнутий досі!  Однак ми повинні бути в змозі висловити імпульс через \(\) щодо двох різних об'єктів.  При зіткненні з об'єктом A і B, а зсуваються в протилежному напрямку Б:

Рівняння 8

 \[ В'^У = ^А + \фрац{Дж * Н}{мас^а} \ \ '^У = ^Б - \фрац{Дж * Н}{маса^Б} \]

 Ці два рівняння будуть штовхати від Б вздовж напрямку одиничного вектора \(н\) з імпульсним скаляр (величина \(н\)) \(\).

 Все, що зараз потрібно, це об'єднати рівняння 8 і 5. Наші отримане рівняння буде виглядати так:

Рівняння 9

 \ [ (^А - ^ + \фрац{Дж * Н}{мас^а} + \фрац{Дж * Н}{маса^б}) * н = -е * ^Б - В^А) \cDOT на п \\ \Тому \ \ (^А - ^ + \фрац{Дж * Н}{мас^а} + \фрац{Дж * Н}{маса^Б}) * П + Е * ^Б - В^А) \cDOT на н = 0 \]

Якщо ви пам'ятаєте, первісна мета полягала в тому, щоб ізолювати наші масштаби.  Це тому, що ми знаємо в якому напрямку вирішення конфлікту (передбачається, дане виявлення зіткнень), і залишилося лише вирішити масштаби цього напрямку.  Величина, яка є невідомою в нашому випадку \(\), ми повинні ізолювати \(\) і видаліть його.

Рівняння 10

 \[ (П^Б - В^А) \cDOT на н + з * (\фрац{Дж * Н}{мас^а} + \фрац{Дж * Н}{маса^б}) * н + е * ^Б - В^А) \cDOT на н = 0 \\ \тому \\ (1 + е) (В^Б - В^А) \cDOT на н) + з * (\фрац{Дж * Н}{мас^а} + \фрац{Дж * Н}{маса^Б}) * П = 0 \\ \тому \\ Дж = \фрац{-(1 + е) (В^Б - В^А) \cDOT на Н)}{\ГРП{1}{мас^а} + \ГРП{1}{маса^Б}} \]

 Ух!  Це було трохи математики!  Це зараз, хоча у всьому.  Важливо зауважити, що в остаточному варіанті рівняння 10 ми маємо \(\) зліва (наші масштаби) і все, що праворуч-це все відомо.  Це означає, що ми можемо написати кілька рядків коду, щоб вирішити для нашого імпульсу скалярного \(\).  І хлопчик-це код набагато більш читабельним, ніж математичної нотації!

 Є декілька ключових речей, щоб відзначити в наведеному вище прикладі коду.  Першим ділом перевірка у рядку 10, якщо(VelAlongNormal > 0).  Ця перевірка дуже важлива, вона гарантує, що ви тільки дозволяти колізію, якщо об'єкти рухаються назустріч один одному.

Two objects collide but velocity will separate them next frame Do not resolve this type of collision

Два об'єкта стикаються, але швидкість їх буде відокремлювати наступний кадр. Не дозволяти подібні колізії.

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

 Друге застереження полягає в тому, що зворотній маси обчислюється кілька разів без причини.  Краще всього зберігати ваші зворотних мас у межах кожного об'єкта і попередньо обчислити його один раз:

Багато фізики двигуни насправді не зберігати сирої маси.  Фізика двигуни часто магазин зворотна маси і один інверсний маси. Просто так вийшло, що більшість математичних участю мас у вигляді 1/маса.

 Остання річ, щоб зазначити, що ми грамотно розподілити наші імпульсу скалярного \(\) на два об'єкта.  Ми хочемо, щоб невеликі об'єкти відскакують на великі об'єкти з великою часткою \(\), і великі об'єкти, щоб їх швидкостей змінюються дуже невелику частину \(\).

 Для того щоб зробити це, ви могли б зробити:

 Важливо розуміти, що приведений вище код еквівалентний ResolveCollision() приклад функції раніше. Як вже говорилося, зворотна мас вельми корисні в фізичний движок.

 Тонути Об'єктів

 Якщо ми йдемо далі і використовувати код, який ми досі, об'єкти будуть зіткнутися один з одним і відскакують.  Це чудово, хоча що станеться, якщо один з об'єктів має нескінченну масу?  Ну нам потрібен хороший спосіб, щоб уявити нескінченну масу в нашій моделі.

 Я пропоную використовувати нуль як нескінченну масу - Хоча, якщо ми спробуємо обчислити зворотну маса об'єкта з нуля у нас буде ділення на нуль. Обхідний шлях для цього це зробити наступне при обчисленні зворотних мас:

Нульове значення приведе до правильної викладки при вирішенні імпульсів. Це ще добре. Проблема затоплення об'єктів виникає тоді, коли щось починає тонути в інший об'єкт важкості.  Можливо, щось з низьким реституцію потрапляє в стіну з нескінченною масою і починає тонути.

Це тоне з-за плаваючою помилки точок.  Під час кожного розрахунку плаваючою точкою невеликий плаваючою помилки вводиться через апаратних. (Для отримання додаткової інформації, компанія Google [помилка плаваючою точкою IEEE754].)  З плином часу ця помилка накопичується в позиційну помилку, змушуючи об'єкти занурюватися в один одного.

 Для того, щоб виправити цю помилку вона повинна бути врахована.  Щоб виправити цю позиційну помилку я покажу вам метод, званий лінійної проекції. Лінійна проекція зменшує проникнення двох об'єктів на невеликий відсоток, і це відбувається після застосування імпульсу.  Позиційна корекція дуже проста: переміщення кожного об'єкта уздовж лінії зіткнення нормальний \(н\) у відсотках від глибини проникнення:

Зверніть увагу, що ми масштаб penetrationDepth загальна маса системи. Це дасть позиційної корекції пропорційно до того, скільки мас Ми маємо справу. Дрібні предмети відштовхують швидше, ніж більш важкі предмети.

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

 Це дозволяє об'єктам проникнути дуже небагато без корекції положення в ногами.


 Простий Колектор Покоління

 Останні теми в даній статті, є простий колектор покоління.  Колектор в математичному плані-це щось вздовж ліній "колекцію очок, яка являє собою область в просторі".  Однак, коли я посилаюся на цей термін колектор я маю на увазі невеликий об'єкт, який містить інформацію про зіткнення між двома об'єктами.

 Ось типовий колектор установки:

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

 Коло кола проти

 Давайте почнемо з алгоритму найпростішої зіткнення: коло проти кола.  Цей тест в основному тривіальними. Чи Можете ви уявити, що в напрямку вирішення конфлікту буде?  Це вектор від точки А в точку Б. Це коло може бути отриманий шляхом вирахування установки Б У в.

 Глибина проникнення пов'язана з кіл радіусів і відстані один від одного.  Перекриття кіл може бути обчислено шляхом віднімання сума радіусів на відстань від кожного об'єкта.

 Ось повний приклад алгоритму колектор для генерації кола проти зіткнення колі:

 Найбільш помітні речі тут: ми не виконуєте якісь квадратні корені, поки це необхідно (предмети виявляються зіткнення), і ми перевіряємо, щоб переконатися, що кола не в тому ж положенні.  Якщо вони знаходяться на тій же позиції, наша дистанція буде дорівнює нулю, і ми повинні уникнути ділення на нуль при обчисленні т / добу.

ААВВ Ст. ААВВ

 ААВВ в ААВВ тест є трохи більш складним, ніж коло проти кола.  Зіткнення нормального не буде вектор з A в B, але буде особа нормальне. У ААВВ-це коробка з чотирма особами. Кожна особа має нормальний.  Це нормально являє собою одиничний вектор, який перпендикулярний до лиця.

 Вивчити загальне рівняння лінії в 2D:

 \[ ах + ьу + з = 0 \\ нормальний = завжди \begin{bmatrix} \а\б\кінець{bmatrix} \]

custom-physics-line2d

 У наведеному вище рівнянні, A і B-вектор нормалі до лінії, а вектор (А, B) передбачається нормованої (довжина вектора дорівнює нулю).  Знову ж таки, наші зіткнення нормальне (напрям вирішення конфлікту) буде в напрямку одного з нормалей до граней.

Ви знаєте, що c являє собою загальне рівняння лінії?  з-відстань від джерела.  Це дуже корисно для тестування, щоб побачити, чи точка знаходиться на одній стороні рядка чи іншої, як ви побачите в наступній статті

Тепер все, що необхідно, щоб з'ясувати, які особи зустрічних на одному з об'єктів з іншим об'єктом, і у нас нормальні.  Однак іноді кілька граней двох AABBs можуть перетинатися, наприклад, двох кутів перетинаються один з одним.  Це означає, що ми повинні знайти вісь найменшого проникнення.

Two axes of penetration the horizontal x axis is axis of least penetration and this collision should be resolved along the x axis

 Дві осі проходки; горизонтальна вісь є віссю X принаймні проникнення і ця колізія повинна вирішуватися уздовж осі X.

 Ось повний алгоритм ААВВ в ААВВ колектор покоління і виявлення зіткнень:

custom-physics-aabb-diagram

 Коло проти ААВВ

 Останній тест я закрию тест коло проти ААВВ.  Ідея тут полягає в тому, щоб обчислити найближчу точку на ААВВ кола; звідти тест переходить у щось схоже на коло проти тіста круг.  Як тільки найближча точка обчислюється і зіткнення виявляється нормальний напрямку найближчої точки до центра кола.  Глибина проникнення-це різниця між відстанню від найближчої точки на колі і радіус кола.

AABB to Circle intersection diagram
 ААВВ з круговою діаграмою перетину.

 Є один хитрий особливий випадок; якщо центр кола знаходиться в межах ААВВ потім в центр кола повинен бути закріплений до найближчої кромці ААВВ, а нормальна повинна бути перевернута.


 Висновок

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

  • Контактної пари сортування та вибраковування
  • Broadphase
  •  Нашаровувати
  • Інтеграція
  • Timestepping
  •  Перетин півпростір
  • Модульна конструкція (матеріали, маси та сили)

 Я сподіваюся, вам сподобалася ця стаття і я з нетерпінням чекаю відповідей на питання в коментарях.

Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.