Pixel perfect collision detection

Well I tried to post the code but the forum software says it is too long so the link is:

http://www.scottshaver2000.com/ImageFunctions.java


class CollisionMask
{
      int width, height, maskWidth;
      int mask[];

      public CollisionMask ( Image image, int width, int height )
      {
            this ( image, 0, 0, width, height );
      }
      
      public CollisionMask ( Image image, int x, int y, int width, int height )
      {
            this.width = width;
            this.height = height;
            maskWidth = ( width >> 5 ) + ( ( width % 32 == 0 ) ? 0 : 1 );
            
            mask = new int[ maskWidth * height ];
            
            int pixels[] = new int[ width * height ];
            PixelGrabber pixelGrabber = new PixelGrabber ( image, x, y, width, height, pixels, 0, width );

            try
            {
                  pixelGrabber.grabPixels ( );
            }
            catch ( InterruptedException e )
            {
                  throw new RuntimeException ( "Error grabbing pixels!" );
            }
            
            if ( ( pixelGrabber.getStatus ( ) & ImageObserver.ABORT ) != 0 )
            {
                  throw new RuntimeException ( "Error grabbing pixels!" );
            }
            
            for ( int i = 0; i < height; i++ )
            {
                  for ( int j = 0; j < width; j++ )
                  {
                        if ( ( pixels[ j + ( i * width ) ] & 0xFF000000 ) != 0 )
                        {
                              mask[ ( j >> 5 ) + ( i * maskWidth ) ] |= 0x80000000 >>> ( j % 32 );
                        }
                  }
            }
      }
      
      public boolean collidesWith ( CollisionMask b, int x1, int y1, int x2, int y2 )
      {
            // Check if the masks even overlap
            if ( ( x1 >= x2 + b.width ) || ( x1 + width <= x2 ) ||
                   ( y1 >= y2 + b.height ) || ( y1 + height <= y2 ) )
            {
                  return false;
            }
            
            int ax, ay, bx, by;
            
            if ( y1 < y2 )
            {
                  ay = y2 - y1;
                  by = 0;
            }
            else
            {
                  ay = 0;
                  by = y1 - y2;
            }
            
            if ( x1 < x2 )
            {
                  int dx = x2 - x1;
                  int aStart = dx >> 5;
                  int shift = dx % 32;
                  int aOffset = aStart + ( ay * maskWidth );
                  int bOffset = by * b.maskWidth;
                  
                  if ( shift == 0 )      // Masks are aligned, we do not need to shift pixels
                  {                  
                        while ( ( ay < height ) && ( by < b.height ) )
                        {
                              ax = aStart;
                              bx = 0;
                                                            
                              while ( ( ax < maskWidth ) && ( bx < b.maskWidth ) )
                              {
                                    if ( ( mask[ aOffset ] & b.mask[ bOffset ] ) != 0 )
                                    {
                                          return true;
                                    }
                                    
                                    aOffset++;
                                    bOffset++;
                                    ax++;
                                    bx++;
                              }

                              aOffset += maskWidth - ( ax - 1 ) + aStart - 1;
                              bOffset += b.maskWidth - ( bx - 1 ) - 1;
                              ay++;
                              by++;
                        }
                  }
                  else                  // Masks are not aligned, we need to shift pixels
                  {
                        int invShift = 32 - shift;
                  
                        while ( ( ay < height ) && ( by < b.height ) )
                        {
                              ax = aStart;
                              bx = 0;
                              
                              while ( bx < b.maskWidth )
                              {
                                    if ( ax < maskWidth - 1 )
                                    {
                                          if ( ( ( ( mask[ aOffset ] << shift ) |
                                                       ( mask[ aOffset + 1 ] >>> invShift ) ) &
                                                   b.mask[ bOffset ] ) != 0 )
                                          {
                                                return true;
                                          }
                                    }
                                    else
                                    {
                                          if ( ( ( mask[ aOffset ] << shift ) & b.mask[ bOffset ] ) != 0 )
                                          {
                                                return true;
                                          }

                                          aOffset++;
                                          bOffset++;
                                          ax++;
                                          bx++;

                                          break;
                                    }
                                    
                                    aOffset++;
                                    bOffset++;
                                    ax++;
                                    bx++;
                              }
                              
                              aOffset += maskWidth - ( ax - 1 ) + aStart - 1;
                              bOffset += b.maskWidth - ( bx - 1 ) - 1;
                              ay++;
                              by++;
                        }
                  }
            }
            else
            {
                  int dx = x1 - x2;
                  int bStart = dx >> 5;
                  int shift = dx % 32;
                  int aOffset = ay * maskWidth;
                  int bOffset = bStart + ( by * b.maskWidth );
                  
                  if ( shift == 0 )      // Masks are aligned, we do not need to shift pixels
                  {
                        while ( ( ay < height ) && ( by < b.height ) )
                        {
                              ax = 0;
                              bx = bStart;
                              
                              while ( ( ax < maskWidth ) && ( bx < b.maskWidth ) )
                              {
                                    if ( ( mask[ aOffset ] & b.mask[ bOffset ] ) != 0 )
                                    {
                                          return true;
                                    }
                                    
                                    aOffset++;
                                    bOffset++;
                                    ax++;
                                    bx++;
                              }
                              
                              aOffset += maskWidth - ( ax - 1 ) - 1;
                              bOffset += b.maskWidth - ( bx - 1 ) + bStart - 1;
                              ay++;
                              by++;
                        }
                  }
                  else                        // Masks are not aligned, we need to shift pixels
                  {
                        int invShift = 32 - shift;
                  
                        while ( ( ay < height ) && ( by < b.height ) )
                        {
                              ax = 0;
                              bx = bStart;
                              
                              while ( ax < maskWidth )
                              {
                                    if ( bx < b.maskWidth - 1 )
                                    {
                                          if ( ( ( ( b.mask[ bOffset ] << shift ) |
                                                       ( b.mask[ bOffset + 1 ] >>> invShift ) ) &
                                                   mask[ aOffset ] ) != 0 )
                                          {
                                                return true;
                                          }
                                    }
                                    else
                                    {
                                          if ( ( mask[ aOffset ] & ( b.mask[ bOffset ] << shift ) ) != 0 )
                                          {
                                                return true;
                                          }
                                          
                                          aOffset++;
                                          bOffset++;
                                          ax++;
                                          bx++;
                                          
                                          break;
                                    }

                                    aOffset++;
                                    bOffset++;
                                    ax++;
                                    bx++;
                              }

                              aOffset += maskWidth - ( ax - 1 ) - 1;
                              bOffset += b.maskWidth - ( bx - 1 ) + bStart - 1;
                              ay++;
                              by++;
                        }
                  }
            }
            
        return false;
      }
}

I posted my pixel-perfect collision detection routines. The differences to zparticle’s are mainly that I use Java 1.2 and that I keep the mask in a one-dimensional array.

I tried to optimize my code so that only simple operations (+, -, <<, >>>) would be performed in the loops. I don’t need any multiplications to index ints in the collision masks because I keep an offset, which gets incremented appropriately as the loop executes.

I know it’s kind of messy, but it is fairly low-level bitmanipulation code. Also, my optimization strategy was basically just intuition. I didn’t think too much about it, especially in the context of Java. So if you see some obvious problem/optimization/simplification please tell me.

I tested this code for quite a bit today, so I am fairly confident that there are no bugs. Again, feel free to prove me wrong.

from, Stefan

[quote]Well I tried to post the code but the forum software says it is too long so the link is:

http://www.scottshaver2000.com/ImageFunctions.java
[/quote]
Can we request for a version of your code that uses single images rather than array so it will be easier to understand it please.

Thanx.

Few questions on your code please.

for(int big=0;big<50;big++)

why only traverse the loop till 50 ?

first = com.sun.j3d.utils.timer.J3DTimer.getValue(); //System.currentTimeMillis();

second = com.sun.j3d.utils.timer.J3DTimer.getValue(); //System.currentTimeMillis();

why do you do this even though it is not needed for collision detection ?

Would be nice if you answer please :slight_smile:

Thanx.

[quote]Few questions on your code please.

why only traverse the loop till 50 ?

first = com.sun.j3d.utils.timer.J3DTimer.getValue(); //System.currentTimeMillis();

second = com.sun.j3d.utils.timer.J3DTimer.getValue(); //System.currentTimeMillis();
[/quote]
The stuff in the main method was just me testing the functions, it has nothing to do with the actuall collision detection, please ignore it.

The code already has methods for both single images and arrays of images:

static public int[][][] createPixelMasks(Image[] spriteImages, GraphicsConfiguration gc)

static public int[][] createPixelMask(Image spriteImage, GraphicsConfiguration gc)

And then the actuall collision detection acts on two masks which have to be arrays for the code to work:

public final static boolean maskCollision(int[][] mask1, int x1, int y1, int width1, int height1,
int[][] mask2, int x2, int y2, int width2, int height2)

basically the masks are simply bits representing each pixel in the images to check for a collision. The bits are on for pixels that can collide and off for pixels that can’t.

I should note that this code is based heavy one another persons work, although I can’t remember who it was. I think he used to be a member here.