UnderwareDESIGN

PlayBASIC => Resources => Source Codes => Topic started by: kevin on May 02, 2018, 11:56:06 AM

Title: Manually Doing Pixel Perfect Sprite Collisions (Examples & Video)
Post by: kevin on May 02, 2018, 11:56:06 AM
Manually Doing Pixel Perfect Sprite Collisions (Examples & Video)

 In this example we look over three methods for doing Pixel Perfect sprite collisions manually in  program code, rather than using the built in sprite collision method (see SpriteCollisionMode) .  

 Sprite Collision Method 1:   Raw Image (point by point) - The first example computes the common area the two sprites share and then reads the source pixels from the source sprite, checks for a mask colour then does the same for the Destination sprite.  If the two pixels are Not mask colour, then we have a collision.     This method works but there's a lot of surface overhead constantly pulling individual pixels from the surfaces via Point()  


Sprite Collision Method 2:  Byte Mask (Preprocess images into a 8bit Collision Mask) - The second method actually works almost the same as the first, except this time we create a collision mask of each image. The mask is an 8 bit version of the image, where each byte in the mask is either 0 for transparent or 1 for solid.    The sprite collision routine then  uses the same logic as method #1 but directly reads the mask bytes rather than reading pixels.   This means it doesn't matter what type of image format (video/ FX/ AFX) the cost of reading the mask is the same regardless.


 Sprite Collision Method #3:  Span Buffer (Preprocess the images into a span list of hard pixels only) - The last method we look at is an extension to the mask concept, expect rather than store pixel data, we scan the image and make list of hard spans (none transparent pixels) for each row.   Each span is just two values, the  starting coordinate and length of the span.  The data structure contains a table at the top of offsets into the span list, so we can look up each row as need be.    Rows with no solid pixels, don't create any data, so we're only every comparing solid pixels to solid pixels.  The regions might not overlap, but for most sprites it all boils down to only a couple of compares per row..

 
 PlayBASIC has a number of sprite collision methods built in, ranging from simple bounding box intersections, rotated bounding box,  Vector Shape ( Polygon Collision supporting Convex / Concave & complex polygons),  Various sliding methods through to Pixel Perfect collision mode.



Video

 




Sample Code

   This is the pixel perfect sprite collision function from the first demo, it basically just computes if the two sprites share a common area and then runs through the pixel data point() by point() exiting when it finds two pixels that aren't mask colour



[pbcode]
; *=---------------------------------------------------------------------=*
; *=---------------------------------------------------------------------=*
; *=---------------------------------------------------------------------=*
;                        >> Sprite Hit Pixels <<
; *=---------------------------------------------------------------------=*
; *=---------------------------------------------------------------------=*
; *=---------------------------------------------------------------------=*
;     This function check if two spites overlap at pixel level.  
;
;
; *=---------------------------------------------------------------------=*
      
      constant SpriteHitPixels_DEBUG = false
      
      
function SpriteHitPixels(SrcSprite,DestSprite)
         
      SrcX1      =GetSpriteX(SrcSprite)+GetSpriteHandleX(SrcSprite)
      SrcY1      =GetSpriteY(SrcSprite)+GetSpriteHandleY(SrcSprite)
      SrcWidth   =GetSpriteWidth(SrcSprite)
      SrcHeight=GetSpriteHeight(SrcSprite)
      

      DestX1      =GetSpriteX(DestSprite)+GetSpriteHandleX(DestSprite)
      DestY1      =GetSpriteY(DestSprite)+GetSpriteHandleY(DestSprite)
      DestWidth   =GetSpriteWidth(DestSprite)
      DestHeight   =GetSpriteHeight(DestSprite)

      //  Manually check if the two sprites at least share the same area

      ;
      if (SrcX1+SrcWidth)  > DestX1
         if SrcX1            <(DestX1+DestWidth)
         
            if (SrcY1+SrcHeight)  > DestY1
               if SrcY1            <(DestY1+DestHeight)

                     //Status=true
         
                     // Scan through smallest region  of the two
                     OldSurface         =GetSurface()
                     
                     SrcImage            =GetSpriteImage(SrcSprite)
                     SrcImageMaskColour =GetImageMaskColour(SrcImage)
                     
                     DestImage            =GetSpriteImage(DestSprite)
                     DestImageMaskColour  =GetImageMaskColour(DestImage)
                     

                     // Compute the rect the two images share
                     ClipX1=MaxVal(SrcX1,DestX1)
                     ClipX2=MinVal(SrcX1+SrcWidth,DestX1+DestWidth)
   
                     ClipY1=MaxVal(SrcY1,DestY1)
                     ClipY2=MinVal(SrcY1+SrcHeight,DestY1+DestHeight)
   
   
                     #IF SpriteHitPixels_DEBUG=true
                        Boxc DestX1,DestY1,DestX1+DestWidth,DestY1+DestHeight,False, Rgb(255,0,0)
                        Box ClipX1,ClipY1,ClipX2,ClipY2,false
                     #ENDIF


                     // Translate the world space cords to local
                     // image space cordinates
                     
                     OffsetX  =SrcX1 -DestX1
                     OffsetY  =SrcY1 -DestY1
                     

                     ClipX1 -=SrcX1
                     ClipX2 -=SrcX1
                     ClipY1 -=SrcY1
                     ClipY2 -=SrcY1

                     OldSurface=GetSurface()


                     // Brute force scan through images
                     for ScanLPY = ClipY1 to ClipY2

                        // Check if this row overlap   
                        rendertoimage SrcImage
                        for ScanLPX = ClipX1 to ClipX2
                  
                              ThisPixel1=Point(ScanLPX,ScanLPY)
                              if ThisPixel1!=SrcImageMaskColour

                                 rendertoimage DestImage
                  
                                 ThisPixel2=Point(OffsetX+ScanLPX,OffsetY+ScanLPY)
                                 if ThisPixel2!=DestImageMaskColour
                                    STATUS=TRUE

                                    #IF SpriteHitPixels_DEBUG=false
                                     eXITfor ScanLpY
                                    #endif   

                                    #IF SpriteHitPixels_DEBUG=true
                                       rendertoimage 0
                                       dotc OffsetX+ScanLPX,OffsetY+ScanLPY,$00ff00   ;ThisPixel2                              
                                    #endif
                                 endif
                                 rendertoimage SrcImage
                              endif
                        next
                     next
         
                     rendertoimage OldSurface
         
               endif   
            endif   
            
         endif   
      endif   

EndFunction Status


[/pbcode]


Related Links

   + LowRES LIbrary for PlayBASIC (https://www.underwaredesign.com/forums/index.php?topic=4725.0)
   + PlayBASIC Sprite Help Files (http://playbasic.com/help.php?page=SPRITES.INDEX)



Downloads

   The examples are attached to this post. Log in to download them.