Risposte:
Non ho familiarità con Android, quindi non so quali strumenti hai a disposizione, ma posso dirti un modo per implementarlo in termini generali. Quanto sarà facile dipende da ciò che Android offre per te. Avrai bisogno di matrici o almeno semplificheranno molto i calcoli.
Per i principianti fare un controllo delle collisioni con la delimitazione e tornare immediatamente se non si scontrano per evitare ulteriori calcoli. Questo è logico perché se i riquadri di delimitazione non si scontrano è garantito che nessun pixel si scontrerà.
Successivamente, se è necessario un controllo di collisione perfetto dei pixel, il punto più importante è che è necessario eseguire quel controllo nello stesso spazio . Questo può essere fatto prendendo ogni pixel dallo sprite A, applicando una serie di trasformazioni al fine di portarli nello spazio locale dello sprite B, e quindi verificare se si scontra con qualsiasi pixel in quella posizione sullo sprite B. Una collisione si verifica quando entrambi i pixel controllato sono opachi.
Quindi, la prima cosa di cui hai bisogno è costruire una matrice mondiale per ciascuno degli sprite. Probabilmente ci sono tutorial online che ti insegnano come crearne uno, ma in pratica dovrebbe essere una concatenazione di alcune matrici più semplici nel seguente ordine:
Translation(-Origin) * Scale * Rotation * Translation(Position)
L'utilità di questa matrice è che moltiplicando un punto nello spazio locale - e per esempio se ottieni i pixel usando un metodo come bitmap.getPixelAt(10,20)
allora 10,20 è definito nello spazio locale - dalla matrice mondiale corrispondente lo sposterà nello spazio mondiale:
LocalA * WorldMatrixA -> World
LocalB * WorldMatrixB -> World
E se inverti le matrici puoi anche andare nella direzione opposta, cioè trasformare i punti dallo spazio del mondo in ciascuno degli spazi locali dello sprite a seconda della matrice che hai usato:
World * InverseWorldMatrixA -> LocalA
World * InverseWorldMatrixB -> LocalB
Quindi, al fine di spostare un punto dallo spazio locale di sprite A nello spazio locale di sprite B , prima lo trasformi usando la matrice mondiale di sprite A, al fine di portarlo nello spazio del mondo, e quindi usando la matrice inversa del mondo di sprite B , per farlo entrare lo spazio locale di sprite B:
LocalA * WorldMatrixA -> World * InverseWorldMatrixB -> LocalB
Dopo la trasformazione, controlli se il nuovo punto rientra nei limiti dello sprite B e, in tal caso, controlli il pixel in quella posizione proprio come hai fatto per lo sprite A. Quindi l'intero processo diventa qualcosa del genere (in pseudocodice e non testato) :
bool PixelCollision(Sprite a, Sprite B)
{
// Go over each pixel in A
for(i=0; i<a.Width; ++i)
{
for(j=0; j<a.Height; ++j)
{
// Check if pixel is solid in sprite A
bool solidA = a.getPixelAt(i,j).Alpha > 0;
// Calculate where that pixel lies within sprite B's bounds
Vector3 positionB = new Vector3(i,j,0) * a.WorldMatrix * b.InverseWorldMatrix;
// If it's outside bounds skip to the next pixel
if(positionB.X<0 || positionB.Y<0 ||
positionB.X>=b.Width || positionB.Y>=b.Height) continue;
// Check if pixel is solid in sprite B
bool solidB = b.getPixelAt(positionB.X, positionB.Y).Alpha > 0;
// If both are solid then report collision
if(solidA && solidB) return true;
}
}
return false;
}
Mentre la risposta di David Gouveia sembra corretta, non è la soluzione migliore dal punto di vista delle prestazioni. Ci sono diverse importanti ottimizzazioni che devi fare:
sistemare ed evitare controlli non necessari controllando prima con una semplice collisione circolare: per ottenere il centro e il raggio dei tuoi folletti in qualsiasi rotazione, ottenere le coordinate minima e massima xey di tutti e 4 i vertici (già ruotati): quindi puoi costruire un cerchio che si chiude nello sprite di:
center = max_x-min_x / 2, max_y-min_y / 2
raggio = max (max_x-min_x, max_y-min_y)
ora non dovresti avere troppi candidati da verificare rasterizzando le immagini già trasformate (ruotate) fondamentalmente usando un semplice algoritmo di affinamento del testo affine . Fondamentalmente tieni traccia di 4 linee per sprite rasterizzata: 2 linee che vanno dal vertice a ai vertici successivi della casella ruotata (bec) 2 linee che vanno nello "spazio bitmap" dal vertice u1 / v1 ai vertici successivi u2 / v2 e u3 / v3: Nota: ho cercato su Google questa immagine e mostra un triangolo, le tue caselle sono solo due triangoli. È vitale per questo algoritmo disegnare linee orizzontali (ecco perché si chiama " rasterizer ") per evitare "buchi" all'interno della bitmap a causa di errori di arrotondamento. Calcolando le linee con un algoritmo di Bresenham, per ogni pixel sono necessarie solo 4 aggiunte e 4 confronti (e talvolta due aggiunte aggiuntive, a seconda della pendenza) Quello che fai è scrivere il tuo texturemapper poligonale, tuttavia senza la costosa (e difficile da ottimizzare) correzione 3d.
puoi facilmente ridurre la risoluzione delle bitmap di collisione (ad esempio per fattore 2) e risparmiare ancora più tempo. un controllo delle collisioni sulla metà della risoluzione deve essere impercettibile.
Se si utilizza l'accellerazione grafica, potrebbe essere possibile utilizzare una sorta di controllo buffer accelerato HW (stencil?) Per evitare di codificare il rasterizer da soli.
Il problema principale è: Java non è molto veloce nell'accedere ai dati bitmap memorizzati in un array 2D. Consiglierei di archiviare i dati in un array monodimensionale per evitare almeno 1 controllo indexOutOfBounds per ogni accesso. Utilizza anche dimensioni power-of-2 (come 64x64, 128x128, ecc.). In questo modo puoi calcolare l'offset tramite bit-shifting e non moltiplicazione). Puoi anche ottimizzare l'accesso alla seconda trama per eseguirlo solo se il primo ha valore! = 0 (trasparente)
Tutti questi problemi sono stati risolti nei motori di rendering software, potrebbe essere utile esaminare il codice sorgente