Come calcolo la risposta alla collisione tra una sfera e un piano?


9

Sto cercando di creare un semplice gioco 3D e ho bisogno di costringere il giocatore entro i limiti del mondo di gioco. Quando il giocatore colpisce i lati del mondo, voglio che la nave del giocatore rimbalzi leggermente.

In effetti sto cercando di intrappolare il giocatore all'interno di una scatola e impedire che fuggano attraverso i lati ...

Sono riuscito a definire i limiti del mondo di gioco come una raccolta di aerei, con normali e distanze dall'origine. Il giocatore ha una sfera di delimitazione sferica e dal seguire questo sito web http://www.gamasutra.com/view/feature/3383/simple_intersection_tests_for_games.php sono riuscito a rilevare le collisioni.

Ora non riesco a capire cosa fare quando viene rilevata una collisione. Il meglio che posso fare è che il giocatore rimanga bloccato nell'aereo, lo attraversi o lo rimbalzi ripetutamente a una velocità molto elevata.

Il buon senso mi dice che devo calcolare l'angolo riflesso fuori dall'aereo, usando il suo normale e applicarlo alla velocità del giocatore, tuttavia penso di dover prima vedere se il giocatore ha attraversato l'aereo che è il punto che non posso allenarsi.

Risposte:


4

Dovrai applicare un impulso al tuo oggetto, che è un cambiamento immediato nella sua velocità. Nel mondo reale, una forza potente verrebbe applicata all'oggetto per un tempo molto breve, invertendo la sua accelerazione e facendo cambiare la sua velocità. Tuttavia, poiché stiamo lavorando in un mondo discreto, dobbiamo ingannare un po 'per simulare questo brusco cambio di direzione. Per una sfera e un piano, è piuttosto semplice. La risposta di base alla collisione è quella di riflettere la velocità della sfera attorno alla normale del piano, e quindi il risultato è la nuova velocità della sfera. Lo pseudo-codice sarebbe simile a questo:

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

Da lì, puoi aggiungere un po 'di smorzamento (moltiplicare per un certo coefficiente, come 0,9) per tenere conto dell'energia persa a causa del calore o dell'attrito. Se vuoi coinvolgere la velocità angolare (forse la tua sfera sta ruotando), le equazioni diventano un po 'più complicate.

Per maggiori informazioni, ti rimando agli articoli di Chris Hecker su Rigid Body Dynamics . Se non hai mai sentito parlare di Chris Hecker prima, è ben noto per la fisica dei giochi e per il suo lavoro sulla generazione e l'animazione procedurali dei personaggi in Spore.


4
Questo è essenzialmente il modo giusto di procedere, tuttavia il calcolo del tempo di impatto (TOI) può rendere le cose più accurate quando i framerate fluttuano o cadono. Sapere, in base alla velocità corrente, quanto tempo fa si è verificato l'impatto può aiutarti a calcolare un tempo di impatto, e usando quello puoi spostare la sfera nella sua posizione al momento dell'impatto e regolare la velocità da lì. Dopo aver regolato la posizione e la velocità dal punto di impatto, al momento dell'impatto, si sposta quindi lungo la nuova velocità per il tempo che è stato sottratto per raggiungere il TOI.
Nic Foster,

OK, questo sembra funzionare principalmente, ma è un po '... strano. Penso che potrei farlo nel punto sbagliato del mio codice. Devo passare in rassegna tutti i miei oggetti e testare se si scontreranno prima di spostarli (in base a dove saranno il prossimo fotogramma) o spostarli e quindi testare le collisioni in seguito?
Piku,

@Piku, no non rilevare se si scontreranno. Se si verifica una collisione, ricordati che esiste una buona probabilità che i due oggetti si sovrappongano molto oltre il punto in cui si sarebbe verificata la collisione effettiva. In sostanza, è necessario capire dove si è verificata la collisione come se si avesse un framerate infinito (cosa che non si fa) e spostare l'oggetto nella posizione in cui si sarebbe inizialmente verificata la collisione. Se non separi gli oggetti in questo modo, reagirai continuamente alla stessa collisione e l'oggetto rimarrà bloccato.
Jonathan Dickinson,

@Piku e per farlo scopriamo il tempo nel passato in cui si è verificata la collisione (chiamata TOI / tempo di impatto). Una volta che abbiamo che possiamo usare la velocità dell'oggetto per spostarlo indietro ( distance = speed * time, di solito con una distanza minuscola per evitare errori) e quindi aggiornare la sua velocità a quale sia il risultato della collisione.
Jonathan Dickinson,

@Piku inoltre non capiamo dove saremo nel prossimo frame (non l'ho mai visto personalmente), ma, in generale, eseguiamo il rilevamento e la risposta alle collisioni: DOPO che calcoliamo la nuova posizione per QUESTO frame, ma PRIMA applichiamo la nuova posizione per QUESTO frame.
Jonathan Dickinson,

1

F = ma o a = F / m. Calcola il punto di collisione tra la sfera e il piano. Di solito questo è il centro della sfera - raggio normale *. Se vuoi maggiore precisione, calcola fino a che punto la sfera è penetrata nel piano e regola il tuo calcolo. Questo è in gran parte facoltativo ovviamente, a meno che tu non voglia una fisica davvero accurata. Ora calcola la velocità relativa lungo la normale. Per un piano statico questo è: Vball Dot N. Quindi moltiplica VballDotN per -1 e moltiplica per massa. In fisica in questa fase si moltiplicherebbe anche questo per il coefficiente di restituzione (fattore di rimbalzo). Moltiplica questo scalare per N e avrai la tua forza.

Quando regoli Vball, dividi di nuovo la forza per massa e hai l'accelerazione finale, quindi aggiungi questo alla velocità e avrai la velocità finale post-collisione.

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

Questo metodo è preciso alle formule della risposta alla collisione. Se vuoi una precisione ancora maggiore, dovrai tenere conto dell'attrito, che farà girare la palla, ma non so se lo desideri nel tuo gioco. In tal caso, ecco come calcolare la forza tangenziale:

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

Assicurati di calcolare Ft prima di applicare qualsiasi effetto lineare, altrimenti l'attrito non sarà accurato.


La riga 3 non dovrebbe essere vec3 F = -CollisionNormal * (1.0f+Ball.getRestitution())*vDotN;:?
Shital Shah,

Anzi sì, mi mancava quella parte. Grazie per segnalarlo.
Ian Young,

0

Suggerirei di calcolare prima la distanza dal piano; e poi quando la distanza <= al raggio effettua la reazione di collisione.

È quindi possibile modificarlo per calcolare la distanza e se la distanza è inferiore a quel raggio (il che significa che l'oggetto si sovrappone) spostare la posizione delle sfere e quindi eseguire la reazione di collisione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.