Jak obliczyć reakcję na zderzenie między kulą a płaszczyzną?

9

Próbuję stworzyć prostą grę 3D i muszę ograniczyć gracza w granicach świata gry. Kiedy gracz uderza w boki świata, chcę, żeby statek gracza lekko się odbił.

W efekcie próbuję uwięzić gracza w polu i powstrzymać go przed ucieczką przez boki ...

Zdołałem zdefiniować granice świata gry jako zbiór samolotów, z normami i odległościami od pochodzenia. Gracz ma kulistą sferę ograniczającą i dzięki śledzeniu tej witryny http://www.gamasutra.com/view/feature/3383/simple_intersection_tests_for_games.php udało mi się wykryć kolizje.

Nie mogę teraz dokładnie ustalić, co zrobić, gdy zostanie wykryta kolizja. Najlepsze, co potrafię, to utknąć w samolocie, przejść prosto przez niego lub kilkakrotnie odbić się od niego w naprawdę szybkim tempie.

Zdrowy rozsądek mówi mi, że muszę obliczyć kąt odbicia od płaszczyzny, używając jego normalnej wartości i zastosować ją do prędkości gracza, jednak myślę, że najpierw muszę sprawdzić, czy gracz przeszedł przez płaszczyznę, której nie mogę odrobić.

Piku
źródło

Odpowiedzi:

4

Będziesz musiał przyłożyć impuls do swojego obiektu, co jest natychmiastową zmianą jego prędkości. W prawdziwym świecie potężna siła byłaby przyłożona do obiektu w bardzo krótkim czasie, odwracając jego przyspieszenie i powodując zmianę jego prędkości. Ponieważ jednak pracujemy w dyskretnym świecie, musimy nieco oszukać, aby zasymulować tę nagłą zmianę kierunku. W przypadku kuli i samolotu jest to dość proste. Najbardziej podstawową reakcją na zderzenie jest odzwierciedlenie prędkości kuli wokół normalnej płaszczyzny, a następnie wynikiem jest nowa prędkość kuli. Pseudo-kod wyglądałby mniej więcej tak:

reflected = 2 * plane.normal * (plane.normal * sphere.velocity)
sphere.velocity -= reflected

Stamtąd można dodać tłumienie (pomnożyć przez pewien współczynnik, np. 0,9), aby uwzględnić energię traconą na ciepło lub tarcie. Jeśli chcesz zaangażować prędkość kątową (być może twoja kula się obraca), równania stają się nieco bardziej skomplikowane.

Aby uzyskać więcej informacji, odsyłam do artykułów Chrisa Heckera na temat sztywnej dynamiki ciała . Jeśli nie słyszałeś wcześniej o Chrisie Heckerze, jest on dobrze znany z fizyki gry, a także z pracy nad procesem generowania postaci i animacji w Spore.

kevintodisco
źródło
4
Jest to zasadniczo właściwa droga, jednak obliczenie czasu uderzenia (TOI) może sprawić, że rzeczy będą dokładniejsze, ponieważ liczba klatek na sekundę będzie się zmieniać lub spadać. Wiedza, na podstawie bieżącej prędkości, o tym, jak dawno uderzenie miało miejsce, może pomóc ci obliczyć czas uderzenia, a dzięki temu możesz przesunąć kulę z powrotem do jej pozycji w momencie uderzenia i stamtąd dostosować prędkość. Po dostosowaniu pozycji i prędkości od punktu uderzenia, w momencie uderzenia, poruszasz się wzdłuż nowej prędkości o czas odliczony, aby dostać się do TOI.
Nic Foster
OK, to wydaje się w większości działać, ale to jest trochę ... dziwne. Myślę, że robię to w niewłaściwym punkcie mojego kodu. Czy powinienem zapętlać wszystkie moje obiekty i sprawdzać, czy zderzą się, zanim je przesunę (na podstawie miejsca, w którym będą następna klatka), czy przenieść je, a następnie przetestować pod kątem kolizji?
Piku
@Piku, nie nie wykrywaj, czy się zderzą. Jeśli dojdzie do kolizji, pamiętaj, że istnieje bardzo duża szansa, że ​​oba obiekty nakładają się daleko poza miejsce, w którym miałaby miejsce faktyczna kolizja. Zasadniczo musisz dowiedzieć się, gdzie nastąpiło zderzenie, tak jakbyś miał nieskończoną liczbę klatek na sekundę (czego nie masz) i przenieść obiekt z powrotem do pozycji, w której początkowo miałoby miejsce zderzenie. Jeśli nie rozdzielisz takich obiektów, będziesz stale reagować na tę samą kolizję i obiekt utknie.
Jonathan Dickinson
@Piku i aby to zrobić, obliczamy czas w przeszłości, w którym doszło do kolizji (zwany TOI / czas uderzenia). Kiedy już to osiągniemy, możemy użyć prędkości obiektu, aby przesunąć go do tyłu ( distance = speed * timezwykle z bardzo małą odległością, aby uniknąć błędu), a następnie zaktualizować jego prędkość do wyniku kolizji.
Jonathan Dickinson
@Piku również nie ustalamy, gdzie będziemy w następnej klatce (nigdy nie widziałem tego osobiście), ale generalnie wykonujemy wykrywanie kolizji i reakcję: PO obliczeniu nowej pozycji dla TEJ ramki, ale PRZED stosujemy nową pozycję dla tej ramki.
Jonathan Dickinson
1

F = ma lub a = F / m. Oblicz punkt zderzenia między kulą a płaszczyzną. Zwykle jest to środek Kuli - normalny * promień. Jeśli chcesz uzyskać większą dokładność, oblicz, jak daleko kula przebiła płaszczyznę, i dostosuj swoje obliczenia. Jest to oczywiście w dużej mierze opcjonalne, chyba że chcesz naprawdę dokładnej fizyki. Teraz obliczyć prędkość względną wzdłuż normalnej. W przypadku płaszczyzny statycznej jest to: Vball Dot N. Następnie pomnóż VballDotN przez -1 i pomnóż przez masę. W fizyce na tym etapie pomnożymy to również przez współczynnik restytucji (współczynnik odrzuceń). Pomnóż ten skalar przez N, a będziesz miał swoją siłę.

Podczas dostosowywania Vball ponownie podziel siłę przez masę, a uzyskasz ostateczne przyspieszenie, więc po prostu dodaj to do prędkości i uzyskasz ostateczną prędkość po zderzeniu.

vec3 Vrel = Ball.getVelocity();
float vDotN = Vrel.Dot(CollisionNormal);
vec3 F = -(1.0f+Ball.getRestitution())*vDotN;
F*=Ball.getMass();
Ball.accelerate(F/Ball.getMass());

Ta metoda jest zgodna z formułami reakcji na zderzenie. Jeśli chcesz jeszcze większej dokładności, weź pod uwagę tarcie, które spowoduje obrót piłki, ale nie wiem, czy chcesz tego w swojej grze. W takim przypadku obliczasz siłę styczną:

vec3 Ft = -(Ball.getvelocity()+(vDotN*CollisionNormal));
Ft*=Ball.getKineticFriction()+Wall.getKineticFriction(); //you could fudge these numbers
Ft*=Ball.getMass();
vec3 vec2Centre = Ball.getPosition()-ContactPoint;
vec3 Torque = cross(vec2Centre,Ft);
Ball.AngularAccelerate(Torque/Ball.getMomentofInertia(glm::normalize(Torque)));

Przed zastosowaniem jakichkolwiek efektów liniowych należy obliczyć Ft, w przeciwnym razie tarcie nie będzie dokładne.

Ian Young
źródło
Czy linia 3 nie powinna być vec3 F = -CollisionNormal * (1.0f+Ball.getRestitution())*vDotN;:?
Shital Shah
Rzeczywiście tak, tęskniłem za tą częścią. Dzięki za zwrócenie na to uwagi.
Ian Young,
0

Proponuję najpierw obliczyć odległość od samolotu; a następnie, gdy odległość <= do promienia prowadzi reakcję zderzenia.

Następnie możesz to zmienić, aby obliczyć odległość, a jeśli odległość jest mniejsza niż promień (co oznacza, że ​​obiekt się nakłada), zmień położenie kul, a następnie przeprowadź reakcję na kolizję.

Czerwone niebo
źródło