pixel perfect sprite collision

Post your FreeBASIC tips and tricks here. Please don’t post your code without including an explanation.
BasicCoder2
Posts: 3472
Joined: Jan 01, 2009 7:03

pixel perfect sprite collision

Postby BasicCoder2 » Mar 16, 2012 2:10

Code: Select all


#include "fbgfx.bi"
using fb
screenres 640,480,32

type SPRITE
    as integer x   'top left coordinates
    as integer y
    as integer w   'width
    as integer h   'height
    as double  r   'degrees of rotation
    as double  px  'point around which to rotate
    as double  py
    as integer d   'display = 1
    as FB.Image ptr image
end type

dim shared as SPRITE diamond  'shared makes them GLOBAL variables
dim shared as SPRITE star1

'create two bitmaps
diamond.image = ImageCreate( 64, 64, 0 )
star1.image = imageCreate( 64, 64, 0 )

function ImagesCollide(s1 as SPRITE, s2 as SPRITE) as integer
    dim as uinteger pixel_value
    dim as integer hit,x1,y1,x2,y2
   
    hit = 0
    for j as integer = s1.y to s1.y+s1.h-1
        for i as integer = s1.x to s1.x+s1.w-1
           
            if i > s2.x and i < s2.x+s2.w then
                if j > s2.y and j < s2.y + s2.h then
                    x1 = i-s1.x
                    y1 = j-s1.y
                    x2 = i-s2.x
                    y2 = j-s2.y
                    if Point(x1,y1, s1.image) <> rgb(255,0,255) and Point(x2,y2,s2.image) <> rgb(255,0,255) then
                        hit = 1
                    end if
                end if
            end if
           
        next i
    next j

    return hit
end function


sub drawSprites()
    screenlock
    color rgb(255,255,255) 'white ink
    cls
    locate 1,1
    print "USE CURSOR KEYS TO MOVE BALL"
    print "HIT ESC KEY TO QUIT GAME"
   
    if star1.d = 1 then
        put (star1.x,star1.y),star1.image,trans
    end if
    'test for collision
    locate 41,1
    if ImagesCollide(diamond,star1) then
        'paint diamond.image,(diamond.x+diamond.w\2,diamond.y+diamond.h\2),rgb(110,10,255)
        print "COLLISION"
    else
        print "         "
        'paint diamond.image,(diamond.x+diamond.w\2,diamond.y+diamond.h\2),rgb(240,10,25)
    end if
    'diamond sprite is drawn after and thus over star1 sprite
    if diamond.d = 1 then                 
        put (diamond.x,diamond.y),diamond.image,trans
    end if
   
    screenunlock

    sleep 1,1
end sub

sub updateData()
    ' Check arrow keys and update position accordingly
    IF MULTIKEY(&h4B) AND diamond.x > 0 THEN diamond.x = diamond.x - 1
    IF MULTIKEY(&h4D) AND diamond.x < 639 THEN diamond.x = diamond.x + 1
    IF MULTIKEY(&h48) AND diamond.y > 0 THEN diamond.y = diamond.y - 1
    IF MULTIKEY(&h50) AND diamond.y < 479 THEN diamond.y = diamond.y + 1
    'Check and adjust for outOfBounds
    if diamond.x <0 then diamond.x = 0
    if diamond.x > 640 - diamond.w then diamond.x = 640-diamond.w
    if diamond.y <0 then diamond.y = 0
    if diamond.y > 480 - diamond.h then diamond.y = 480-diamond.h
end sub



'read data for diamond sprite
dim as uinteger pixelValue
dim as string datum
for j as integer = 0 to 63
    read datum
    for i as integer = 0 to 63
        pixelValue = val(mid(datum,i+1,1))
        if pixelValue = 1 then
            pset diamond.image,(i,j),rgb(255,0,0)  'red
        else
            pset diamond.image,(i,j),rgb(255,0,255)
        end if
    next i
next j

diamond.w = 64
diamond.h = 64
diamond.x = 200
diamond.y = 200
diamond.d = 1

for j as integer = 0 to 63
    read datum
    for i as integer = 0 to 63
        pixelValue = val(mid(datum,i+1,1))
        if pixelValue = 1 then
            pset star1.image,(i,j),rgb(0,255,0) 'green
        else
            pset star1.image,(i,j),rgb(255,0,255)
        end if
    next i
next j

star1.w = 64
star1.h = 64
star1.x = 100
star1.y = 100
star1.d = 1 

'*****************  MAIN LOOP ******************
drawSprites()
do

    drawSprites()
    updateData()
   
loop until multikey(&H01)  'esc key

'clean up
ImageDestroy(diamond.image)
ImageDestroy(star1.image)

end
DATA "0000000000000000000000000000000000000000000000000000000000000000"
DATA "0000000000000000000000000000000010000000000000000000000000000000"
DATA "0000000000000000000000000000000010000000000000000000000000000000"
DATA "0000000000000000000000000000000111000000000000000000000000000000"
DATA "0000000000000000000000000000000111000000000000000000000000000000"
DATA "0000000000000000000000000000000111000000000000000000000000000000"
DATA "0000000000000000000000000000001111100000000000000000000000000000"
DATA "0000000000000000000000000000001111100000000000000000000000000000"
DATA "0000000000000000000000000000001111100000000000000000000000000000"
DATA "0000000000000000000000000000011111110000000000000000000000000000"
DATA "0000000000000000000000000000011111110000000000000000000000000000"
DATA "0000000000000000000000000000011111110000000000000000000000000000"
DATA "0000000000000000000000000000111111111000000000000000000000000000"
DATA "0000000000000000000000000000111111111000000000000000000000000000"
DATA "0000000000000000000000000000111111111000000000000000000000000000"
DATA "0000000000000000000000000001111111111100000000000000000000000000"
DATA "0000000000000000000000000001111111111100000000000000000000000000"
DATA "0000000000000000000000000001111111111100000000000000000000000000"
DATA "0000000000000000000000000011111111111110000000000000000000000000"
DATA "0000000000000000000000000011111111111110000000000000000000000000"
DATA "0000000000000000000000000011111111111110000000000000000000000000"
DATA "0000000000000000000000000111111111111111000000000000000000000000"
DATA "0000000000000000000000000111111111111111000000000000000000000000"
DATA "0000000000000000000000000111111111111111000000000000000000000000"
DATA "0111111111111111111111111111111111111111111111111111111111111111"
DATA "0011111111111111111111111111111111111111111111111111111111111110"
DATA "0001111111111111111111111111111111111111111111111111111111111100"
DATA "0000111111111111111111111111111111111111111111111111111111111000"
DATA "0000001111111111111111111111111111111111111111111111111111100000"
DATA "0000000111111111111111111111111111111111111111111111111111000000"
DATA "0000000011111111111111111111111111111111111111111111111110000000"
DATA "0000000000111111111111111111111111111111111111111111111000000000"
DATA "0000000000011111111111111111111111111111111111111111110000000000"
DATA "0000000000001111111111111111111111111111111111111111100000000000"
DATA "0000000000000011111111111111111111111111111111111110000000000000"
DATA "0000000000000001111111111111111111111111111111111100000000000000"
DATA "0000000000000000111111111111111111111111111111111000000000000000"
DATA "0000000000000000001111111111111111111111111111100000000000000000"
DATA "0000000000000000000111111111111111111111111111000000000000000000"
DATA "0000000000000000000111111111111111111111111111000000000000000000"
DATA "0000000000000000000111111111111111111111111111000000000000000000"
DATA "0000000000000000000111111111111111111111111111000000000000000000"
DATA "0000000000000000001111111111111111111111111111100000000000000000"
DATA "0000000000000000001111111111111111111111111111100000000000000000"
DATA "0000000000000000001111111111111111111111111111100000000000000000"
DATA "0000000000000000001111111111111111111111111111100000000000000000"
DATA "0000000000000000011111111111111111111111111111110000000000000000"
DATA "0000000000000000011111111111111111111111111111110000000000000000"
DATA "0000000000000000011111111111111111111111111111110000000000000000"
DATA "0000000000000000111111111111111000111111111111111000000000000000"
DATA "0000000000000000111111111111110000011111111111111000000000000000"
DATA "0000000000000000111111111111100000001111111111111000000000000000"
DATA "0000000000000001111111111110000000000011111111111100000000000000"
DATA "0000000000000001111111111100000000000001111111111100000000000000"
DATA "0000000000000001111111110000000000000000011111111100000000000000"
DATA "0000000000000001111111100000000000000000001111111100000000000000"
DATA "0000000000000011111110000000000000000000000011111110000000000000"
DATA "0000000000000011111100000000000000000000000001111110000000000000"
DATA "0000000000000011110000000000000000000000000000011110000000000000"
DATA "0000000000000111100000000000000000000000000000001111000000000000"
DATA "0000000000000110000000000000000000000000000000000011000000000000"
DATA "0000000000000100000000000000000000000000000000000001000000000000"
DATA "0000000000000000000000000000000000000000000000000000000000000000"
DATA "0000000000000000000000000000000000000000000000000000000000000000"


DATA "0000000000000000000000000000000100000000000000000000000000000000"
DATA "0000000000000000000000000000001110000000000000000000000000000000"
DATA "0000000000000000000000000000011111000000000000000000000000000000"
DATA "0000000000000000000000000000111111100000000000000000000000000000"
DATA "0000000000000000000000000001111111110000000000000000000000000000"
DATA "0000000000000000000000000011111111111000000000000000000000000000"
DATA "0000000000000000000000000111111111111100000000000000000000000000"
DATA "0000000000000000000000001111111111111110000000000000000000000000"
DATA "0000000000000000000000011111111111111111000000000000000000000000"
DATA "0000000000000000000000111111111111111111100000000000000000000000"
DATA "0000000000000000000001111111111111111111110000000000000000000000"
DATA "0000000000000000000011111111111111111111111000000000000000000000"
DATA "0000000000000000000111111111111111111111111100000000000000000000"
DATA "0000000000000000001111111111111111111111111110000000000000000000"
DATA "0000000000000000011111111111111111111111111111000000000000000000"
DATA "0000000000000000111111111111111111111111111111100000000000000000"
DATA "0000000000000001111111111111111111111111111111110000000000000000"
DATA "0000000000000011111111111111111111111111111111111000000000000000"
DATA "0000000000000111111111111111111111111111111111111100000000000000"
DATA "0000000000001111111111111111111111111111111111111110000000000000"
DATA "0000000000011111111111111111111111111111111111111111000000000000"
DATA "0000000000111111111111111111111111111111111111111111100000000000"
DATA "0000000001111111111111111111111111111111111111111111110000000000"
DATA "0000000011111111111111111111111111111111111111111111111000000000"
DATA "0000000111111111111111111111111111111111111111111111111100000000"
DATA "0000001111111111111111111111111111111111111111111111111110000000"
DATA "0000011111111111111111111111111111111111111111111111111111000000"
DATA "0000111111111111111111111111111111111111111111111111111111100000"
DATA "0001111111111111111111111111111111111111111111111111111111110000"
DATA "0011111111111111111111111111111111111111111111111111111111111000"
DATA "0111111111111111111111111111111111111111111111111111111111111100"
DATA "0011111111111111111111111111111111111111111111111111111111111000"
DATA "0001111111111111111111111111111111111111111111111111111111110000"
DATA "0000111111111111111111111111111111111111111111111111111111100000"
DATA "0000011111111111111111111111111111111111111111111111111111000000"
DATA "0000001111111111111111111111111111111111111111111111111110000000"
DATA "0000000111111111111111111111111111111111111111111111111100000000"
DATA "0000000011111111111111111111111111111111111111111111111000000000"
DATA "0000000001111111111111111111111111111111111111111111110000000000"
DATA "0000000000111111111111111111111111111111111111111111100000000000"
DATA "0000000000011111111111111111111111111111111111111111000000000000"
DATA "0000000000001111111111111111111111111111111111111110000000000000"
DATA "0000000000000111111111111111111111111111111111111100000000000000"
DATA "0000000000000011111111111111111111111111111111111000000000000000"
DATA "0000000000000001111111111111111111111111111111110000000000000000"
DATA "0000000000000000111111111111111111111111111111100000000000000000"
DATA "0000000000000000011111111111111111111111111111000000000000000000"
DATA "0000000000000000001111111111111111111111111110000000000000000000"
DATA "0000000000000000000111111111111111111111111100000000000000000000"
DATA "0000000000000000000011111111111111111111111000000000000000000000"
DATA "0000000000000000000001111111111111111111110000000000000000000000"
DATA "0000000000000000000000111111111111111111100000000000000000000000"
DATA "0000000000000000000000011111111111111111000000000000000000000000"
DATA "0000000000000000000000001111111111111110000000000000000000000000"
DATA "0000000000000000000000000111111111111100000000000000000000000000"
DATA "0000000000000000000000000011111111111000000000000000000000000000"
DATA "0000000000000000000000000001111111110000000000000000000000000000"
DATA "0000000000000000000000000000111111100000000000000000000000000000"
DATA "0000000000000000000000000000011111000000000000000000000000000000"
DATA "0000000000000000000000000000001110000000000000000000000000000000"
DATA "0000000000000000000000000000000100000000000000000000000000000000"
DATA "0000000000000000000000000000000000000000000000000000000000000000"
DATA "0000000000000000000000000000000000000000000000000000000000000000"
DATA "0000000000000000000000000000000000000000000000000000000000000000"


Running this code might give a better visualization of how it works,

Code: Select all

screenres 640,480,32

 line (174,149)-(321,290),rgb(255,100,100),bf
 line (238,217)-(413,372),rgb(100,255,100),bf

 'green sprite 1
 line (50,50)-(174,149),rgb(255,0,0),b
 line (50,50)-(321,290),rgb(255,0,0),b
 line (174,149)-(321,290),rgb(255,0,0),b
 line (175,150)-(320,289),rgb(255,0,0),b

 'red sprite 1
 line (50,50)-(238,217),rgb(0,255,0),b
 line (50,50)-(413,372),rgb(0,255,0),b
 line (238,217)-(413,372),rgb(0,255,0),b
 line (239,218)-(412,371),rgb(0,255,0),b
 line (238,217)-(321,290),rgb(100,100,0),bf
'current coordinate of scanning loops
 line (50,50)-(280,247),rgb(50,50,255),b

'draw screen coordinates
 line (50,50)-(566,50),rgb(255,255,255)
 line (50,50)-(50,433),rgb(255,255,255)
 
 'shared point on screen i,j
 circle (280,247),5,rgb(0,0,255)
 
 draw string (22,28), "(0,0)"
 color rgb(255,0,0)
 draw string (157,33), "s1.x"
 draw string (307,33), "s1.x+s1.w"
 draw string (4,145),"s1.y"
 draw string (0,278),"s1.y+s1.h"
 color rgb(100,100,255)
 draw string (30,245),"j"
 draw string (280,33),"i"
 color rgb(0,255,0)
 draw string (224,33),"s2.x"
 draw string (404,33),"s2.x+s2.w"
 draw string (12,205),"s2.y"
 draw string (0,360),"s2.y+s2.h"

 sleep
Last edited by BasicCoder2 on Mar 16, 2012 11:19, edited 1 time in total.
codeFoil
Posts: 255
Joined: Dec 22, 2011 4:45
Location: United States
Contact:

Re: pixel perfect sprite collision

Postby codeFoil » Mar 16, 2012 4:02

Now that you have it working, you can speed things up a little by moving some of the calculations outside of the For loops.

These expressions don't change, so you can precalculate them:
s2.x + s2.w
s2.y + s2.h

Also, if you are only interested in detecting a collision and don't need every overlapping pixel, you could
return or exit the for statements as soon as you find the first hit. (For your example, though, I can see why you continue searching.)

Append: Try this:

Code: Select all

Function ImagesCollide(s1 as SPRITE, s2 as SPRITE) as Integer
#Define HIT 1
#Define NOHIT 0
    Dim as Integer x1,y1,x2,y2

    Dim as Integer s2Right = s2.x + s2.w
    Dim as Integer s2Bottom = s2.y + s2.h
   
    For j as Integer = s1.y to s1.y+s1.h-1
        If j > s2.y AndAlso j < s2Bottom Then
            y1 = j-s1.y
            y2 = j-s2.y
            For i as Integer = s1.x to s1.x+s1.w-1
                If i > s2.x AndAlso i < s2Right then
                    x1 = i-s1.x
                    x2 = i-s2.x
                    If      Point(x1,y1, s1.image) <> rgb(255,0,255) _
                    AndAlso Point(x2,y2,s2.image)  <> rgb(255,0,255) then
                        Return HIT
                    End if
                End if
            Next i
        End if
    Next j

    Return NOHIT

End function
Last edited by codeFoil on Mar 16, 2012 7:22, edited 4 times in total.
VANYA
Posts: 1362
Joined: Oct 24, 2010 15:16
Location: Ярославль
Contact:

Re: pixel perfect sprite collision

Postby VANYA » Mar 16, 2012 4:52

A good example!
codeFoil
Posts: 255
Joined: Dec 22, 2011 4:45
Location: United States
Contact:

Re: pixel perfect sprite collision

Postby codeFoil » Mar 16, 2012 9:10

I am suffering from insomnia at the moment.

Code: Select all

Function ImagesCollide(s1 as SPRITE, s2 as SPRITE) as Integer
#Define HIT   1
#Define NOHIT 0
#Define NO_PIXEL     (RGB(255, 0, 255)      )
#Define DEBUG_COLOUR (RGB(&HcF, &hcF, &hcF ))

    Dim as Integer x1, y1, x2, y2

    Dim as Integer s2Right  = s2.x + s2.w
    Dim as Integer s2Bottom = s2.y + s2.h
    Dim as Integer s1Right  = s1.x + s1.w
    Dim as Integer s1Bottom = s1.y + s1.h
   
    Dim as Integer j     = Iif(s1.y >= s2.y       , s1.y    , s2.y     )
    Dim as Integer jStop = Iif(s1Bottom < s2Bottom, s1Bottom, s2Bottom )

    Dim as Integer iStart = Iif(s1.x >= s2.x     , s1.x   , s2.x    )
    Dim as Integer iStop  = Iif(s1Right < s2Right, s1Right, s2Right )

    Do while j < jStop
        y1 = j - s1.y
        y2 = j - s2.y
        Dim as Integer i = iStart
        Do while i < iStop
           'Pset( i,j ), DEBUG_COLOUR 'uncomment to view area of iteration
            x1 = i - s1.x
            x2 = i - s2.x
            If      Point(x1, y1, s1.image) <> NO_PIXEL _
            AndAlso Point(x2, y2, s2.image) <> NO_PIXEL Then
                Return HIT
            End if
            i +=1
         Loop
         j += 1
    Loop

    Return NOHIT

End function


BasicCoder2, thank you for posting. I haven't enjoyed playing with code like this for a while.
By the way, the output of your "visualization" program is a work of art.

Append: One last stab

Code: Select all

Function ImagesCollide(sprite1 as SPRITE,_
                       sprite2 as SPRITE ) as Integer
#Define HIT   1
#Define NOHIT 0
#Define NO_PIXEL     (RGB(255, 0, 255)      )
#Define DEBUG_COLOUR (RGB(&HcF, &hcF, &hcF ))

#Define LEFT1 (sprite1.x)
#Define LEFT2 (sprite2.x)
#Define TOP1  (sprite1.y)
#Define TOP2  (sprite2.y)

    Dim as Integer Right1  = sprite1.x + sprite1.w,_
                   Right2  = sprite2.x + sprite2.w,_
                   Bottom1 = sprite1.y + sprite1.h,_
                   Bottom2 = sprite2.y + sprite2.h
   
    Dim as Integer row     = Iif(TOP1    >= TOP2   , TOP1    , TOP2    ),_
                   rowStop = Iif(Bottom1 <  Bottom2, Bottom1 , Bottom2 )

    Dim as Integer columnStart = Iif(LEFT1   >= LEFT2 , LEFT1  , LEFT2  ),_
                   columnStop  = Iif(Right1  <  Right2, Right1 , Right2 )

    Dim as Integer y1 = row - TOP1,_
                   y2 = row - TOP2
    Do while row < rowStop

        Dim as Integer column = columnStart   ,_
                           x1 = column - LEFT1,_
                           x2 = column - LEFT2
        Do while column < columnStop
            'DEBUG***************************
            'Pset(column, row), DEBUG_COLOUR
            '********************************
            If      Point(x1, y1, sprite1.image) <> NO_PIXEL _
            AndAlso Point(x2, y2, sprite2.image) <> NO_PIXEL Then
                Return HIT
            End if
            column  += 1
            x1      += 1
            x2      += 1
         Loop
         row  += 1
         y1   += 1
         y2   += 1
    Loop

    Return NOHIT

End function

Return to “Tips and Tricks”

Who is online

Users browsing this forum: No registered users and 3 guests