Difference between revisions of "VB:Tutorials:Rectangular and Circular Collision Detection"

From GPWiki
Jump to: navigation, search
m (Marked for broken markup)
(s/code/syntaxhighlight/ for all code fragments)
 
Line 1: Line 1:
{{MarkupFix}}
 
 
 
Games aren't much fun without collisions.. I mean, really, would you play Mortal Kombat if there were no contact, no blood? It'd just be some sort of friendly martial-arts ballet!
 
Games aren't much fun without collisions.. I mean, really, would you play Mortal Kombat if there were no contact, no blood? It'd just be some sort of friendly martial-arts ballet!
  
Line 7: Line 5:
 
Let me introduce you to my friend, the <tt>IntersectRect</tt> API call, and his buddy, the <tt>RECT</tt> type.
 
Let me introduce you to my friend, the <tt>IntersectRect</tt> API call, and his buddy, the <tt>RECT</tt> type.
  
<code type="vb">
+
<syntaxhighlight type="vb">
 
Type RECT
 
Type RECT
 
     Left As Long
 
     Left As Long
Line 17: Line 15:
 
Declare Function IntersectRect Lib "user32" Alias "IntersectRect" _
 
Declare Function IntersectRect Lib "user32" Alias "IntersectRect" _
 
     (lpDestRect As RECT, lpSrc1Rect As RECT, lpSrc2Rect As RECT) As Long
 
     (lpDestRect As RECT, lpSrc1Rect As RECT, lpSrc2Rect As RECT) As Long
</code>
+
</syntaxhighlight>
  
 
The <tt>IntersectRect</tt> function accepts three rectangles as arguments. It checks for intersection between the 2nd and 3rd arguments and places the resultant intersection rectangle in the first argument. Also, the function itself returns a value of <tt>1</tt> if there is an intersection, and a value of <tt>0</tt> if there is no intersection.
 
The <tt>IntersectRect</tt> function accepts three rectangles as arguments. It checks for intersection between the 2nd and 3rd arguments and places the resultant intersection rectangle in the first argument. Also, the function itself returns a value of <tt>1</tt> if there is an intersection, and a value of <tt>0</tt> if there is no intersection.
Line 23: Line 21:
 
Obviously you could do these things manually with a number of <tt>If</tt> statements, but why bother? The <tt>IntersectRect</tt> API is simple to use, and fast. Watch:
 
Obviously you could do these things manually with a number of <tt>If</tt> statements, but why bother? The <tt>IntersectRect</tt> API is simple to use, and fast. Watch:
  
<code type="vb">
+
<syntaxhighlight type="vb">
 
mblnCollision = IntersectRect(udtTempRect, udtRect1, udtRect2)
 
mblnCollision = IntersectRect(udtTempRect, udtRect1, udtRect2)
</code>
+
</syntaxhighlight>
  
 
This statement will check for intersection between <tt>udtRect1</tt> and <tt>udtRect2</tt> and assign the intersecting area to the rectangle <tt>udtTempRect</tt>. Also, if there IS an intersection, it will assign the value of <tt>True</tt> to the boolean <tt>mblnCollision</tt>. We can then take action based on the value of this boolean in other areas of the code.
 
This statement will check for intersection between <tt>udtRect1</tt> and <tt>udtRect2</tt> and assign the intersecting area to the rectangle <tt>udtTempRect</tt>. Also, if there IS an intersection, it will assign the value of <tt>True</tt> to the boolean <tt>mblnCollision</tt>. We can then take action based on the value of this boolean in other areas of the code.
Line 31: Line 29:
 
Circular collision detection doesn't come with its own API call, so I've made my own function to speed things up a tad.
 
Circular collision detection doesn't come with its own API call, so I've made my own function to speed things up a tad.
  
<code type="vb">
+
<syntaxhighlight type="vb">
 
Private Function GetDist(intX1 As Single, intY1 As Single, intX2 As Single, intY2 As Single) As Single
 
Private Function GetDist(intX1 As Single, intY1 As Single, intX2 As Single, intY2 As Single) As Single
 
     ' DaB - Don't use ^2 it's really slow.
 
     ' DaB - Don't use ^2 it's really slow.
Line 37: Line 35:
 
      
 
      
 
End Function
 
End Function
</code>
+
</syntaxhighlight>
  
 
This will return the distance between any two points in an X,Y plane. To find out how far away two circles are, pass the coordinates of their center points. Then subtract the radii of the two circles, and viola! You have the distance between the circles!
 
This will return the distance between any two points in an X,Y plane. To find out how far away two circles are, pass the coordinates of their center points. Then subtract the radii of the two circles, and viola! You have the distance between the circles!
  
<code type="vb">
+
<syntaxhighlight type="vb">
 
mblnCollision = (GetDist(msngCircle1X, msngCircle1Y, msngCircle2X, msngCircle2Y) <= RADIUS1 + RADIUS2)
 
mblnCollision = (GetDist(msngCircle1X, msngCircle1Y, msngCircle2X, msngCircle2Y) <= RADIUS1 + RADIUS2)
  
 
'DaB - Or do it "inline in one line" if you need speed (and avoid the Sqr and ^2 operators)
 
'DaB - Or do it "inline in one line" if you need speed (and avoid the Sqr and ^2 operators)
 
mblnCollision = ((msngCircle1X-msngCircle2X)*(msngCircle1X-msngCircle2X)+(msngCircle1Y-msngCircle2Y)*(msngCircle1Y-msngCircle2Y)) <= (RADIUS1+RADIUS2)*(RADIUS1+RADIUS2)
 
mblnCollision = ((msngCircle1X-msngCircle2X)*(msngCircle1X-msngCircle2X)+(msngCircle1Y-msngCircle2Y)*(msngCircle1Y-msngCircle2Y)) <= (RADIUS1+RADIUS2)*(RADIUS1+RADIUS2)
</code>
+
</syntaxhighlight>
  
 
Analogous to the rectangle code shown above, this code will assign <tt>True</tt> or <tt>False</tt> to the boolean depending on the outcome of the collision detection attempt. If the distance between the circles' centers is less than the sum of the radii, then they've collided. It's that easy.
 
Analogous to the rectangle code shown above, this code will assign <tt>True</tt> or <tt>False</tt> to the boolean depending on the outcome of the collision detection attempt. If the distance between the circles' centers is less than the sum of the radii, then they've collided. It's that easy.

Latest revision as of 18:09, 3 July 2014

Games aren't much fun without collisions.. I mean, really, would you play Mortal Kombat if there were no contact, no blood? It'd just be some sort of friendly martial-arts ballet!

Basic collision detection techniques should be sufficient for most games. Side scrollers, puzzle games, RPGs, and many others can get by with rectangular detection. Shooters and any sort of space game would likely be better off with circular detection. You'll see that these techniques are quite rudimentary.

Let me introduce you to my friend, the IntersectRect API call, and his buddy, the RECT type.

Type RECT
    Left As Long
    Top As Long
    Right As Long
    Bottom As Long
End Type
 
Declare Function IntersectRect Lib "user32" Alias "IntersectRect" _
    (lpDestRect As RECT, lpSrc1Rect As RECT, lpSrc2Rect As RECT) As Long

The IntersectRect function accepts three rectangles as arguments. It checks for intersection between the 2nd and 3rd arguments and places the resultant intersection rectangle in the first argument. Also, the function itself returns a value of 1 if there is an intersection, and a value of 0 if there is no intersection.

Obviously you could do these things manually with a number of If statements, but why bother? The IntersectRect API is simple to use, and fast. Watch:

mblnCollision = IntersectRect(udtTempRect, udtRect1, udtRect2)

This statement will check for intersection between udtRect1 and udtRect2 and assign the intersecting area to the rectangle udtTempRect. Also, if there IS an intersection, it will assign the value of True to the boolean mblnCollision. We can then take action based on the value of this boolean in other areas of the code.

Circular collision detection doesn't come with its own API call, so I've made my own function to speed things up a tad.

Private Function GetDist(intX1 As Single, intY1 As Single, intX2 As Single, intY2 As Single) As Single
    ' DaB - Don't use ^2 it's really slow.
    GetDist = Sqr((intX1 - intX2) * (intX1 - intX2) + (intY1 - intY2) * (intY1 - intY2))
 
End Function

This will return the distance between any two points in an X,Y plane. To find out how far away two circles are, pass the coordinates of their center points. Then subtract the radii of the two circles, and viola! You have the distance between the circles!

mblnCollision = (GetDist(msngCircle1X, msngCircle1Y, msngCircle2X, msngCircle2Y) <= RADIUS1 + RADIUS2)
 
'DaB - Or do it "inline in one line" if you need speed (and avoid the Sqr and ^2 operators)
mblnCollision = ((msngCircle1X-msngCircle2X)*(msngCircle1X-msngCircle2X)+(msngCircle1Y-msngCircle2Y)*(msngCircle1Y-msngCircle2Y)) <= (RADIUS1+RADIUS2)*(RADIUS1+RADIUS2)

Analogous to the rectangle code shown above, this code will assign True or False to the boolean depending on the outcome of the collision detection attempt. If the distance between the circles' centers is less than the sum of the radii, then they've collided. It's that easy.

Have a look at this sample source code which demonstrates these principles.