Rilevazione di collisioni con curve


12

Sto lavorando a un gioco 2D in cui vorrei effettuare il rilevamento delle collisioni tra un cerchio mobile e una sorta di curve statiche (forse curve di Bezier).

Attualmente il mio gioco presenta solo linee rette come geometria statica e sto facendo il rilevamento delle collisioni calcolando la distanza dal cerchio alle linee e proiettando il cerchio fuori dalla linea nel caso in cui la distanza sia inferiore al raggio dei cerchi.

Come posso eseguire questo tipo di rilevamento delle collisioni in modo relativamente semplice? So ad esempio che Box2D presenta il rilevamento delle collisioni con curve di Bezier. Non ho bisogno di un meccanismo completo di rilevamento delle collisioni, solo qualcosa che può fare quello che ho descritto.


AGGIORNAMENTO: Grazie mille per le ottime risposte! Dovrò leggere sulle curve di Bezier per comprendere appieno il metodo che hai descritto. Allora torno da te.

Risposte:


6

29/09/2012 - 23:20

Ho creato un Repto git qui: https://github.com/ArthurWulfWhite/Bezier-Distance/

Da qui puoi scaricare i file sorgente come zip. Include anche una demo che puoi compilare usando FlashDevelop. Per utilizzare la demo, apri il progetto in Flash Develop e fai clic su "Test progetto". Durante l'esecuzione della demo, fai clic su LMB per randomizzare una nuova curva di Bezier e un nuovo cerchio.

In bocca al lupo!

Il collegamento zip è difficile da vedere: basta usare Ctrl + F e digitare zip. Questa fonte rappresenta un paio di settimane di ricerca e programmazione, spero che ti piaccia.


Se hai intenzione di dividere il bezier in modo ricorsivo in segmenti e di verificare la presenza di collisioni con loro, ti suggerisco di creare un array di 100.100 (griglia) e di posizionare ogni segmento nei quattro quadrati più vicini, quindi devi solo verificare le collisioni con 4 / 10.000 del segmenta ogni fotogramma.

Penso che trarrai beneficio da box2d sia come programmatore che come creatore di giochi, dal momento che ci sono molti piccoli ostacoli nascosti nel creare un motore fisico "semplice" che fa sembrare il movimento un po 'irregolare e meno fluido di quanto potrebbe essere.

Vecchia risposta: il modo puro.

Puoi effettivamente vedere se un cerchio si scontra con una curva di Bezier, controllando la distanza tra la distanza tra il centro del cerchio e il punto più vicino sulla curva.

L'equazione per la distanza (in generale)

ha spiegato:

Equazione di Bezier:

q(t) = (1-t) * ((1-t) * start.(x,y) + t * control.(x,y)) + t*(t * control.(x,y) + (1 - t) * end.(x,y))

Questo può essere riassunto in (con un po 'di algebra) - Ometterò. (X, y) per leggibilità (sono ancora punti, non un numero)

q(t) = (start -2 * cont + end) t^2 + (-2 * start + 2 * control) + start

La distanza dal punto (x, y) è:

sqrt ((q(t).x - point.x)^2 + (q(t).y - point.y)^2)

Per trovare il punto più vicino sul bezier alla palla, è necessario derivare e trovare tutti i punti in cui la derivata è uguale a zero (le radici). È un polinomio di terzo grado, quindi è possibile utilizzare una formula chiusa ma potrebbe non essere affidabile poiché la precisione delle frazioni rappresentate in virgola mobile del computer potrebbe non essere sufficiente. È molto meglio usare Newton o qualcosa del genere.

Il derivato di cui hai bisogno per trovare le radici è:

Supponendo: a = inizio b = controllo c = fine d = punto centrale del cerchio

Derivata utilizzando wolfram alpha

La parte difficile sta moltiplicando questi punti, devi usare il prodotto punto.

Se vuoi, ho il codice per questo e posso condividerlo qui sotto forma di una funzione che restituisce semplicemente un valore booleano se c'è una collisione o meno e un angolo di collisione. Alcuni problemi potrebbero apparire nell'implementazione ingenua di un motore di collisione come questo, ad esempio una palla in rapido movimento potrebbe rimanere intrappolata tra due curve.

Consiglio di evitarlo per ora, basta sommare i coefficienti per l'asse xe per l'asse y e sommarli.

Usa qualsiasi metodo affidabile che puoi scegliere come Newton per trovare le radici, controllare la distanza dai punti radice sul Bezier, 0 <= t <= 1 al centro del cerchio e controllare la distanza per le due estremità del Bezier (inizio e fine) al centro del cerchio, qualunque sia il più vicino, ti dirà se c'è una collisione.

Se il raggio è inferiore alla distanza minima, si verifica una collisione.

L'angolo è approssimativamente quello tra il centro del cerchio e il punto più vicino sul Bezier.

Detto questo, se desideri davvero fare un gioco con la fisica delle collisioni, ti suggerisco di ripetere il più bezier

    q(t) = (1-t) * ((1-t) * start.(x,y) + t * control.(x,y)) + t*(t * control.(x,y) + (1 - t) * end.(x,y))

Dividi ogni pezzo nel mezzo in modo ricorsivo fino a quando non è abbastanza piccolo, diciamo 10 pixel o meno, quindi costruisci il bezier approssimativamente dalle scatole e usa Box2d per la fisica perché è possibile che scrivere tutto questo codice di rilevamento delle collisioni si rivelerà un grande perdita di tempo che non migliora molto il gameplay. L'uso di Box2d si è dimostrato valido in innumerevoli progetti in passato.


Il metodo che descrivi per calcolare il punto più corto alla curva è esattamente quello che sto attualmente usando con le linee invece delle curve. Ma fare lo stesso per le curve con il metodo che spieghi sembra un po 'troppo complicato. Che, a quanto ho capito, è anche quello che pensi. E per quanto riguarda Box2D. Sono certo che sia un ottimo lavoro. Ma la fisica nel mio gioco è onestamente molto semplice e quindi ho deciso che un vero e proprio motore di fisica è eccessivo.
paldepind,

Quanti oggetti ci sono nel tuo gioco? Quanti possono scontrarsi tra loro? A volte l'uso di un motore fisico può offrire grandi benefici, come il calcolo accurato del tempo di collisione. (perché i frame sono discreti e le collisioni sono reali (non si verificano esattamente quando si esegue il rendering di un frame)
AturSams

Spesso non ci sono sfide inaspettate quando si implementa qualcosa di nuovo e la bellezza dell'uso di un'API di fisica 2D, è che è proprio come usare qualsiasi linguaggio di programmazione, non richiede uno sforzo particolare da parte tua se non investire un paio d'ore per impararlo e i risultati sono molto soddisfacenti.
AturSams,

Ho aggiunto alcuni dettagli in questo momento, buona fortuna. :)
AturSams,

Sto creando un semplice gioco simile a Elasto Mania. Solo tre cerchi in movimento e geometria statica. L'intero motore è finito e funziona alla grande. L'unica cosa rimasta è consentire curve che sto per risolvere atm grazie all'aiuto in questa risposta :) Sentiti libero di pubblicare il codice che hai citato. Quanto ritieni opportuno utilizzare nella vita reale? Meglio che convertire il bezier in linee minuscole?
paldepind,

7

Per fare questo, vorrei:

  • Rompere la curva di Bezier in segmenti di linea di divisione e memorizzarli.

  • Metti tutti questi segmenti in un riquadro di delimitazione allineato agli assi per l'intera curva.

Rilevazione di collisioni:

1) controlla se la sfera è all'interno del riquadro di selezione principale. se no, nessuna collisione.

2) in caso contrario, verificare se uno dei singoli segmenti calcolati sopra si scontrano con la sfera. Vedi l' articolo Intersezione linea-sfera di Wikipedia .

EDIT: se hai bisogno di alta precisione e desideri buone prestazioni, puoi anche creare un riquadro di delimitazione principale per l'intera curva, quindi suddividere la curva in due segmenti (ad esempio: [0.0 - 0.5]e [0.5 - 1.0]). Crea una bouding box per ciascuno di essi, quindi suddividi nuovamente ciascuno di questi segmenti in due segmenti (dando così [0 - 0.25], [0.25 - 0.5]e [0.5 - 0.75], [0.75 - 1.0]). Continua così fino a raggiungere una precisione sufficiente. alla fine avrai un binary treeriquadro di delimitazione con il riquadro di delimitazione della curva principale alla radice e segmenti di linea alle foglie. la ricerca nell'albero ti darà O(log n)invece di O(n)(dove n= numero di segmenti di linea per la curva)


Questa soluzione ha senso per me ed è sicuramente la più facile da capire e potrei accontentarmi. Ma sono curioso di sapere se esiste un'opzione più "pura".
paldepind,

5

L'intersezione tra una linea e una curva di Bezier si ottiene matematicamente suddividendo la curva. Ciò significa fare affidamento sulla proprietà convessa dello scafo della curva e suddividerla in archi più piccoli con poligoni di controllo diversi in modo diviso-e-impera.

Questo articolo lo copre fino a un certo punto: http://students.cs.byu.edu/~tom/557/text/cic.pdf .

La parte bella è che l'algoritmo funziona con qualsiasi linea, devi solo applicare una curva rigida alla curva in modo da poter considerare la tua linea bersaglio parallela all'asse del bue.

Allo stesso modo, puoi controllare il cerchio e il poligono di ciascuno di questi archi di Bézier quando suddividi un arco di Bézier in due sotto-archi. Il cerchio dovrebbe intersecare il poligono di controllo di un arco affinché un test da curva a cerchio abbia un senso.


Non ho ancora letto l'articolo. Ma come posso ottenere dall'intersezione tra una linea e una curva di Bezier all'intersezione tra un cerchio e una Bezier? Controllare la collisione con un cerchio e un poligono suona un po 'troppo complicato per me.
paldepind,
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.