Болт-коллаж - обнаружение и обработка
С помощью сообщества Qaru я написал довольно простой, но увлекательный симулятор физики.
![alt text]()
Вы щелкаете мышью и запускаете шар. Он будет подпрыгивать и в конце концов остановится на "полу".
Моя следующая большая особенность, которую я хочу добавить, это коллизия мяча с мячом. Движение мяча разбито на топор и вектор скорости y. У меня есть сила тяжести (небольшое уменьшение вектора y на каждом шаге), у меня есть трение (небольшое уменьшение обоих векторов при каждом столкновении со стеной). Мячи честно перемещаются удивительно реалистичным способом.
Я думаю, мой вопрос состоит из двух частей:
- Каков наилучший метод обнаружения столкновения шара с мячом?
У меня просто есть петля O (n ^ 2), которая перебирает каждый шар и проверяет каждый другой шар, чтобы увидеть, перекрывается ли его радиус? - Какие уравнения я использую, чтобы справиться с столкновениями шара с шаром? Физика 101
Как это влияет на скорость вращения двух шаров по векторам x/y? В каком направлении движутся два мяча? Как я могу применить это к каждому шару?
![alt text]()
Обработка обнаружения столкновений "стенок" и результирующих изменений вектора была легкой, но я вижу больше сложностей с столкновениями шарик-шар. Со стенами мне просто нужно было взять отрицательное значение соответствующего вектора x или y, и оно пошло бы в правильном направлении. С шарами я не думаю, что это так.
Несколько быстрых пояснений: для простоты пока я в порядке с совершенно упругим коллизиям, сейчас все мои шары имеют одинаковую массу, но я могу изменить это в будущем.
Изменение: ресурсы, которые я нашел полезными
Физика двумерного шара с векторами: двумерные столкновения без тригонометрии .pdf
Пример обнаружения столкновения 2d Ball: добавление обнаружения столкновения
Успех!
У меня есть обнаружение столкновения мяча и реакция работает отлично!
Соответствующий код:
Обнаружение столкновения:
for (int i = 0; i < ballCount; i++)
{
for (int j = i + 1; j < ballCount; j++)
{
if (balls[i].colliding(balls[j]))
{
balls[i].resolveCollision(balls[j]);
}
}
}
Это будет проверять наличие столкновений между каждым мячом, но пропустить лишние проверки (если вам нужно проверить, сталкивается ли шар 1 с шаром 2, вам не нужно проверять, сталкивается ли шар 2 с шаром 1. Кроме того, он пропускает проверку на столкновения с самим собой.).
Затем в моем классе Ball у меня есть методы colliding() и resolCollision():
public boolean colliding(Ball ball)
{
float xd = position.getX() - ball.position.getX();
float yd = position.getY() - ball.position.getY();
float sumRadius = getRadius() + ball.getRadius();
float sqrRadius = sumRadius * sumRadius;
float distSqr = (xd * xd) + (yd * yd);
if (distSqr <= sqrRadius)
{
return true;
}
return false;
}
public void resolveCollision(Ball ball)
{
// get the mtd
Vector2d delta = (position.subtract(ball.position));
float d = delta.getLength();
// minimum translation distance to push balls apart after intersecting
Vector2d mtd = delta.multiply(((getRadius() + ball.getRadius())-d)/d);
// resolve intersection --
// inverse mass quantities
float im1 = 1 / getMass();
float im2 = 1 / ball.getMass();
// push-pull them apart based off their mass
position = position.add(mtd.multiply(im1 / (im1 + im2)));
ball.position = ball.position.subtract(mtd.multiply(im2 / (im1 + im2)));
// impact speed
Vector2d v = (this.velocity.subtract(ball.velocity));
float vn = v.dot(mtd.normalize());
// sphere intersecting but moving away from each other already
if (vn > 0.0f) return;
// collision impulse
float i = (-(1.0f + Constants.restitution) * vn) / (im1 + im2);
Vector2d impulse = mtd.normalize().multiply(i);
// change in momentum
this.velocity = this.velocity.add(impulse.multiply(im1));
ball.velocity = ball.velocity.subtract(impulse.multiply(im2));
}
Исходный код: полный исходный код для коллайдера.
Если у кого-то есть предложения по улучшению этого базового симулятора физики, дайте мне знать! Одна вещь, которую я должен добавить, - это момент импульса, чтобы шарики катились более реалистично. Любые другие предложения? Оставить комментарий!
Ответы
Ответ 1
Чтобы определить, сталкиваются ли два шара, просто проверьте, меньше ли расстояние между их центрами меньше радиуса. Чтобы сделать совершенно упругое столкновение между шарами, вам нужно только беспокоиться о компоненте скорости, которая находится в направлении столкновения. Другой компонент (касательный к столкновению) останется неизменным для обоих шаров. Вы можете получить компоненты столкновения, создав единичный вектор, указывающий в направлении от одного шара к другому, а затем возьмем точечный продукт с векторами скорости шаров. Затем вы можете подключить эти компоненты в одномерное упругое уравнение столкновения.
В Википедии есть довольно хорошая сводка всего процесса. Для шаров любой массы новые скорости могут быть вычислены с использованием уравнений (где v1 и v2 - скорости после столкновения, а u1, u2 - из ранее):
![v_{1} = \frac{u_{1}(m_{1}-m_{2})+2m_{2}u_{2}}{m_{1}+m_{2}}]()
![v_{2} = \frac{u_{2}(m_{2}-m_{1})+2m_{1}u_{1}}{m_{1}+m_{2}}]()
Если шары имеют одинаковую массу, то скорости просто переключаются. Вот код, который я написал, который делает что-то подобное:
void Simulation::collide(Storage::Iterator a, Storage::Iterator b)
{
// Check whether there actually was a collision
if (a == b)
return;
Vector collision = a.position() - b.position();
double distance = collision.length();
if (distance == 0.0) { // hack to avoid div by zero
collision = Vector(1.0, 0.0);
distance = 1.0;
}
if (distance > 1.0)
return;
// Get the components of the velocity vectors which are parallel to the collision.
// The perpendicular component remains the same for both fish
collision = collision / distance;
double aci = a.velocity().dot(collision);
double bci = b.velocity().dot(collision);
// Solve for the new velocities using the 1-dimensional elastic collision equations.
// Turns out it really simple when the masses are the same.
double acf = bci;
double bcf = aci;
// Replace the collision velocity components with the new ones
a.velocity() += (acf - aci) * collision;
b.velocity() += (bcf - bci) * collision;
}
Что касается эффективности, Райан Фокс прав, вы должны рассмотреть вопрос о разделении региона на разделы, а затем выполнить обнаружение столкновений в каждом разделе. Имейте в виду, что шары могут сталкиваться с другими шарами на границах раздела, поэтому это может сделать ваш код намного сложнее. Эффективность, вероятно, не будет иметь значения, пока у вас не будет несколько сотен мячей. Для бонусных очков вы можете запускать каждый раздел на другом ядре или разбивать обработку столкновений в каждом разделе.
Ответ 2
Хорошо, много лет назад я сделал программу, как вы здесь.
Существует одна скрытая проблема (или многие, зависит от точки зрения):
- Если скорость мяча тоже
высоко, вы можете пропустить столкновение.
А также почти в 100% случаях ваши новые скорости будут неправильными. Ну, не скорости, а позиции. Вы должны рассчитывать новые скорости точно в нужном месте. В противном случае вы просто сбрасываете шары на небольшое количество "ошибок", которое доступно на предыдущем дискретном шаге.
Решение очевиден: вам нужно разделить временную метку так, чтобы сначала вы переместились в правильное место, затем столкнулись, а затем переместились на оставшееся время.
Ответ 3
Для решения этой проблемы необходимо использовать разметку пространства.
Читайте дальше
Двоичное пространственное разделение
а также
Квадраты
Ответ 4
В качестве пояснения к предложению Райана Фокса разделить экран на регионы и проверить только столкновения внутри регионов...
например. разделить зону воспроизведения вверх на сетку квадратов (которая будет произвольно произносить 1 единицу длины на каждую сторону) и проверить наличие столкновений внутри каждого квадрата сетки.
Это абсолютно правильное решение. Единственная проблема с ним (как указывал еще один плакат) состоит в том, что столкновения между границами являются проблемой.
Решением этого является наложение второй сетки с вертикальным и горизонтальным смещением на 0,5 единицы к первому.
Тогда любые столкновения, которые будут пересекаться через границы в первой сетке (и, следовательно, не обнаружены), будут находиться внутри квадратов сетки во второй сетке. Пока вы отслеживаете столкновения, с которыми вы уже справились (поскольку, вероятно, будет некоторое перекрытие), вам не нужно беспокоиться о том, как обращаться с крайними случаями. Все столкновения будут находиться в квадрате сетки на одной из сеток.
Ответ 5
Хорошим способом сокращения числа проверок столкновения является разделение экрана на разные разделы. Затем вы сравниваете каждый шар с шарами в том же разделе.
Ответ 6
Одна вещь, которую я вижу здесь, чтобы оптимизировать.
Пока я согласен, что шары попадают, когда расстояние является суммой их радиусов, никогда не следует рассчитывать на это расстояние! Скорее, вычислите его квадрат и работайте с ним таким образом. Нет причин для этой дорогостоящей операции с квадратным корнем.
Кроме того, как только вы обнаружили столкновение, вы должны продолжать оценивать столкновения, пока не останется больше. Проблема в том, что первый может заставить других решить проблему, прежде чем вы получите точную картину. Подумайте, что произойдет, если мяч попадает в мяч по краю? Второй мяч попадает в край и сразу же подбирается к первому мячу. Если вы стучите в кучу мячей в углу, вы можете столкнуться с несколькими столкновениями, которые должны быть решены, прежде чем вы сможете повторять следующий цикл.
Что касается O (n ^ 2), все, что вы можете сделать, это свести к минимуму затраты на отказ от пропущенных:
1) Мяч, который не движется, не может ничего ударить. Если на полу есть разумное количество мячей, это может сэкономить много испытаний. (Обратите внимание, что вы все равно должны проверить, попало ли что-то в неподвижный мяч.)
2) Что-то, что может стоить сделать: Разделите экран на несколько зон, но линии должны быть нечеткими - шары на краю зоны перечислены как находящиеся во всех соответствующих (может быть 4) зонах. Я бы использовал сетку 4x4, сохраняя зоны как биты. Если И из зон двух зон шариков возвращает ноль, конец теста.
3) Как я уже говорил, не делайте квадратный корень.
Ответ 7
Я нашел отличную страницу с информацией об обнаружении и ответе на столкновение в 2D.
http://www.metanetsoftware.com/technique.html
Они пытаются объяснить, как это делается с академической точки зрения. Они начинаются с простого обнаружения столкновения объектов с объектом, и переходят к ответу на столкновение и как его масштабировать.
Изменить: Обновлена ссылка
Ответ 8
У вас есть два простых способа сделать это. Джей закрыл точный способ проверки из центра мяча.
Более простой способ - использовать прямоугольный ограничивающий прямоугольник, установить размер вашего поля на 80% по размеру мяча, и вы будете хорошо имитировать столкновение.
Добавьте метод к вашему классу шаров:
public Rectangle getBoundingRect()
{
int ballHeight = (int)Ball.Height * 0.80f;
int ballWidth = (int)Ball.Width * 0.80f;
int x = Ball.X - ballWidth / 2;
int y = Ball.Y - ballHeight / 2;
return new Rectangle(x,y,ballHeight,ballWidth);
}
Затем в вашем цикле:
// Checks every ball against every other ball.
// For best results, split it into quadrants like Ryan suggested.
// I didn't do that for simplicity here.
for (int i = 0; i < balls.count; i++)
{
Rectangle r1 = balls[i].getBoundingRect();
for (int k = 0; k < balls.count; k++)
{
if (balls[i] != balls[k])
{
Rectangle r2 = balls[k].getBoundingRect();
if (r1.Intersects(r2))
{
// balls[i] collided with balls[k]
}
}
}
}
Ответ 9
Я вижу, что он намекнул здесь и там, но сначала вы можете выполнить более быстрый расчет, например, сравнить ограничивающие поля для перекрытия, а THEN выполнить перекрытие по радиусу, если этот первый тест пройдет.
Математика сложения/разности намного быстрее для ограничивающего прямоугольника, чем все тригг для радиуса, и в большинстве случаев тест с ограничивающей рамкой отклоняет возможность столкновения. Но если вы повторите тест с помощью триггера, вы получите точные результаты, которые вы ищете.
Да, это два теста, но они будут быстрее в целом.
Ответ 10
Этот KineticModel
является реализацией цитированного в Java.
Ответ 11
Я реализовал этот код в JavaScript с помощью элемента HTML Canvas, и он произвел замечательные симуляции со скоростью 60 кадров в секунду. Я начал моделирование с помощью коллекции из десятка шариков в случайных положениях и скоростях. Я обнаружил, что при более высоких скоростях кратковременное столкновение между маленьким шаром и намного большим заставляло маленький шар казаться STICK к краю большего шара и перемещался примерно до 90 градусов вокруг большой шар перед отделением. (Интересно, заметил ли кто-нибудь еще такое поведение.)
Некоторые протоколирования вычислений показали, что минимальное количество переводов в этих случаях было недостаточно большим, чтобы предотвратить одновременное слияние шаров на следующем шаге. Я экспериментировал и обнаружил, что могу решить эту проблему, увеличив MTD на основе относительных скоростей:
dot_velocity = ball_1.velocity.dot(ball_2.velocity);
mtd_factor = 1. + 0.5 * Math.abs(dot_velocity * Math.sin(collision_angle));
mtd.multplyScalar(mtd_factor);
Я проверил, что до и после этого исправления полная кинетическая энергия сохранялась для каждого столкновения. Значение 0.5 в mtd_factor было примерно равным минимальному значению, которое всегда заставляло шары разделиться после столкновения.
Хотя это исправление вводит небольшую ошибку в точной физике системы, компромисс заключается в том, что теперь очень быстрые шары могут быть смоделированы в браузере без уменьшения размера временного шага.
Ответ 12
Как я вижу здесь, лучший способ его реализации не упоминается.
Я расскажу вам "Как имитировать бильярд и Похожие системы" Бориса Д. Любачевского, доступного по arxiv: http://arxiv.org/abs/cond-mat/0503627 На прилагаемом рисунке показан снимок экрана программы, которую я намереваюсь сделать с открытым исходным кодом, когда я закончу его. Даже на ранней стадии он работает с 5000 шарами довольно гладко. Надеюсь, это будет еще лучше, хотя я не хочу внедрять секционирование, я хочу, чтобы код был понятен. Описание будет доступно на http://compphys.go.ro
Далее отредактируйте: Код теперь доступен на GitHub: https://github.com/aromanro/EventMolecularDynamics Описание находится в блоге: http://compphys.go.ro/event-driven-molecular-dynamics/