Болт-коллаж - обнаружение и обработка

С помощью сообщества Qaru я написал довольно простой, но увлекательный симулятор физики.

alt text

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

Моя следующая большая особенность, которую я хочу добавить, это коллизия мяча с мячом. Движение мяча разбито на топор и вектор скорости y. У меня есть сила тяжести (небольшое уменьшение вектора y на каждом шаге), у меня есть трение (небольшое уменьшение обоих векторов при каждом столкновении со стеной). Мячи честно перемещаются удивительно реалистичным способом.

Я думаю, мой вопрос состоит из двух частей:

  1. Каков наилучший метод обнаружения столкновения шара с мячом?
    У меня просто есть петля O (n ^ 2), которая перебирает каждый шар и проверяет каждый другой шар, чтобы увидеть, перекрывается ли его радиус?
  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));

}

Исходный код: полный исходный код для коллайдера.

Если у кого-то есть предложения по улучшению этого базового симулятора физики, дайте мне знать! Одна вещь, которую я должен добавить, - это момент импульса, чтобы шарики катились более реалистично. Любые другие предложения? Оставить комментарий!

254
задан 06 дек. '08 в 6:24
источник поделиться
12 ответов

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

В Википедии есть довольно хорошая сводка всего процесса. Для шаров любой массы новые скорости могут быть вычислены с использованием уравнений (где 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;
}

Что касается эффективности, Райан Фокс прав, вы должны рассмотреть вопрос о разделении региона на разделы, а затем выполнить обнаружение столкновений в каждом разделе. Имейте в виду, что шары могут сталкиваться с другими шарами на границах раздела, поэтому это может сделать ваш код намного сложнее. Эффективность, вероятно, не будет иметь значения, пока у вас не будет несколько сотен мячей. Для бонусных очков вы можете запускать каждый раздел на другом ядре или разбивать обработку столкновений в каждом разделе.

110
ответ дан 06 дек. '08 в 6:50
источник

Хорошо, много лет назад я сделал программу, как вы здесь. Существует одна скрытая проблема (или многие, зависит от точки зрения):

  • Если скорость мяча тоже высоко, вы можете пропустить столкновение.

А также почти в 100% случаях ваши новые скорости будут неправильными. Ну, не скорости, а позиции. Вы должны рассчитывать новые скорости точно в нужном месте. В противном случае вы просто сбрасываете шары на небольшое количество "ошибок", которое доступно на предыдущем дискретном шаге.

Решение очевиден: вам нужно разделить временную метку так, чтобы сначала вы переместились в правильное место, затем столкнулись, а затем переместились на оставшееся время.

47
ответ дан 19 дек. '08 в 12:44
источник

Для решения этой проблемы необходимо использовать разметку пространства.

Читайте дальше Двоичное пространственное разделение а также Квадраты

20
ответ дан 06 дек. '08 в 6:43
источник

В качестве пояснения к предложению Райана Фокса разделить экран на регионы и проверить только столкновения внутри регионов...

например. разделить зону воспроизведения вверх на сетку квадратов (которая будет произвольно произносить 1 единицу длины на каждую сторону) и проверить наличие столкновений внутри каждого квадрата сетки.

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

Решением этого является наложение второй сетки с вертикальным и горизонтальным смещением на 0,5 единицы к первому.

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

12
ответ дан 06 дек. '08 в 7:21
источник

Хорошим способом сокращения числа проверок столкновения является разделение экрана на разные разделы. Затем вы сравниваете каждый шар с шарами в том же разделе.

10
ответ дан 06 дек. '08 в 6:39
источник

Одна вещь, которую я вижу здесь, чтобы оптимизировать.

Пока я согласен, что шары попадают, когда расстояние является суммой их радиусов, никогда не следует рассчитывать на это расстояние! Скорее, вычислите его квадрат и работайте с ним таким образом. Нет причин для этой дорогостоящей операции с квадратным корнем.

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

Что касается O (n ^ 2), все, что вы можете сделать, это свести к минимуму затраты на отказ от пропущенных:

1) Мяч, который не движется, не может ничего ударить. Если на полу есть разумное количество мячей, это может сэкономить много испытаний. (Обратите внимание, что вы все равно должны проверить, попало ли что-то в неподвижный мяч.)

2) Что-то, что может стоить сделать: Разделите экран на несколько зон, но линии должны быть нечеткими - шары на краю зоны перечислены как находящиеся во всех соответствующих (может быть 4) зонах. Я бы использовал сетку 4x4, сохраняя зоны как биты. Если И из зон двух зон шариков возвращает ноль, конец теста.

3) Как я уже говорил, не делайте квадратный корень.

7
ответ дан 06 дек. '08 в 8:45
источник

Я нашел отличную страницу с информацией об обнаружении и ответе на столкновение в 2D.

http://www.metanetsoftware.com/technique.html

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

Изменить: Обновлена ​​ссылка

6
ответ дан 12 дек. '08 в 1:46
источник

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

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

Да, это два теста, но они будут быстрее в целом.

3
ответ дан 13 мая '10 в 19:57
источник

У вас есть два простых способа сделать это. Джей закрыл точный способ проверки из центра мяча.

Более простой способ - использовать прямоугольный ограничивающий прямоугольник, установить размер вашего поля на 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]
            }
        }
    }
}
3
ответ дан 06 дек. '08 в 8:03
источник

Этот KineticModel является реализацией цитированного в Java.

3
ответ дан 22 июля '11 в 22:05
источник

Я реализовал этот код в 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 было примерно равным минимальному значению, которое всегда заставляло шары разделиться после столкновения.

Хотя это исправление вводит небольшую ошибку в точной физике системы, компромисс заключается в том, что теперь очень быстрые шары могут быть смоделированы в браузере без уменьшения размера временного шага.

2
ответ дан 06 окт. '13 в 2:32
источник

Как я вижу здесь, лучший способ его реализации не упоминается. Молекулярная динамика, управляемая событиями Я расскажу вам "Как имитировать бильярд и Похожие системы" Бориса Д. Любачевского, доступного по 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/

2
ответ дан 03 авг. '16 в 23:38
источник

Другие вопросы по меткам или Задайте вопрос