Kolizja z piłką - wykrywanie i obsługa

Z pomocą społeczności Stack Overflow napisałem dość prosty, ale zabawny symulator fizyki.

alt text

Klikasz i przeciągasz myszką, aby uruchomić piłkę. Odbije się i ostatecznie zatrzyma się na"podłodze".

Moją kolejną dużą funkcją, którą chcę dodać, jest kolizja między piłką a piłką. Ruch piłki jest podzielony na wektor prędkości X i Y. Mam grawitację (mała redukcja wektora y na każdym kroku), mam tarcie (mała redukcja obu wektorów na każdym kroku zderzenie ze ścianą). Piłki poruszają się w zaskakująco realistyczny sposób.

Moje pytanie ma chyba dwie części:

  1. Jaka jest najlepsza metoda wykrywania kolizji piłki z piłką?
    Czy mam tylko pętlę O (N^2), która powtarza się nad każdą piłką i sprawdza każdą inną piłkę, aby sprawdzić, czy promień się pokrywa?
  2. jakich równań używać do obsługi kolizji piłki z piłką? Fizyka 101
    Jak to wpływa na prędkość dwóch kulek x / y wektory? W jakim kierunku zmierzają obie kulki? Jak zastosować to do każdej piłki?

alt text

Obsługa wykrywania kolizji "ścian" i wynikających z tego zmian wektorowych była łatwa, ale widzę więcej komplikacji przy kolizjach piłka-piłka. Ze ścianami po prostu musiałem wziąć ujemny odpowiedni wektor x lub y i od niego pójdzie we właściwym kierunku. Z jajami chyba tak nie jest.

Kilka szybkich wyjaśnień: dla prostota jestem w porządku z idealnie elastycznym kolizją na razie, również wszystkie moje kulki mają taką samą masę teraz, ale może to zmienię w przyszłości.


Edit: zasoby, które uznałem za przydatne

Fizyka kuli 2d z wektorami: zderzenia 2-wymiarowe bez trygonometrii.pdf
Przykład wykrywania kolizji piłki 2D: dodawanie wykrywania kolizji


Sukces!

Mam wykrywanie kolizji i reagowanie piłki działa świetnie!

Odpowiedni kod:

Wykrywanie Kolizji:

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]);
        }
    }
}

To sprawdza kolizje pomiędzy każdą piłką, ale pomija zbędne kontrole (jeśli musisz sprawdzić, czy piłka 1 zderza się z piłką 2, to nie musisz sprawdzać, czy piłka 2 zderza się z piłką 1. Również pomija sprawdzanie kolizji z samym sobą).

Następnie, w mojej klasie piłki mam moje metody colliding() i resolveCollision ():

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.multiply(i);

    // change in momentum
    this.velocity = this.velocity.add(impulse.multiply(im1));
    ball.velocity = ball.velocity.subtract(impulse.multiply(im2));

}

Kod źródłowy: pełne źródło dla ball to ball Zderzacz.

Jeśli ktoś ma jakieś sugestie jak ulepszyć ten podstawowy symulator fizyki daj znać! Jedną rzeczą, którą muszę jeszcze dodać, jest kątowy pęd, więc kulki będą toczyć się bardziej realistycznie. Jakieś inne sugestie? Dodaj komentarz!

Author: mmcdole, 2008-12-06

12 answers

Aby wykryć, czy dwie kulki zderzają się ze sobą, wystarczy sprawdzić, czy odległość między ich ośrodkami jest mniejsza niż dwa razy większa od promienia. Aby wykonać idealnie elastyczną kolizję między kulkami, musisz tylko martwić się o składnik prędkości, który znajduje się w kierunku kolizji. Drugi składnik (styczny do kolizji) pozostanie taki sam dla obu kulek. Elementy zderzenia można uzyskać, tworząc wektor jednostkowy skierowany w kierunku od jednej kuli do drugiej, a następnie biorąc produkt punktowy z wektorami prędkości kulek. Następnie można podłączyć te komponenty do równania kolizji 1D.

Wikipedia ma całkiem dobrepodsumowanie całego procesu . Dla kul o dowolnej masie nowe prędkości można obliczyć za pomocą równań (gdzie v1 i v2 są prędkościami po zderzeniu, a u1, u2 są przed):

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}}

Jeśli kulki mają taką samą masę To prędkości są po prostu zamienione. Oto jakiś kod, który napisałem, który robi coś podobnego:

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's 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;
}
Jeśli chodzi o wydajność, Ryan Fox ma rację, powinieneś rozważyć podzielenie regionu na sekcje, a następnie wykrycie kolizji w każdej sekcji. Pamiętaj, że kulki mogą zderzać się z innymi kulkami na granicach sekcji, więc może to znacznie skomplikować Twój kod. Wydajność prawdopodobnie nie będzie miała znaczenia, dopóki nie będziesz miał kilkuset piłek. Za punkty bonusowe można uruchomić każdą sekcję na inny rdzeń lub podzielić przetwarzanie kolizji w ramach każdej sekcji.
 106
Author: Jay Conrod,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-08 14:09:20

Cóż, lata temu stworzyłem program tak, jak prezentowałeś tutaj.
Jest jeden ukryty problem (lub wiele, zależy od punktu widzenia):

  • Jeśli prędkość piłki jest zbyt wysoko, możesz przegapić kolizję.

A także, prawie w 100% przypadków twoje nowe prędkości będą złe. Cóż, nie Prędkości , ale pozycje . Musisz obliczyć nowe prędkości dokładnie we właściwym miejscu. W przeciwnym razie po prostu przesuń kulki na jakąś małą" błąd " kwotę, która jest dostępne od poprzedniego dyskretnego kroku.

Rozwiązanie jest oczywiste: musisz podzielić timestep tak, że najpierw przesuniesz się w odpowiednie miejsce, następnie zderzysz się, a następnie przesuniesz na resztę czasu, który masz.

 46
Author: avp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-19 09:44:04

Powinieneś użyć partycjonowania spacji, aby rozwiązać ten problem.

Czytaj dalej Binarne Partycjonowanie Przestrzeni oraz Quadtrees

 20
Author: grepsedawk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-06 03:43:14

Jako wyjaśnienie sugestii Ryana Foxa, aby podzielić ekran na regiony i sprawdzać tylko kolizje w regionach...

Np. podziel pole gry na siatkę kwadratów (które arbitralnie powiedzą, że mają 1 jednostkę długości na stronę) i sprawdź, czy w każdym kwadracie nie ma kolizji.

To absolutnie poprawne rozwiązanie. Jedynym problemem z nim (jak zauważył inny plakat) jest to, że kolizje poza granicami są problemem.

The rozwiązaniem jest nałożenie drugiej siatki na 0,5 jednostki przesunięcia pionowego i poziomego do pierwszej.

Wtedy wszelkie kolizje, które byłyby poza granicami w pierwszej siatce (a więc nie wykryte), będą w obrębie kwadratów siatki w drugiej siatce. Tak długo, jak będziesz śledzić kolizje, z którymi już się uporałeś (ponieważ prawdopodobnie pojawią się pewne nakładki), nie musisz się martwić o obsługę przypadków krawędziowych. Wszystkie kolizje będą w kwadracie siatki na jednej z siatek.

 12
Author: Andrew Rollings,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-06 04:21:33

Dobrym sposobem na zmniejszenie liczby kontroli kolizji jest podzielenie ekranu na różne sekcje. Następnie porównujesz tylko każdą piłkę z piłkami w tej samej sekcji.

 10
Author: Ryan Fox,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-06 03:39:25

Jedna rzecz, którą widzę tutaj, aby zoptymalizować.

Chociaż zgadzam się, że kule trafione, gdy odległość jest sumą ich promieni, nigdy nie należy obliczać tej odległości! Raczej Oblicz, że jest kwadratowy i pracuj z nim w ten sposób. Nie ma powodu do tej kosztownej operacji pierwiastka kwadratowego.

Ponadto, po znalezieniu kolizji musisz kontynuować ocenę kolizji, dopóki nie zostanie już więcej. Problem polega na tym, że pierwszy z nich może spowodować inne, które muszą być rozwiązane zanim uzyskasz dokładny obraz. Zastanów się, co się stanie, jeśli piłka uderzy piłkę na krawędzi? Druga piłka uderza w krawędź i natychmiast odbija do pierwszej piłki. Jeśli uderzysz w stertę piłek w rogu, możesz mieć sporo kolizji, które muszą zostać rozwiązane, zanim będziesz mógł powtórzyć następny cykl.

Co do O (n^2), wszystko co możesz zrobić to zminimalizować koszty odrzucenia tych, które nie trafią:

1) piłka, która się nie porusza, nie może w nic trafić. Jeśli istnieją rozsądna liczba piłek leżących na podłodze może zaoszczędzić wiele testów. (Pamiętaj, że musisz jeszcze sprawdzić, czy coś uderzyło w nieruchomą piłkę.)

2) coś, co może być warte zrobienia: podziel ekran na kilka stref, ale linie powinny być rozmyte-kulki na krawędzi strefy są wymienione jako znajdujące się we wszystkich odpowiednich (może być 4) strefach. Użyłbym siatki 4x4, przechowując strefy jako bity. Jeśli i strefy dwóch stref kulek zwróci zero, koniec test.

3) Jak wspomniałem, nie rób pierwiastka kwadratowego.

 7
Author: Loren Pechtel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-06 05:45:27

Znalazłem doskonałą stronę z informacjami na temat wykrywania kolizji i reakcji w 2D.

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

Próbują wyjaśnić, jak to się robi z akademickiego punktu widzenia. Zaczynają się od prostego wykrywania kolizji obiekt-obiekt, a następnie przechodzą do odpowiedzi na kolizje i skalowania jej.

Edit: Updated link

 6
Author: Markus Jarderot,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-16 19:15:05

Masz na to dwa proste sposoby. Jay pokrył dokładny sposób sprawdzania od środka piłki.

Łatwiejszym sposobem jest użycie prostokątnej obwiedni, ustawienie rozmiaru pudełka na 80% wielkości piłki, a będziesz symulować kolizję całkiem dobrze.

Dodaj metodę do klasy piłki:

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);
}

Następnie w pętli:

// 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
Author: FlySwat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-06 15:37:51

Widzę to tu i ówdzie, ale możesz też najpierw wykonać szybsze obliczenia, np. porównać obwiedni dla nakładania się, a następnie wykonać nakładanie na Promień, jeśli pierwszy test przejdzie.

Matematyka dodawania/różnicy jest znacznie szybsza dla obwiedni niż wszystkie Tryg dla promienia, a najczęściej test obwiedni odrzuca możliwość kolizji. Ale jeśli następnie ponownie przetestować z Tryg, otrzymujesz dokładne wyniki, które szukasz.

Tak, to dwa testy, ale ogólnie będzie szybciej.

 3
Author: Jason Kleban,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-13 16:57:44

To KineticModel jest implementacją cytowanego podejścia w Javie.

 3
Author: trashgod,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-02 17:29:59

Zaimplementowałem ten kod w JavaScript za pomocą elementu HTML Canvas, który produkował wspaniałe symulacje z prędkością 60 klatek na sekundę. Zacząłem symulację od kolekcji kilkunastu kulek w losowych pozycjach i prędkościach. Okazało się, że przy wyższych prędkościach, kolizja między małą piłką a znacznie większą spowodowała, że mała piłka wydawała się trzymać do krawędzi większej piłki i przesunęła się do około 90 stopni wokół większej piłki przed rozdzieleniem. (Ciekawe czy ktoś jeszcze zaobserwował takie zachowanie.)

Niektóre zapisy obliczeń wykazały, że minimalna odległość tłumaczenia w tych przypadkach nie była wystarczająco duża, aby zapobiec zderzeniu tych samych kulek w następnym kroku czasowym. Zrobiłem kilka eksperymentów i okazało się, że mogę rozwiązać ten problem, skalując MTD w oparciu o względne prędkości: {]}

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);
Zweryfikowałem, że przed i po tej naprawie, całkowita energia kinetyczna była zachowana dla każdego zderzenia. 0.5 wartość w mtd_factor była w przybliżeniu wartością minumum, która zawsze powodowała oddzielenie kulek po kolizji.

Chociaż ta poprawka wprowadza niewielką ilość błędów w dokładnej fizyce systemu, kompromis polega na tym, że teraz bardzo szybkie kulki mogą być symulowane w przeglądarce bez zmniejszania rozmiaru kroku czasowego.

 2
Author: Stefan Musarra,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-06 00:53:26

Jak widzę tutaj, nie wspomniano o lepszym sposobie jego realizacji. Dynamika molekularna napędzana zdarzeniami odsyłam do "jak symulować bilard i podobne systemy" Borysa D. Lubaczewskiego, dostępnego na arxiv: http://arxiv.org/abs/cond-mat/0503627 na załączonym obrazku jest zrzut ekranu programu, który zamierzam zrobić open source, kiedy go skończę. Nawet na wczesnym etapie działa z 5000 kul całkiem płynnie. Mam nadzieję, że będzie jeszcze lepiej, chociaż nie chcę wdrażać sektorowania, I chcesz, aby Kod był łatwy do zrozumienia. Opis będzie dostępny na stronie http://compphys.go.ro

Później edycja: kod jest już dostępny na Githubie: https://github.com/aromanro/EventMolecularDynamics opis jest na blogu: http://compphys.go.ro/event-driven-molecular-dynamics/

 2
Author: Adrian Roman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-30 18:16:13