Обнаружение столкновений с кривыми

12

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

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

Как я могу сделать такое обнаружение столкновений относительно простым способом? Я знаю, например, что Box2D имеет обнаружение столкновений с кривыми Безье. Мне не нужен полнофункциональный механизм обнаружения столкновений, просто что-то, что может сделать то, что я описал.


ОБНОВЛЕНИЕ: Большое спасибо за отличные ответы! Мне придется прочитать кривые Безье, чтобы полностью понять метод, который вы описали. Тогда я вернусь к тебе.

paldepind
источник

Ответы:

6

29.09.2012 - 23:20

Я создал репо git здесь: https://github.com/ArthurWulfWhite/Bezier-Distance/

Вы можете скачать исходные файлы в виде zip оттуда. Он также включает в себя демонстрацию, которую вы можете скомпилировать с помощью FlashDevelop. Чтобы использовать демо, откройте проект во Flash Develop и нажмите «Test Project». Во время запуска демонстрации нажмите ЛКМ, чтобы рандомизировать новую кривую Безье и новый Круг.

Удачи!

Ссылку zip трудно увидеть - просто используйте Ctrl + F и введите zip. Этот источник представляет собой пару недель исследований и программирования, надеюсь, вам понравится.


Если вы планируете рекурсивно делить Безье на сегменты и проверять наличие с ними столкновений, я предлагаю создать массив 100 100 (сетку) и поместить каждый сегмент в четыре ближайших квадрата, так что вам нужно только проверять наличие столкновений с 4/10 000 из сегментирует каждый кадр.

Я действительно думаю, что 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)

Чтобы найти ближайшую точку на безье к мячу, вам нужно вывести и найти все точки, где производная равна нулю (корни). Это многочлен третьей степени, поэтому вы можете использовать закрытую формулу, но это может быть ненадежно, поскольку точность представленных на компьютере дробей с плавающей запятой может быть недостаточной. Гораздо лучше использовать Ньютона или что-то в этом роде.

Производная, для которой нужно найти корни:

Предполагая: 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 зарекомендовало себя в бесчисленных проектах в прошлом.

AturSams
источник
Метод вычисления кратчайшей точки кривой, который вы описываете, является именно тем, который я сейчас использую с линиями вместо кривых. Но делать то же самое для кривых с помощью метода, который вы объясняете, звучит слишком сложно. Что, как я понимаю, тоже то, что вы думаете. И что касается Box2D. Я уверен, что это отличная работа. Но физика в моей игре, честно говоря, очень проста, и поэтому я решил, что полноценный физический движок излишний.
paldepind
Сколько объектов в вашей игре? Сколько может столкнуться друг с другом? Иногда использование физического движка может дать большие преимущества, например, точное вычисление времени столкновения. (причина в том, что кадры дискретны, а столкновения реальны (не происходит точно при рендеринге кадра)
AturSams
Часто, когда возникают неожиданные проблемы при реализации чего-то нового и прелесть использования 2-го физического API, заключается в том, что это похоже на использование любого языка программирования, это не требует особых усилий с вашей стороны, кроме как потратить пару часов на его изучение и результаты очень удовлетворительные.
AturSams
Я добавил еще несколько деталей прямо сейчас, удачи. :)
AturSams
Я создаю простую игру Elasto Mania. Всего три движущихся круга и статическая геометрия. Весь двигатель закончен и отлично работает. Осталось только разрешить кривые, которые я собираюсь решить, благодаря помощи в этом ответе :) Не стесняйтесь размещать код, который вы упомянули. Как вы думаете, насколько уместно использовать его в реальной жизни? Лучше, чем преобразовать Безье в крошечные линии?
paldepind
7

Для этого я бы:

  • Разбейте кривую Безье на несколько отрезков и сохраните их.

  • Поместите все эти сегменты в выровненную по оси ограничивающую рамку для всей кривой.

Обнаружение столкновения:

1) проверьте, находится ли сфера внутри основной ограничительной рамки. если нет, нет столкновения.

2) в противном случае проверьте, не сталкивается ли какой-либо из отдельных сегментов, рассчитанных выше, со сферой. См. Статью о пересечении линии и сферы из Википедии .

РЕДАКТИРОВАТЬ: если вам нужна высокая точность и хорошая производительность, вы также можете создать основной ограничивающий прямоугольник для всей кривой, а затем разделить кривую на два сегмента (например: [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= количество отрезков для кривой)

tigrou
источник
Это решение имеет смысл для меня и определенно является самым простым для понимания, и я мог бы с этим согласиться. Но мне любопытно, существует ли более «чистый» вариант.
paldepind
5

Пересечение между линией и кривой Безье достигается математически путем деления кривой. Это означает полагаться на свойство выпуклой оболочки кривой и делить ее на более мелкие дуги с различными управляющими полигонами в стиле «разделяй и властвуй».

Эта статья освещает это до некоторой степени: http://students.cs.byu.edu/~tom/557/text/cic.pdf .

Приятно то, что алгоритм работает с любой линией, вам просто нужно применить жесткое преобразование к кривой, чтобы вы могли рассматривать вашу целевую линию как параллельную оси Ox.

Точно так же вы можете проверить по кругу и многоугольнику каждой такой дуги Безье, когда вы подразделяете дугу Безье на две суб-дуги. Окружность должна пересекать контрольный многоугольник дуги, чтобы тест с кривой на окружность имел смысл.

Габриэль Конрад
источник
Я еще не читал статью. Но как мне добраться от пересечения прямой и кривой Безье до пересечения между кругом и Безье? Проверка столкновения с кругом и многоугольником звучит для меня слишком сложно.
paldepind