Aller au contenu

Gérer collision entre canvas ? (pour jeu 2D)


Stilgardt

Recommended Posts

Bonjour,

Tout d'abord, j'ai trouvé un excellent exemple de code très simple pour tous ceux qui veulent s'amuser à afficher des sprites et les faire progresser à l'écran dans une direction donnée:

http://www.anddev.org/basic_and_simple_ … t3085.html

Ici, ce sont des petites boules qui se balladent et qui sont dessinées à l'aide de canvas.drawBitmap.

Maintenant, j'aimerai pouvoir améliorer ce code et empêcher la collision entre ces boules pour les faire rebondir avant le choc :P

En Java, il y a la méthode Intersects qui existent pour détecter dès qu'il y a une intersection entre deux objets et qui fait ça simplement (avec des rectangles par exemple). Par contre, sur Android, je ne trouve pas l'équivalent avec les canvas.

Est-ce que quelqu'un aurait une idée? :)

Lien vers le commentaire
Partager sur d’autres sites

Merci Pierre87!

Seulement, comme j'utilise des Canvas et non des lignes ou des rectangles, je crains de devoir gérer deux objets à la fois (en superposant le rectangle au canvas...). Ou peut-être que je n'ai rien compris? :|

Bon, je vais y réfléchir! Merci beaucoup en tout cas! :)

Lien vers le commentaire
Partager sur d’autres sites

je ne sait pas ce que tu utilisais en java "classique"

mais pour un "objet" de ton jeu, c'est peut être plus propre de séparer collision et dessin

un peu comme dans les jeux en 3D, où tu sépares le modele de la hitbox

comme ça le modele peut etre très détaillé, alors que la hitbox est relativement simple

bon dans ton cas c'est des canvas, mais ... :P

Lien vers le commentaire
Partager sur d’autres sites

je ne sait pas ce que tu utilisais en java "classique"

mais pour un "objet" de ton jeu, c'est peut être plus propre de séparer collision et dessin

un peu comme dans les jeux en 3D, où tu sépares le modele de la hitbox

comme ça le modele peut etre très détaillé, alors que la hitbox est relativement simple

bon dans ton cas c'est des canvas, mais ... :P

Je jouais juste avec des lignes et des rectangles en java "classique" et là mes ambitions sont donc un peu plus élevées :) Mais ce que tu écris me parait plein de bon sens alors je vais me lancer comme ça :lol:

Merci encore!

Lien vers le commentaire
Partager sur d’autres sites

Salut,

si tu as des sprites, une solution pour detecter les collisions de maniere precise (c'est a dire que si tu as des spheres qui se deplacent, tu veux tester l'intersection de la sphere "geometrique" et non l'intersection du rectangle du bitmap) est de faire un test par pixel.

L'idee est que si tu as des sprites avec un background transparent (ie l'alpha de la couleur du background est 0), il suffit alors de tester si pour une position donnee, les 2 pixels correspondant dans tes sprites ont une couleur avec un alpha !=0. Si c'est le cas, alors il y a collision, car il s'agit de deux pixels qui ne font pas partis du background des sprites.

Ci-dessous un petit code qui sera plus clair que mon charabia ;-) :

ou`:

sprite1 et sprite2 sont tes 2 sprites a` tester,

cdata1 est le resultat de sprite1.getBitmap().getPixels(...);

cdata2 idem,

   private Rect checkCollision(BitmapDrawable sprite1, BitmapDrawable sprite2) {
       Rect rect1 = sprite1.getBounds();
       Rect rect2 = sprite2.getBounds();
       Rect inters = new Rect(rect1);
       if (inters.intersect(rect2)) {
           for (int y = inters.top; y < inters.bottom; ++y) {
               for (int x = inters.left; x < inters.right; ++x) {
                   int c1 = cdata1[(x - rect1.left) + (y - rect1.top)
                           * rect1.width()];
                   int c2 = cdata2[(x - rect2.left) + (y - rect2.top)
                           * rect2.width()];
                   int a1 = (int) (c1 >> 24);
                   int a2 = (int) (c2 >> 24);
                   if (a1 != 0 && a2 != 0)
                       return inters;
               }
           }
       }
       return null;
   }

On regarde d'abord l'intersection des deux bitmaps. Si il y a une intersection, alors on verifie pour chaque pixel de l'intersection la couleur des pixels correspondant pour les 2 sprites. Si les 2 couleurs ont une alpha != 0 (c'est a dire que ce ne sont pas des couleurs transparentes, donc que ce ne sont pas des couleurs du background), alors il y a collision.

Si ce n'est pas clair, je joindrai un petit exemple.

Patrick

Lien vers le commentaire
Partager sur d’autres sites

et bien la` en l'occurence il s'agit de collisions de sprites, et une question en amenant une autre, il aura ainsi une methode pour le jour ou` les spheres seront remplacees par des sprites plus compliques. La curiosite' n'est pas un vilain defaut ;-)

Patrick

Modifié par glhu
Lien vers le commentaire
Partager sur d’autres sites

et bien la` en l'occurence il s'agit de collisions de sprites, et une question en amenant une autre, il aura ainsi une methode pour le jour ou` les spheres seront remplacees par des sprites plus compliques. La curiosite' n'est pas un vilain defaut ;-)

Patrick

Salut Patrick!

Merci pour ton intervention. :P Ca tombe plutôt bien car en effet, en gérant avec des rectangles mes collisions, je n'ai parfois pas un super résultat: quand les deux boules rentrent en collision en diagonale, on voit bien que je teste mes collisions sur des rectangles car elles ne se touchent même pas avant de se repousser!

Je vais donc tenter ta méthode. :) C'est vrai que ça devient tout de suite plus compliqué par contre.

Si je comprends bien, tu testes ligne par ligne la collision entre chaque pixel de sprite1 et sprite2. Mais si c'est ça, je ne pige pas la suite car j'ai l'impression que dans c1 et c2, tu considères des zônes bien plus grandes...

Qu'est supposé contenir le tableau cdata1 (et cdata2) ? La réponse à cette question m'éclairera sur le shift de 24 bits aussi j'imagine!). :lol:

Merci bien en tout cas!

Lien vers le commentaire
Partager sur d’autres sites

Salut Stilgardt,

Si je comprends bien, tu testes ligne par ligne la collision entre chaque pixel de sprite1 et sprite2. Mais si c'est ça, je ne pige pas la suite car j'ai l'impression que dans c1 et c2, tu considères des zônes bien plus grandes...

Qu'est supposé contenir le tableau cdata1 (et cdata2) ? La réponse à cette question m'éclairera sur le shift de 24 bits aussi j'imagine!). :lol:

Je ne teste pas chaque pixel de sprite1 et sprite2 (cela serait inutilement couteux), mais seulement l'intersection des deux sprites (regarde la doc de rect.intersect, cette methode modifie 'rect' avec le rectangle d'intersection). Donc, pour chaque point de cette intersection, je regarde dans chaque bitmap la valeur du pixel correspondant. Pour cela, j'utilise l'api Bitmap.getPixels, qui stocke dans un tableau (ici cdata1) les valeurs de la couleur de chaque pixel du bitmap.

Et comme la position (x,y) que je teste lorsque j'itere sur les points de mon intersection est exprimee dans le systeme de coordonnees de l'ecran, je fais juste un changement de coordonnees pour me remettre dans le systeme du sprite (d'ou le x-spritePos.x, etc.).

Enfin, ayant les deux couleurs pour une position donnee, je regarde leur channel alpha. Comme la represention 'int' d'une couleur est exprimee en AARRGGBB, je decale de 24 bits. Tu peux tout aussi bien utilise Color.alpha(int color). qui fait la meme chose et qui a le merite de ne pas compliquer ton code pour rien :)

Je remets ci-dessous une version plus propre du code de la View. Dans cette exemple, les deux sprites cessent de se deplacer des qu'une intersection est detectee.

Et une image qui montre le resultat avec un bitmap autre qu'une sphere....

http://yfrog.com/5ecollisionbp

J'espere que c'est plus clair. A toi de jouer ;)

Patrick



import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.view.View;

public class BounceView extends View {

   protected GameEntity sprite1;
   protected GameEntity sprite2;
   private boolean nomove = false;

   protected enum HorizontalDirection {
       LEFT, RIGHT
   }

   protected enum VerticalDirection {
       UP, DOWN
   }

   public class GameEntity {
       private HorizontalDirection myXDirection = HorizontalDirection.RIGHT;;
       private VerticalDirection myYDirection = VerticalDirection.UP;;
       // The sprite
       public BitmapDrawable sprite;
       // The sprite position
       private Point spritePos;
       // The bitmap pixels value (ie colors)
       private int[] colors;
       private int bmpW;
       private int bmpH;

       public GameEntity(BitmapDrawable sprite, Point initPos){
           this.sprite = sprite;
           this.spritePos = initPos;
           Bitmap bmp = sprite.getBitmap();
           bmpW = bmp.getWidth();
           bmpH = bmp.getHeight();
           colors = new int[bmpW*bmpH];
           bmp.getPixels(colors, 0, bmpW, 0, 0, bmpW, bmpH);
           sprite.setBounds(spritePos.x, spritePos.y,
                   spritePos.x + bmpW, spritePos.y + bmpH);
       }
       public void update() {
           if (spritePos.x >= BounceView.this.getWidth() - bmpW) {
               myXDirection = HorizontalDirection.LEFT;
           } else if (spritePos.x <= 0) {
               myXDirection = HorizontalDirection.RIGHT;
           }
           if (spritePos.y >= BounceView.this.getHeight()- bmpH) {
               myYDirection = VerticalDirection.UP;
           } else if (spritePos.y <= 0) {
               myYDirection = VerticalDirection.DOWN;
           }

           if (myYDirection == VerticalDirection.DOWN) {
               spritePos.y += 2;
           } else {
               spritePos.y -= 2;// mHeading;
           }
           if (myXDirection == HorizontalDirection.RIGHT) {
               spritePos.x += 2;
           } else {
               spritePos.x -= 2;
           }
           sprite.setBounds(spritePos.x, spritePos.y,
                            spritePos.x + bmpW, spritePos.y + bmpH);


       }
       public void draw(Canvas canvas){
           sprite.draw(canvas);
       }
       public int getPixelAlpha(int x, int y){
           return Color.alpha(colors[(x - spritePos.x) + (y - spritePos.y)* bmpW]);
       }
   }

   public BounceView(Context context) {
       super(context);
       // Set the background
       this.setBackgroundDrawable(this.getResources().getDrawable(
               R.drawable.icon));
       sprite1 = new GameEntity((BitmapDrawable) this.getResources().getDrawable(
               R.drawable.ball2), new Point());
       sprite2 = new GameEntity((BitmapDrawable) this.getResources().getDrawable(
               R.drawable.arrow), new Point(320,-20));
   }

   @Override
   protected void onDraw(Canvas canvas) {

       /*
        * Set the location, where the sprite will draw itself to the canvas
        */
       if (!nomove) {
           sprite1.update();
           sprite2.update();

           Rect r = checkCollision(sprite1, sprite2);
           if (r != null) {
               this.nomove = true;
           }
       }
       /* Make the sprite draw itself to the canvas */
       sprite1.draw(canvas);
       sprite2.draw(canvas);
   }

   private static Rect checkCollision(GameEntity sprite1, GameEntity sprite2) {
       Rect rect1 = sprite1.sprite.copyBounds();
       Rect rect2 = sprite2.sprite.getBounds();
       if (rect1.intersect(rect2)) {
           for (int y = rect1.top; y < rect1.bottom; ++y) {
               for (int x = rect1.left; x < rect1.right; ++x) {
                   int a1 = sprite1.getPixelAlpha(x,y);
                   int a2 = sprite2.getPixelAlpha(x,y);
                   if (a1 != 0 && a2 != 0)
                       return rect1;
               }
           }
       }
       return null;
   }            
}

Modifié par glhu
Lien vers le commentaire
Partager sur d’autres sites

Chouette! Merci :-)

Sans tes explications, j'aurai passé beaucoup de temps à chercher (on dit que ça forme le caractère mais parfois, c'est bien d'aller plus vite! ;-))

Je pense avoir compris tout ton code cette fois :) et je vais pouvoir m'amuser à gérer des collisions.

Par contre, j'ai dû ajouter:

// refresh the canvas

invalidate();

à la fin de la méthode OnDraw sinon les sprites restaient immobiles.

Merci encore Patrick et à charge de revanche! ;-)

Lien vers le commentaire
Partager sur d’autres sites

Par contre, j'ai dû ajouter:

// refresh the canvas

invalidate();

à la fin de la méthode OnDraw sinon les sprites restaient immobiles.

Ah, bizarre. De mon cote', j'utilisais cette vue dans l'exemple de Profete162 (modifie' pour ne pas utiliser l'accelerometre). Bon, l'essentiel est que tu ais reussi a` l'adapter pour tes besoins.

Lien vers le commentaire
Partager sur d’autres sites

Une derniere remarque: je pense qu'il faudrait que tu invalides juste la zone de tes sprites et non tout l'ecran (avec invalidate(rect), ou` rect est l'union des bounds des sprites)

Ok, je vais modifier ça ce soir pour voir si je constate une différence.

Il ne reste plus qu'à trouver une réaction cohérente aux sprites lors de leur collision et ce sera parfait!

Je joue en ce moment à Crysis sur PC et leur gestion de la physique des objets et de l'environnement me fait bien rêver... :cool:

Lien vers le commentaire
Partager sur d’autres sites

Rejoignez la conversation

Vous pouvez poster maintenant et vous enregistrez plus tard. Si vous avez un compte, connectez-vous maintenant pour poster.

Invité
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Répondre à ce sujet…

×   Collé en tant que texte enrichi.   Coller en tant que texte brut à la place

  Seulement 75 émoticônes maximum sont autorisées.

×   Votre lien a été automatiquement intégré.   Afficher plutôt comme un lien

×   Votre contenu précédent a été rétabli.   Vider l’éditeur

×   Vous ne pouvez pas directement coller des images. Envoyez-les depuis votre ordinateur ou insérez-les depuis une URL.

×
×
  • Créer...