Виявлення зіткнення з кривими


12

Я працюю над 2D грою, в якій мені хотілося б виявити зіткнення між рухомим колом і деякими статичними кривими (можливо, криві Безьє).

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

Як я можу зробити подібне виявлення зіткнення відносно прямо? Я знаю, наприклад, що Box2D має функції виявлення зіткнень з кривими Безьє. Мені не потрібен повнофункціональний механізм виявлення зіткнень, просто те, що може зробити те, що я описав.


ОНОВЛЕННЯ: Дякую за чудові відповіді! Мені доведеться прочитати криві Безьє, щоб повністю зрозуміти описаний вами метод. Тоді я повернуся до вас.

Відповіді:


6

29.09.2012 - 23:20

Я створив git Repo тут: https://github.com/ArthurWulfWhite/Bezier-Distance/

Ви можете завантажити вихідні файли у вигляді поштового індексу. Сюди також входить демонстрація, яку можна скласти за допомогою FlashDevelop. Щоб використовувати демонстраційну версію, відкрийте проект у програмі Flash Developer та натисніть «Тестувати проект». Під час запуску демонстрації натисніть на LMB, щоб рандомізувати нову криву Безьє та нове коло.

Удачі!

Посилання на zip важко помітити - просто використовуйте Ctrl + F і введіть zip. Це джерело представляє пару тижнів дослідження та програмування, сподіваюся, вам сподобається.


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

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

Стара відповідь: Чистий шлях.

Ви дійсно можете побачити, чи коло стикається з кривою Безьє, перевіривши відстань між відстань між центром кола та найближчою точкою на кривій.

Рівняння відстані (загалом)

пояснив:

Рівняння Безьє:

q(t) = (1-t) * ((1-t) * start.(x,y) + t * control.(x,y)) + t*(t * control.(x,y) + (1 - t) * end.(x,y))

Це можна підсумувати (з деякою алгеброю) - я пропущу. (X, y) для читабельності (це все-таки точки, а не одне число)

q(t) = (start -2 * cont + end) t^2 + (-2 * start + 2 * control) + start

Відстань від точки (x, y) дорівнює:

sqrt ((q(t).x - point.x)^2 + (q(t).y - point.y)^2)

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

Похідна, для якої потрібно знайти корені:

Припустимо: a = старт b = контроль c = кінець d = центральна точка кола

Похідне з використанням вольфрам альфа

Хитра частина - це множення цих точок, ви повинні використовувати крапковий продукт.

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

Я рекомендую поки уникати цього, просто підсумуйте коефіцієнти для осі x та осі y та додайте їх.

Використовуйте будь-який надійний метод, який ви можете обрати, як Ньютон, щоб знайти корені, перевірте відстань від кореневих точок на безьє, 0 <= t <= 1 до центру кола і перевірте відстань для двох кінців безьє (початок і кінець) до центру кола, який із найближчих, скаже вам, чи має місце зіткнення.

Якщо радіус менший, ніж мінімальна відстань, відбувається зіткнення.

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

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

    q(t) = (1-t) * ((1-t) * start.(x,y) + t * control.(x,y)) + t*(t * control.(x,y) + (1 - t) * end.(x,y))

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


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

Скільки предметів у вашій грі? Скільки може зіткнутися один з одним? Іноді використання двигуна «Фізика» може принести великі переваги, як би точно розрахувати час зіткнення. (бо кадри дискретні, а зіткнення справжні (не трапляються точно, коли ви
рендеруєте

Часто, ніж несподівані виклики, коли реалізуєш щось нове та краса використання 2d фізики api, полягає в тому, що це подібно до використання будь-якої мови програмування, вона не вимагає особливих зусиль з вашого боку, крім вкладення пари годин для її вивчення та результати дуже задовільні.
AturSams

Я додав ще кілька деталей прямо зараз, удачі. :)
AturSams

Я створюю просту гру типу Elasto Mania. Лише три рухомих кола та статична геометрія. Весь двигун закінчений і працює чудово. Єдине, що залишилося - це дозволити криві, які я збираюся вирішити atm завдяки допомозі у цій відповіді :) Не соромтеся розмістити згаданий вами код. Наскільки доцільним, на вашу думку, було б користуватися в реальному житті? Краще, ніж перетворити безьє на крихітні лінії?
paldepind

7

Для цього я:

  • Розбийте криву Безьє на сегменти рядків декількох і зберігайте їх.

  • Помістіть усі ці відрізки в обмежувальний вісь, орієнтований на вісь, для всієї кривої.

Виявлення зіткнення:

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

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

EDIT: якщо вам потрібна висока точність і ви хочете отримати хороші показники, ви також можете створити основне обмежувальне поле для всієї кривої, а потім розділити криву на два сегменти (наприклад: [0.0 - 0.5]і [0.5 - 1.0]). Створення bouding вікна для кожного з них, а потім знову розділити кожен з цих сегментів в двох сегментах (таким чином , даючи [0 - 0.25], [0.25 - 0.5]і [0.5 - 0.75], [0.75 - 1.0]). Продовжуйте так, поки не досягнете достатньої точності. врешті-решт, у вас з'явиться binary treeобмежувальний ящик з основним обмежувальним полем кривої в кореневих та лінійних сегментах на листках. пошук у дереві дасть вам O(log n)замість O(n)(де n= кількість відрізків рядка для кривої)


Це рішення має сенс для мене, і, безумовно, найлегше зрозуміти, і я можу з цим погодитися. Але мені цікаво, чи існує більш "чистий" варіант.
paldepind

5

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

Ця стаття висвітлює його до точки: http://students.cs.byu.edu/~tom/557/text/cic.pdf .

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

Аналогічно, ви можете перевірити коло та багатокутник кожної такої безьє дуги, коли поділите дугу Безьє на дві піддуги. Коло повинно перетинати контрольний багатокутник дуги, щоб тест на криву в коло мав сенс.


Я ще не читав статті. Але як мені дістатися від перетину між прямою та кривою Безьє до перетину між колом та Безьє? Перевірка зіткнення проти кола та багатокутника звучить для мене трохи надто складно.
paldepind
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.