TGC Codebase Backup



Nearly pixel perfect sprite collision detection by haggisman

11th Oct 2003 7:23
Summary

It works for any sprite, no matter what the rotation or scaling of the sprites it will still retain the near pixel perfect collision.



Description

Everything needed can be found in the zip, both some simple code showing the collision detection routine and also the code for creating the collision template for the sprites.

The collision template creator simply creates an outline of a sprite on the edge between the opaque and transparent areas, the outline is used to generate lines that the collision will be detected against. Normals will also be generated at this point, which will eventually allow me to make collision responses IE sliding/bouncing/sticking collision.

The Simple example just shows the collision routine very basically between two objects. The one being controlled can be moved rotated and scaled and still produce accurate collision. It works on the very basic principle that if lines of either shape intersect then there is a collision. There is still a lot of optimization to do but I believe its still fast enough in its current state for some nice 2d games.



Code
                                    ` This code was downloaded from The Game Creators
                                    ` It is reproduced here with full permission
                                    ` http://www.thegamecreators.com
                                    
                                    Rem This is just sample source, it will most likely NOT work. For the actual source
Rem download the attached zip file!

Rem ***** Main Source File *****

rem Standard Setup Code
sync on : sync rate 0 : color backdrop rgb(0,0,0) : hide mouse
set text font "arial" : set text size 24 : set text transparent : set text to bold

rem Variables
global Uvector=1
global Vvector=2
global Wvector=3
global Normal_length#

Sprite_number=2
control=2
Highest_possible=1000
Normal_length#=1.0

rem Types
Type Sprite_data line_number as integer X as integer Y as integer Orig_Radius as float Radius as float Mass as integer endtype
Type normal_data X as float Y as float endtype
Type line_data X1 as float Y1 as float X2 as float Y2 as float endtype

rem Arrays
global Dim User_Sprite(Sprite_number) as Sprite_data
global Dim lines(Sprite_number, Highest_possible) as line_data
global Dim T_lines(Sprite_number, Highest_possible) as line_data
global Dim normals(Sprite_number, Highest_possible) as normal_data

rem Vectors
rf=make vector2(Uvector)
rf=make vector2(Vvector)
rf=make vector2(Wvector)

rem Firstly load the sprites in
Load_Sprite("ShapeA", ".bmp", 1)
Load_Sprite("ShapeB", ".bmp", 2)

rem Set some nice sprite positions
User_sprite(1).X=512 : User_sprite(1).Y=384
User_sprite(2).X=600 : User_sprite(2).Y=350

Transform_collision_coords(1)
Transform_collision_coords(2)

rem Main loop
Do
   `control the strange shape
   User_sprite(control).X=mousex()
   User_sprite(control).Y=mousey()
   if mouseclick()=1 then rotate sprite control, (sprite angle(control) + 2)
   if mouseclick()=2 then rotate sprite control, (sprite angle(control) - 2)
   if upkey()=1 then stretch sprite control, sprite scale x(control), sprite scale y(control)+5
   if downkey()=1 and sprite scale y(control)>10 then stretch sprite control, sprite scale x(control), sprite scale y(control)-5
   if leftkey()=1 and sprite scale x(control)>10 then stretch sprite control, sprite scale x(control)-5, sprite scale y(control)
   if rightkey()=1 then stretch sprite control, sprite scale x(control)+5, sprite scale y(control)
   Transform_collision_coords(control)

   `check for collision
   for x=1 to 2
      collision_return=0
      `need distance check with bounding spheres.
      if x<>control
         collision_return=Sprite_Collision_Check(x, control)
      endif
      if collision_return>0 then collision_return=x : exit
   next x

   `Paste the sprites to screen
   for x=1 to Sprite_number
      if x<>control
         paste sprite x, User_sprite(x).X, User_sprite(x).Y
      endif
   next x

   paste sprite control, User_sprite(control).X, User_sprite(control).Y

   `draw all the lines for puropses of debugging
   `for x=1 to sprite_number
      `draw_lines_on(x)
   `next x

   center Text 512, 0, "Mouse moves the red object; Mouse buttons rotate it; Arrow keys change scale"
   center Text 512, 24, "COLLISION RESULT :- " + str$(collision_return)
   Text 0, 0, "FPS: " + str$(screen fps())

   Sync
Loop

`*******************************************************************************************
`*******************************************************************************************

rem Finally the boring bit of checking lots of bloody line intersections
function Sprite_Collision_Check(SA, SB)

`setup variables
SMALL_NUM#=0.00000001
D as float
DU as float
DV as float
SI as float
TI as float

`default negative result
collision_return=0

`firstly check if within collision radius
distance_between_pivots#=SQRT((User_sprite(SA).X-User_sprite(SB).X)^2 + (User_sprite(SA).Y-User_sprite(SB).Y)^2)

if distance_between_pivots#<User_sprite(SA).radius+User_sprite(SB).radius
   `hmm this bit could be slow
   For a=1 to User_sprite(SA).line_number
      For b=1 to User_sprite(SB).line_number

         set vector2 Uvector, T_lines(SA,a).X2-T_lines(SA,a).X1, T_lines(SA,a).Y2-T_lines(SA,a).Y1
         set vector2 Vvector, T_lines(SB,b).X2-T_lines(SB,b).X1, T_lines(SB,b).Y2-T_lines(SB,b).Y1
         set vector2 Wvector, T_lines(SA,a).X1-T_lines(SB,b).X1, T_lines(SA,a).Y1-T_lines(SB,b).Y1
         D=perp(Uvector, Vvector)

         `test if parallel lines
         if abs(D)>SMALL_NUM#
            SI=perp(Vvector, Wvector)/D
            If SI>0 and SI<1
            TI=perp(Uvector, Wvector)/D
               If TI>0 and TI<1
                  collision_return=1
               endif
            endif
         endif

         if collision_return=1 then exit
      Next b
   if collision_return=1 then exit
   Next a
endif

endfunction collision_return

rem Calculate the perp product
function perp(VectorA, VectorB)
   D as float
   D=((x vector2(VectorA) * y vector2(VectorB)) - (y vector2(VectorA) * x vector2(VectorB)))
endfunction D

`*******************************************************************************************

function Transform_collision_coords(number)

   cosine_multiple#=cos(-1*sprite angle(number))
   sine_multiple#=sin(-1*sprite angle(number))
   scale_X#=(sprite scale x(number)/100.0)
   scale_Y#=(sprite scale y(number)/100.0)

   for l=1 to User_sprite(number).line_number
      T_lines(number, l).X1=((lines(number, l).X1*scale_X#)*cosine_multiple#) + ((lines(number, l).Y1*scale_Y#)*sine_multiple#)+User_Sprite(number).X
      T_lines(number, l).Y1=(-(lines(number, l).X1*scale_X#)*sine_multiple#) + ((lines(number, l).Y1*scale_Y#)*cosine_multiple#)+User_Sprite(number).Y
      T_lines(number, l).X2=((lines(number, l).X2*scale_X#)*cosine_multiple#) + ((lines(number, l).Y2*scale_Y#)*sine_multiple#)+User_Sprite(number).X
      T_lines(number, l).Y2=(-(lines(number, l).X2*scale_X#)*sine_multiple#) + ((lines(number, l).Y2*scale_Y#)*cosine_multiple#)+User_Sprite(number).Y
   next l

   `set the new range check as well
   if scale_X#>scale_Y#
      max_scale#=scale_X#
   else
      max_scale#=scale_Y#
   endif
   User_Sprite(number).Radius=User_Sprite(number).Orig_Radius*max_scale#

endfunction

`*******************************************************************************************

rem Load Sprite Function
function Load_Sprite(Name$, FileType$, number)
   `Simple loading and creating sprite
   load image "Input/"+Name$+FileType$, number
   Sprite number, 0, 0, number

   `Open the sprites col file to extract important data
   open to read 1, "Output/"+Name$+".col"

   read file 1, line_number
   read file 1, offset_X
   read file 1, offset_Y
   read float 1, User_Sprite(number).Orig_Radius
   read file 1, User_Sprite(number).Mass

   `Read out all the polar coords into the global polar array
   For l=1 to line_number
      read float 1, lines(number,l).X1
      read float 1, lines(number,l).X2
      read float 1, lines(number,l).Y1
      read float 1, lines(number,l).Y2
   next l

   `Read out all the normals
   For l=1 to line_number
      write float 1, normals(number,l).X
      write float 1, normals(number,l).Y
   next l

   `close file
   close file 1

   `misc clean up
   User_sprite(number).line_number=line_number
   offset sprite number, offset_X, offset_Y
   hide sprite number
endfunction

`*******************************************************************************************

function draw_lines_on(number)

for l=1 to User_sprite(number).line_number
   line T_lines(number,l).X1, T_lines(number,l).Y1, T_lines(number,l).X2, T_lines(number,l).Y2
next l

endfunction

`*******************************************************************************************