Come funziona un motore di collisione?


78

Come funziona esattamente un motore di collisione ?

Questa è una domanda estremamente ampia. Quale codice fa rimbalzare le cose l'una contro l'altra, quale codice fa camminare il giocatore contro un muro invece che attraverso il muro? In che modo il codice aggiorna costantemente la posizione dei giocatori e la posizione degli oggetti per mantenere la gravità e la collisione funzionanti come dovrebbero?

Se non sai cosa sia un motore di collisione, fondamentalmente viene generalmente utilizzato nei giochi con piattaforma per far colpire acutamente i muri e simili. C'è il tipo 2D e il tipo 3D, ma tutti realizzano la stessa cosa: la collisione.

Quindi, cosa mantiene il motore di una collisione che ticchetta?


3
Puoi imbrogliare con scatole e sfere di delimitazione, la cui intersezione è rapida da determinare. Quindi puoi ispezionare più da vicino. cs.unc.edu/~dm/collision.html en.wikipedia.org/wiki/Collision_detection Puoi sempre farlo lentamente con un algoritmo ingenuo. Comp Geometry ha alcuni trucchi che sfruttano la natura geometrica del problema e rendono l'algoritmo più veloce. Ecco un ottimo documento: cs.hku.hk/research/techreps/document/TR-2005-01.pdf

cos'è un motore di collisione ?

4
@gnat un motore di collisione è fondamentalmente un motore utilizzato nei giochi (in genere) in modo che il tuo giocatore (chiamalo bob), ogni volta che Bob si muove in un muro, Bob si ferma, Bob non cammina attraverso il muro. Generalmente gestiscono anche la gravità in un gioco e cose ambientali come questa.
JXPheonix,

Risposte:


172

C'è una grande differenza tra un motore di collisione e un motore fisico. Non fanno la stessa cosa, sebbene il motore fisico si basi generalmente su un motore di collisione.

Il motore di collisione viene quindi suddiviso in due parti: rilevamento delle collisioni e risposta alle collisioni. Quest'ultimo fa generalmente parte del motore fisico. Questo è il motivo per cui i motori di collisione e i motori fisici vengono generalmente inseriti nella stessa libreria.

Il rilevamento delle collisioni si presenta in due forme, discrete e continue. I motori avanzati supportano entrambi, poiché hanno proprietà diverse. In generale, il rilevamento continuo delle collisioni è molto costoso e viene utilizzato solo dove è realmente necessario. La maggior parte delle collisioni e della fisica viene gestita con metodi discreti. In metodi discreti, gli oggetti finiranno per penetrarsi a vicenda e il motore fisico lavorerà per separarli. Quindi il motore in realtà non impedisce a un giocatore di camminare parzialmente attraverso un muro o il pavimento, ma lo risolve solo dopo aver rilevato che il giocatore è parzialmente nel muro / pavimento. Mi concentrerò qui sul rilevamento discreto delle collisioni, poiché è quello che ho più esperienza nell'implementazione da zero.

Rilevazione di collisioni

Il rilevamento delle collisioni è relativamente semplice. Ogni oggetto ha una trasformazione e una forma (possibilmente più forme). Gli approcci ingenui farebbero sì che il motore di collisione esegua un ciclo O (n ^ 2) attraverso tutte le coppie di oggetti e verifichi se vi è sovrapposizione tra le coppie. In approcci più intelligenti ci sono più strutture di dati spaziali (ad esempio, per oggetti statici vs dinamici), una forma di delimitazione per ciascun oggetto e sottomaschere convesse in più parti per ciascun oggetto.

Le strutture di dati spaziali includono elementi come alberi di KD, alberi AABB dinamici, alberi / quadrifogli, alberi di partizionamento dello spazio binario e così via. Ognuno ha i suoi vantaggi e svantaggi, motivo per cui alcuni motori di fascia alta ne utilizzano più di uno. Gli alberi AABB dinamici, ad esempio, sono davvero molto veloci e utili per gestire molti oggetti in movimento, mentre un albero KD può essere più adatto alla geometria a livello statico con cui gli oggetti si scontrano. Ci sono anche altre opzioni.

L'ampia fase utilizza le strutture di dati spaziali e un volume di delimitazione astratto per ciascun oggetto. Un volume di delimitazione è una forma semplice che racchiude l'intero oggetto, generalmente con l'obiettivo di racchiuderlo il più "stretto" possibile pur rimanendo a buon mercato con cui eseguire i test di collisione. Le forme di delimitazione più comuni sono Scatole di confine allineate ad asse, Scatole di confine allineate ad oggetto, Sfere e Capsule. Gli AABB sono generalmente considerati i più veloci e facili (le sfere sono più facili e veloci in alcuni casi, ma molte di quelle strutture di dati spaziali richiederebbero comunque di convertire la sfera in AABB), ma tendono anche a adattarsi a molti oggetti piuttosto male. Le capsule sono popolari nei motori 3D per gestire le collisioni a livello di personaggio. Alcuni motori useranno due forme limitanti,

L'ultima fase del rilevamento delle collisioni è quella di rilevare esattamente dove si interseca la geometria. Questo di solito implica l'uso di una mesh (o di un poligono in 2D), sebbene non sempre. Lo scopo di questa fase è scoprire se gli oggetti si scontrano davvero, se è necessario un livello di dettaglio elevato (diciamo, collisione dei proiettili in uno sparatutto, dove si desidera poter ignorare i colpi che mancano appena), e anche per scoprire esattamente dove gli oggetti si scontrano, il che influenzerà il modo in cui gli oggetti rispondono. Ad esempio, se una scatola è seduta sul bordo di un tavolo, il motore deve sapere in quali punti il ​​tavolo sta spingendo contro la scatola; a seconda della distanza della scatola, la scatola potrebbe iniziare a inclinarsi e cadere.

Generazione del collettore di contatto

Gli algoritmi utilizzati qui includono i famosi algoritmi di affinamento del portale GJK e Minkowski, nonché il test dell'asse di separazione. Poiché gli algoritmi popolari funzionano in genere solo per forme convesse, è necessario suddividere molti oggetti complessi in oggetti secondari convessi ed eseguire test di collisione per ciascuno di essi. Questo è uno dei motivi per cui le mesh semplificate vengono spesso utilizzate per le collisioni, nonché la riduzione dei tempi di elaborazione per l'utilizzo di un numero inferiore di triangoli.

Alcuni di questi algoritmi non solo ti dicono che gli oggetti si sono sicuramente scontrati, ma dove si sono scontrati: quanto si stanno penetrando a vicenda e quali sono i "punti di contatto". Alcuni algoritmi richiedono passaggi aggiuntivi, come il ritaglio poligonale, per ottenere queste informazioni.

Risposta fisica

A questo punto, è stato scoperto un contatto e sono disponibili informazioni sufficienti per consentire al motore fisico di elaborare il contatto. La gestione della fisica può diventare molto complessa. Gli algoritmi più semplici funzionano per alcuni giochi, ma anche qualcosa di apparentemente semplice come mantenere stabile una pila di scatole risulta piuttosto difficile e richiede molto lavoro e hack non ovvi.

Al livello più elementare, il motore fisico farà qualcosa del genere: prenderà gli oggetti in collisione e il loro collettore di contatto e calcolerà le nuove posizioni richieste per separare gli oggetti in collisione. Sposterà gli oggetti in queste nuove posizioni. Calcolerà anche il cambiamento di velocità risultante da questa spinta, combinato con i valori di restituzione (rimbalzo) e attrito. Il motore fisico applicherà anche tutte le altre forze che agiscono sugli oggetti, come la gravità, per calcolare le nuove velocità degli oggetti e quindi (fotogramma successivo) le loro nuove posizioni.

Una risposta fisica più avanzata si complica rapidamente. L'approccio di cui sopra si interromperà in molte situazioni, incluso un oggetto seduto sopra altri due. Trattare ogni coppia da sola provocherà "jitter" e gli oggetti rimbalzeranno molto. La tecnica di base è eseguire una serie di iterazioni di correzione della velocità sulle coppie di oggetti in collisione. Ad esempio, con una casella "A" situata sopra altre due caselle "B" e "C", la collisione AB verrà gestita per prima, facendo sì che la casella A si inclini ulteriormente nella casella C. Quindi la collisione CA viene gestita, sera un po 'fuori dalle caselle, ma tirando A verso il basso e in B. Quindi viene eseguita un'altra iterazione, quindi l'errore AB causato dalla correzione CA viene leggermente risolto, creando un po' più errore nella risposta CA. Che viene gestito quando AC viene nuovamente elaborato. Il numero di iterazioni effettuate non è fisso e non vi è alcun punto in cui diventa "perfetto", ma piuttosto qualunque numero di iterazioni smette di dare risultati significativi. 10 iterazioni sono un tipico primo tentativo, ma ci vogliono modifiche per capire il numero migliore per un motore specifico e le esigenze di un gioco particolare.

Contatta la memorizzazione nella cache

Ci sono altri trucchi che risultano davvero utili (più o meno necessari) quando si ha a che fare con molti tipi di giochi. La memorizzazione nella cache dei contatti è una delle più utili. Con una cache dei contatti, ogni set di oggetti in collisione viene salvato in una tabella di ricerca. Ogni frame, quando viene rilevata una collisione, questa cache viene interrogata per vedere se gli oggetti erano precedentemente in contatto. Se gli oggetti non erano precedentemente in contatto, è possibile generare un evento "nuova collisione". Se gli oggetti erano precedentemente in contatto, le informazioni possono essere utilizzate per fornire una risposta più stabile. Qualsiasi voce nella cache dei contatti che non è stata aggiornata in un frame indica due oggetti che si sono separati e può essere generato un evento "oggetto di separazione". La logica di gioco ha spesso usi per questi eventi.

È anche possibile che la logica di gioco risponda ai nuovi eventi di collisione e li contrassegni come ignorati. Questo è davvero utile per implementare alcune funzionalità comuni nelle piattaforme, come le piattaforme che puoi saltare ma in piedi. Le implementazioni ingenue possono semplicemente ignorare le collisioni che hanno una piattaforma discendente-> normale collisione del personaggio (che indica che la testa del giocatore ha colpito la parte inferiore della piattaforma), ma senza contatto con la cache, questo si romperà se la testa del giocatore sporge attraverso la piattaforma e poi inizia cadere. A quel punto, il contatto normale può finire per puntare verso l'alto, facendo apparire il giocatore attraverso la piattaforma quando non dovrebbe. Con la memorizzazione nella cache dei contatti, il motore può guardare in modo affidabile alla normale collisione iniziale e ignorare tutti gli altri eventi di contatto fino a quando la piattaforma e il giocatore si separano nuovamente.

addormentato

Un'altra tecnica molto utile è quella di contrassegnare gli oggetti come "addormentati" se non vengono interagiti. Gli oggetti dormienti non ottengono aggiornamenti di fisica, non si scontrano con altri oggetti dormienti e, in pratica, restano seduti lì congelati nel tempo fino a quando un altro oggetto non dormiente si scontra con loro.

L'impatto è che tutte le coppie di oggetti in collisione che stanno semplicemente seduti lì senza fare nulla non impiegano alcun tempo di elaborazione. Inoltre, poiché non vi è una quantità costante di minuscole correzioni fisiche, le pile saranno stabili.

Un oggetto è un candidato per dormire quando ha avuto una velocità quasi zero per più di un singolo fotogramma. Nota che l'epsilon che usi per testare questa velocità prossima allo zero sarà probabilmente un po 'più alto del solito epsilon di confronto in virgola mobile, poiché dovresti aspettarti un po' di jitter con oggetti impilati e vuoi che intere pile di oggetti si addormentino se ' stai "abbastanza vicino" per stabile. La soglia richiederà ovviamente modifiche e sperimentazione.

vincoli

L'ultimo pezzo importante di molti motori fisici è il risolutore di vincoli. Lo scopo di un tale sistema è facilitare l'implementazione di cose come molle, motori, assi delle ruote, corpi molli simulati, tessuti, corde e catene e talvolta persino fluidi (sebbene il fluido sia spesso implementato come un sistema completamente diverso).

Anche le basi della risoluzione dei vincoli possono richiedere molta matematica e vanno oltre la mia esperienza in questo argomento. Consiglio di dare un'occhiata all'eccellente serie di articoli sulla fisica di Randy Gaul per una spiegazione più approfondita dell'argomento.


se hai intenzione di affrontare il numero minimo di risoluzioni di collisione, probabilmente dovresti anche rispondere alla necessità di mantenere il numero il più basso possibile considerando che in una configurazione complessa che consente un numero elevato di riposizioni di collisione ridurrà notevolmente la frequenza dei fotogrammi. poi quando parlavi del numero di iterazioni era quello per coppia, o quello per tutte le collisioni.
gardian06,

1
@ gardian06: è una panoramica scritta rapidamente, sicuramente potrebbe essere ampliata un po '. ho dimenticato di menzionare l'oggetto che dorme, per esempio, che è davvero utile. per le iterazioni, eseguo iterazioni su tutte le raccolte di coppie e anche con conteggi di iterazioni relativamente grandi (20+) non ho mai notato un problema di prestazioni (ma, ancora una volta, è con oggetto inattivo, quindi un numero relativamente piccolo di collisioni attive da risolvere ).
Sean Middleditch,

1
Risposta fantastica, +1. Leggere questo mi fa davvero venire voglia di implementare questi algoritmi.
Miles Rout,

3

Il problema generale: determinare quale di tutte le possibili combinazioni di oggetti ha un volume di intersezione diverso da zero.

L'approccio ingenuo e generale è semplice: per ogni possibile coppia di oggetti, calcolare il volume dell'intersezione. Questo di solito non è pratico, poiché richiede O (n ^ 2) operazioni di intersezione relativamente costose.

Pertanto, le implementazioni pratiche sono spesso specializzate, facendo alcune ipotesi per consentire di evitare controlli intersecanti o la riduzione dei loro costi. Il partizionamento spaziale sfrutta il fatto che gli oggetti sono in genere piccoli rispetto al volume totale e riduce in genere il numero di confronti rispetto a O (n log n). Le scatole di delimitazione allineate sull'asse e le sfere di delimitazione forniscono controlli di intersezione grossolani poco costosi, purché gli oggetti obbediscano a determinati presupposti di compattezza. E così via.


2

Un "motore di collisione" che ho usato mi è sembrato estremamente facile da capire.

Fondamentalmente, API ha fornito un tipo di oggetti con metodo collidesWith, tale che

  1. i suoi parametri erano diversi tipi di oggetti "idonei" alla collisione
  2. è tornato vero o falso a seconda che si sia verificata o meno una collisione
  3. c'era un'opzione che consente di scegliere se tutti i rettangoli di delimitazione per gli oggetti attivano l'evento di collisione o solo i pixel opachi all'interno di questi rettangoli (rilevamento a livello di pixel)

Nella mia domanda, ho solo periodicamente invocato collidesWithper scoprire se la collisione è avvenuta o meno.

Abbastanza semplice no?


Forse l'unica cosa che richiedeva un piccolo tratto di immaginazione era quando semplici rettangoli di delimitazione non erano sufficienti per simulare la geometria di oggetti in collisione. In questo caso si dovrebbe semplicemente usare più oggetti raccoglibili invece di uno.

Generalmente, quando scopri che il rettangolo di singola collisione non fa ciò di cui hai bisogno, inventi un modo per scomporre le cose in più sotto-elementi rettangolari in modo che, quando combinati, simulino / approssimino il comportamento desiderato.

  • Gli utenti finali non si preoccupano di quanti oggetti ci siano dietro la scena. Sono felici fintanto che il risultato finale sembra quello che si aspettano, ad esempio come una casa con un recinto dietro:

    http://i.stack.imgur.com/4Wn5B.jpg


2

Penso che tu sia un po 'confuso su ciò di cui stai parlando e stai parlando di alcune cose diverse.

la capacità di dire che questo oggetto viene spostato dalla posizione X alla posizione Y si basa sulla fisica (questo attacca anche il modo in cui si muovono e perché si muovono)

il metodo utilizzato per il rilevamento delle collisioni è determinato in base alla struttura del gioco. se il tuo gioco è un grande mondo aperto, allora dovresti prendere in grande considerazione il partizionamento dello spazio (quad-tree [oct-tree for 3D], BSP, una griglia tradizionale o il vecchio approccio test tutto si avvicina)

il modo migliore per implementare un sistema di rilevamento delle collisioni è farlo in più passaggi.

  1. posizionare tutti gli oggetti in un volume / forma di delimitazione generici, quindi testarli

  2. se 1 è passato, ripetere con una ripetizione volume / forma più complessa fino a quando non si è pronti per testare la geometria assoluta

  3. testare la geometria assoluta il numero di volte in cui si ripete il passaggio 2 per determinare la complessità delle forme e l'accuratezza di tali forme.

dovresti considerare che ciascuno di questi passaggi è precoce, e allo scopo di eliminare le collisioni man mano che procedi, e di tornare vero sul passaggio 3 solo se si toccano davvero.

Quindi per l'ultima parte è la risoluzione delle collisioni. questo determina cosa succede dopo aver trovato una collisione e aver dimostrato che si tratta davvero di una collisione e cosa fare al riguardo. questo è di solito gestito dalla fisica.

il ciclo tradizionale si presenta così:

receive input
update physics
collision detection
collision resolution
render
repeat

Vorrei solo sottolineare che è raro che i motori di gioco testino la geometria assoluta per le collisioni. Di solito l'algoritmo si sposterà fino al passaggio 2 nella struttura.
kevintodisco,

La maggior parte dei motori di gioco verifica la geometria assoluta in molti (non tutti) casi. Senza farlo, ci saranno "anomalie" molto ovvie nella risposta della fisica. La maggior parte dei motori avrà una semplice fase ampia (partizionamento spaziale), un semplice test del volume di delimitazione (AABB più comunemente) e quindi (quando necessario) un test della geometria semplificato (ad esempio, non la stessa geometria della geometria di rendering full-LOD, ma geometria ancora grezza che è probabilmente una delle versioni a basso LOD della mesh renderizzata).
Sean Middleditch

@seanmiddleditch che era più della mia intenzione di testare un'approssimazione di solito cercando di evitare di testare poligoni / poliedri concavi.
gardian06,

@ktodisco dipende dalla concavità della figura e dalla precisione che deve essere, e quindi da cosa deve essere restituito affinché il sistema fisico possa risolvere la collisione in quanto ciò può variare in base al motore fisico e alla precisione prevista del risposta
gardian06,

@ guardian06 La spiegazione di seanmiddleditch è molto più fattibile, anche se testare intersezioni assolute tra personaggi costituiti da migliaia di poligoni non è ancora una buona idea.
kevintodisco,

1

A livello di scheda grafica (dove di solito hai a che fare con triangoli), l'idea generale è quella di dividere la scena in qualche modo in modo da non dover controllare tutti i triangoli N (questo può essere fatto come una fase di pre-elaborazione ), quindi scopri dove ti trovi nella scena e controlla solo quei 10-50 triangoli in quella partizione.

Vedi alberi BSP e Kd per maggiori informazioni. Esistono anche altri approcci di partizionamento.


0

In primo luogo, penso che il lavoro più importante di un motore di collisione sia quello di determinare ciò che non deve essere verificato per la collisione in una situazione particolare su una base per fotogramma e tagliare quegli oggetti da ulteriori controlli.

In secondo luogo, ma anche importante, controlla in modo più dettagliato (accurato) gli oggetti rimanenti che non sono stati abbattuti in quel primo passo.

In terzo luogo, utilizzare i metodi più efficienti / appropriati per eseguire i controlli.

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.