Je ne connais pas Android, donc je ne sais pas quels outils vous avez à votre disposition, mais je peux vous dire un moyen de mettre en œuvre cela en termes généraux. La facilité de ce processus dépend de ce qu'Android vous propose. Vous allez avoir besoin de matrices ou au moins elles simplifieront beaucoup les calculs.
Pour commencer, effectuez un contrôle de collision de la boîte englobante et revenez immédiatement s'ils ne se heurtent pas afin d'éviter d'autres calculs. C'est logique car si les boîtes englobantes ne se heurtent pas, il est garanti qu'aucun pixel ne se heurtera non plus.
Ensuite, si un contrôle de collision parfait de pixels est nécessaire, le point le plus important est que vous devez effectuer ce contrôle dans le même espace . Cela peut être fait en prenant chaque pixel de l'image-objet A, en appliquant une série de transformations afin de les placer dans l'espace local de l'image-objet B, puis en vérifiant s'il entre en collision avec un pixel quelconque à cette position sur l'image-objet B. Une collision se produit lorsque les deux pixels vérifié sont opaques.
Donc, la première chose dont vous avez besoin est de construire une matrice mondiale pour chacun des sprites. Il existe probablement des didacticiels en ligne qui vous apprennent à en créer un, mais il devrait s'agir essentiellement d'une concaténation de quelques matrices plus simples dans l'ordre suivant:
Translation(-Origin) * Scale * Rotation * Translation(Position)
L'utilité de cette matrice est qu'en multipliant un point dans l'espace local - et par exemple si vous obtenez les pixels en utilisant une méthode comme celle-ci, bitmap.getPixelAt(10,20)
10,20 est défini dans l'espace local - par la matrice mondiale correspondante le déplacera dans l'espace mondial:
LocalA * WorldMatrixA -> World
LocalB * WorldMatrixB -> World
Et si vous inversez les matrices, vous pouvez également aller dans la direction opposée, c'est-à-dire transformer des points de l'espace mondial en chacun des espaces locaux du sprite en fonction de la matrice que vous avez utilisée:
World * InverseWorldMatrixA -> LocalA
World * InverseWorldMatrixB -> LocalB
Donc, pour déplacer un point de l'espace local du sprite A vers l'espace local du sprite B , vous devez d'abord le transformer en utilisant la matrice mondiale du sprite A, afin de le placer dans l'espace mondial, puis en utilisant la matrice du monde inverse du sprite B , pour l'intégrer dans l'espace local de sprite B:
LocalA * WorldMatrixA -> World * InverseWorldMatrixB -> LocalB
Après la transformation, vous vérifiez si le nouveau point tombe dans les limites de l'image-objet B, et si c'est le cas, vous vérifiez le pixel à cet endroit comme vous l'avez fait pour l'image-objet A. Ainsi, le processus entier devient quelque chose comme ça (en pseudocode et non testé) :
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;
}
Bien que la réponse de David Gouveia semble correcte, ce n'est pas la meilleure solution du point de vue des performances. Vous devez effectuer plusieurs optimisations importantes:
trier et éviter les vérifications inutiles en vérifiant d'abord avec une simple collision de cercle: pour obtenir le centre et le rayon de vos sprites dans n'importe quelle rotation, obtenez les coordonnées minima et maxima x et y de tous les 4 sommets (déjà tournés): alors vous pouvez construire un cercle se fermant dans le sprite par:
centre = max_x-min_x / 2, max_y-min_y / 2
rayon = max (max_x-min_x, max_y-min_y)
maintenant, vous ne devriez pas avoir trop de candidats à vérifier en pixellisant les images déjà transformées (tournées) essentiellement en utilisant un algorithme de texturemapping affine simple . Fondamentalement, vous gardez une trace de 4 lignes par image-objet pixellisée: 2 lignes allant du sommet a aux sommets suivants de votre boîte pivotée (b et c) 2 lignes allant dans "l'espace bitmap" du sommet u1 / v1 aux sommets suivants u2 / v2 et u3 / v3: Remarque: J'ai googlé cette image et elle montre un triangle, vos boîtes ne sont que deux triangles. Il est essentiel que cet algorithme dessine des lignes horizontales (c'est pourquoi il est appelé " rasterizer ") pour éviter les "trous" à l'intérieur du bitmap en raison d'erreurs d'arrondi. En calculant les lignes avec un algorithme de Bresenham, vous avez besoin pour chaque pixel de seulement 4 ajouts et 4 comparaisons (et parfois deux ajouts supplémentaires, selon la pente). Ce que vous faites, c'est que vous écrivez votre propre texturemapper polygonal, mais sans la correction 3D coûteuse (et difficile à optimiser).
vous pouvez facilement réduire la résolution des bitmaps de collision (par exemple par le facteur 2) et gagner encore plus de temps. un contrôle de collision sur la moitié de la résolution devrait être imperceptible.
Si vous utilisez une accélération graphique, il pourrait être possible d'utiliser une sorte de vérification de tampon accélérée par HW (pochoir?) Pour éviter de coder le rasterizer par vous-même.
Le problème principal est: Java n'est pas très rapide pour accéder aux données bitmap stockées dans un tableau 2D. Je recommanderais de stocker les données dans un tableau à 1 dimension pour éviter au moins 1 vérification indexOutOfBounds pour chaque accès. Utilisez également des dimensions de puissance de 2 (comme 64x64, 128x128, etc. Par cela, vous pouvez calculer le décalage via le décalage de bits et non la multiplication). Vous pouvez également optimiser le deuxième accès de texture pour l'exécuter uniquement si le premier a une valeur! = 0 (transparent)
Tous ces problèmes ont été résolus dans les moteurs de rendu logiciel, il pourrait être utile d'examiner le code source
la source