Free Web Hosting Provider - Web Hosting - E-commerce - High Speed Internet - Free Web Page
Search the Web

Free Web site hosting - Freeservers.com | Web Hosting - GlobalServers.com
Choose an ISP: NetZero High Speed Internet Dial up $14.95 or NetZero Internet Service $9.95
   

vbProgramming
Tutorials - Collision Detection

 
       
vbProgramming Home :: vbProgramming Forums :: Tutorials :: Contact :: Links 

This tutorial will teach you Collision Detection. You will also gain some
knowledge of Functions and Properties. The main focus of the collision
detection in this tutorial is Proximity and Bounding Boxes Collison.
other types of collision detection will be discussed in later tutorials.

 

 

 

Functions
After learning what subs are, you should be able to pick up on what Functions are. Functions are subs with a Return value. How do functions relate to Collision Detection? You'll see in this tutorial! A function has the basic structure of a sub, except it must have a return value (with some slight exceptions)

For example:

Function checkifxis1() As Boolean
   If x = 1 Then
      Return True
   Else
     Return False
   End If
End Function

I italicized what makes a function different from a Sub. The "As Boolean" refers to what you can Return. The Return True/False tells you what you have returned. Here's how you would use Functions(as an example):

MessageBox.Show(checkifxis1)

If x was 1, then it would MessageBox "True", if x was not one then it would MessageBox "False" Think about it this way, pretend x was 1. The compiler would be reading this line:
MessageBox.Show(checkifxis1)
Then it would go through the function, then into the If statement and then it would eventually get to "Return True", so now, to the compiler, the MessageBox statement "turns into" this:
MessageBox.Show(
True)

Now that wasnt that hard was it?
Lets look at Functions a little more closely. Right before "End Function" type in
MessageBox.Show(
"Done checking")

You'd expect it to MessageBox "Done checking" and then MessageBox True or False. However, becuase it says Return True (or False), it exits out of the function when it gets to that line.

Function checkifxis1() As Boolean
   If x = 1 Then
      Return True '<-- when it reaches this line...
   Else
     Return False '<-- ...or this line , it exits the function, Returning True or False...
   End If
   MessageBox.Show("done checking") '<-- ...so it never gets to this line
End Function


One way to avoid that is to MessageBox it before returning True or False; but that's not exactly the best workaround. Here's a way to avoid that:

Function checkifxis1() As Boolean
   If x = 1 Then
      checkifxis1 = True '<-- It'll set the value of the function to True...
   Else
     checkifxis1 = False '<-- or False, without exiting the function.
   End If
   MessageBox.Show("done checking") '<-- this means that it actually gets to this line :D
End Function

(.. argh!! DreamWeaver's automatically messing with my font again!!)

Simple, wasnt it? Now, let's get to the fun stuff - Collision Detection!

Collision Detection
Collision Detection requires a little bit of imagination. I'd recommend keeping a pen and a paper handy (or use Microsoft Paint :-p). There's a little bit of math required for collision detection. One thing im going to focus on is the Pythagorean Theorum.

-Bounding Boxes-
Here's 1 type of collision, it's known as "Bounding Boxes." You basically check weather one object is inside another.
Note, don't type this in code yet, just try to draw this in Paint or on a peice of paper. When you want to check whether  object A is inside object B, object A obviously has to be smaller or the same size as object B (becuase a bigger object cant possibly fit in a smaller object :-p). So, draw 2 rectangles on your paper. Call one of them R1 and the other R2. Make R1 smaller than R2 becuase we're going to check whether R1 is inside R2.

The first thing we're going to look at is how to check whether R1 is inside R2. 4 conditions must be met:
1) R1's left side is to the right of R2's left side
2) R1's right side is to the left of R2's right side
3) R1's top side is below R2's top side
4) R1's bottom side is above R2's bottom side

Easy enough right? Now we have to translate this into code. We can "link" these together by using a couple of 'And's.
(Note, you can use the underscore, "_", to make your code continue to the next line)


If R1.Left > R2.Left _              'Condition 1
And R1.Right < R2.Right _      'Condition 2
And R1.Top > R2.Top _            'Condition 3
And R1.Bottom < R2.Bottom _ 'Condition 4
Then

    MessageBox.Show("Collision!")

End If

Clarification Note:The more you go right, the greater X is. The more you go down, the greater y is. Thus, line 3 has a greater than sign becuase you're checking whether R1's top is below R2's top.

Let's say we make a program, checking for collision within pictureboxes. Great, we found out how to check whether an object is inside another, but Line 2 and 4 will give errors, becuase PictureBoxes dont have .Right and .Bottom properties. :-p There's a way around this. An object's Right property is its Left + Width. If you draw a square at position 60,30 - and its width is 40, that means its right is at 100. Same goes for Bottom. Bottom = Top + Height.  Thus, you can write your code this way:

If R1.Left > R2.Left _                                                                       'Condition 1
And (R1.Left + R1.Width) < (R2.Left + R2.Width)_                      'Condition 2
And R1.Top > R2.Top _                                                                   'Condition 3
And (R1.Top + R1.Height) < (R2.Top + R2.Height)_                    'Condition 4
Then


    MessageBox.Show("Collision!")

End If


Same thing, except now it works with PictureBoxes. Try it out, put this code above in Form1_KeyDown and assign some keys to make the R1 PictureBox move, and when it goes inside R2, you'll get a MessageBox saying "Collision!"

We just did this with PictureBoxes, now let's do this with GDI+. We're going to create a Sprite class which holds the positions of the boxes. Note a few things: the Left property is the same as the X position, and the Top property is the same as the Y position.

Create a new class called clsSprite. Add the following lines to it:
Public Pos as Point
Public Width as Integer
Public Height as integer

This will store the X(Left) and the Y(Top) property of the Boxes. Now we have to find a way to Return(hint hint hint) the Right property(X + Width). One way we could do that is:

Public Function Right() As Integer
   Return Pos.X + Width
End Function

So that when you try to access R1.Right, it will "be replaced by" R1.Pos.X + R1.Width. However.... Functions arent really the proper thing for this. Functions are usually used to execute some code before returning a value. A more proper thing here would be a ReadOnly Property. (We only want to read the .Right property, we don't want to change it - we'll change the Pos for that matter)

Simple as this:
Public ReadOnly Property Right As Integer  'Push Enter, after typing this line.

After you push enter it will generate

Public ReadOnly Property Right()
   Get
     Return Pos.X + Width
   End Get
End Property

(Delete the Function you made earlier)

The "Get" is what happens when you try to access the property, meaning when you say
R1.Right, it will return Pos.X + Width. Soon we'll talk about properties that you can Read and Write(change). It's pretty simple actually, but that's not for now.

Do the same for Bottom.

Public ReadOnly Property Bottom() As Integer
   Get
     Return Pos.Y + Height
   End Get
End Property

Now create an initialization sub.

Public Sub New(ByVal yourPos as Point, byVal yourHeight as Integer, byVal yourWidth as integer)
   Pos = yourPos
   Height = yourHeight
   Width = yourWidth
End Sub

This just sets the value of each variable. Now go back to form1.  Dim a couple clsSprite's:

Dim R1 As clsSprite
Dim R2 As clsSprite

And in Form1_load, Initialize them
R1 = New clsSprite(New Point(30, 30), 60, 60)
R2 = New clsSprite(New Point(80, 80), 80, 80)

Note, you have to do New Point, just like you have to do New Bitmap - pay attention for these things, becuase you basically have to New a lot of things in VB.NET.

This code places R1 at 30,30, with its height and width at 60. R2 is placed below R1 and to the right of R1, with a width and a height of 80.

Now in the Paint sub:
e.Graphics.DrawRectangle(New Pen(Color.Blue), R1.Pos.X, R1.Pos.Y, R1.Width, R1.Height)
e.Graphics.DrawRectangle(New Pen(Color.Red), R2.Pos.X, R2.Pos.Y, R2.Width, R2.Height)

Note the New pen....:-p. This just makes R1 Blue and R2 Red and draws them in their specific position. Run it, see if you like where their positions are.

Now let's make them move. Instead of going around typing, If e.keycode = keys.right then.. etc If e.keycode = keys.left then.. etc - there's a short hand method called Select Case, you should be able to catch on to this easily

In form1_keydown:
Select Case e.KeyCode
    Case Keys.Left
        R1.Pos.X -= 5
    Case Keys.Right
       R1.Pos.X += 5
    Case Keys.Up
       R1.Pos.Y -= 5
    Case Keys.Down
       R1.Pos.Y += 5
End Select

Me.Invalidate() '<-- don't forget this line!

Run it - the blue rectangle shold move. Now we have to check for collision. Easy, right before "select case" just type this in:

If R1.Pos.X > R2.Pos.X _
And R1.Right < R2.Right _
And R1.Pos.Y > R2.Pos.Y _
And R1.Bottom < R2.Bottom _
Then

MessageBox.Show("R1 is inside R2!")

End If

Now you should have Collision. In a real game, people would generally use this type of collision to check whether a character is in a room or something like that. Let's say we have many rectangles, and we didnt want to type in that If statement over and over again. Here's a solution: Create a new class called clsPhysics, and type this in there:

Public Function IsInside(ByVal Character As clsSprite, ByVal Room As clsSprite) As Boolean

If Character.Pos.X > Room.Pos.X _
And Character.Right < Room.Right _
And Character.Pos.Y > Room.Pos.Y _
And Character.Bottom < Room.Bottom Then

   Return True
Else
    Return False
End If

End Function

It's the same code, except i replaced R1 with Character and R2 with Room. Now go back to form1 and type in:
Dim PhysicsChecker as new clsPhysics
'note this line is the same as these 2: Dim physicschecker as clsphysics : physicschecker= new clsphysics.

Delete the If statement in Form1_keydown and type this in:
If physicschecker.IsInside(R1, R2) Then
   MessageBox.Show("R1 is in R2 ")
End If

Proximity

This section should be shorter becuase we already made the classes for everything. Leave all the code in form1 alone except for the "If physicschecker.isInside(r1,r2)" block <-- just comment that entire if statement.

The proximity algorighm requies a little bit of knowledge of the Pythagorean theorum - long word, simple concept. In this example, we're going to use the R1 and R2's Center positions. Take out your paper, or open Paint. Draw 2 squares that arent colliding (make R1 below and to the left of R2). Draw a dot in the center of them and connect them.That forms a diagonal line. Now, take R1's center and draw ahorizontal line until you get to the X position of the Center of R2, and then draw a line straight down from R2's center. It should form a righttriangle and look like this:


                                                           

The red line represents the distance between the 2 objects. Since we're talking about right triangles here, it is also known as the Hypoteneuse. Call the horizontal line, a, and the vertical line, b. Pythagorean's theorum says:

Hypoteneuse^2 = a^2 + b^2 ' note: ^2 means squared. This means that:
Hypoteneuse = square root(a^2 + b^2) 'Remember, the hypoteneuse is our distance

(If you dont know algebra yet , just accept the formula for now)

How do we find a and b? With a good imagination, you should be able to see that a(the horizontal) is the X position of the center  subtracted from each other. For example, look at this diagram:


In the case above, R1's center is 60,30 and R2's center is 80,10. The size of A (the blue line) is 20 (80-60) and the size of B(red line) is 20(30-10). Great, all that's said and done, how would u know what to subtract? You could have mistakenly subtracted 60-80 and gotten -20; or subtract 10-30 and get -20. It doesnt matter actually, becuase if you take the absolute value of them, you'll get the same answer.

Basically, since you're taking the absolute value (the positive value) of each of the differences, it doesnt mater where each object is, you can check for collision without worrying about negative distances.

So here's the formula:
a = |r1.centerX -r2.centerX| 'the |'s indicates absolute value
b = |r1.centerY - r2.centerY|

Now we have to find the distance(hypoteneuse) which is
Hypoteneuse = square root(a^2 + b^2)

That's not that hard to do. So now we know the distance - how do we check for collision from there?
When you know the distance between 2 objects, you check for collision by checking whether the distance is greater than the 2 object's widths (or their heights) combined. Check it out - on your paper, pick a distance, pick 2 widths and add them.  You'll see clearly that if the object's are colliding, the distance needs to be less than the widths combined.

Now how does this all translate into code? Let's refine our class. Go back to the sprite class. In order to create a CenterX property, you need to add the X position + half the width (if the right is the X + width, and left is X, then the center must be in between: X + 1/2 * width. Same goes with centerY. Add the following Properties:

Public ReadOnly Property CenterX() As Integer
   Get
      Return Pos.X + (1 / 2 * Width)
   End Get
End Property

Public ReadOnly Property CenterY() As Integer
   Get
      Return Pos.Y + (1 / 2 * Height)
   End Get
End Property

Now go to form1_keydown and type this in before Select Case:
a = Math.Abs(R1.CenterX - R2.CenterX) '(Math.Abs means absolute value)
b = Math.Abs(R1.CenterY - R2.CenterY) '(Math.Abs means absolute value)



hypoteneuse = Math.Sqrt(a ^ 2 + b ^ 2) '(Math.Sqrt means Square root)

If hypoteneuse <= (R1.Width + R2.Width) / 2 Or hypoteneuse <= (R1.Height + R2.Height) / 2 Then
    MessageBox.Show("collision")
End If

(NOTE: note the /2. The reason taht's there is because(see diagram 1) the actual distance is really between r1's right and r2's left. However since we opted to use the center so that we wont have to check which object is to the left of what.. and get into a huge mess, we need to /2 beucase we're actually getting twice the distance by using the centers.)

Try it out, and it'll work!

Now let's shove this into our physics class. Add the following variables:
Private a, b, hypoteneuse as integer

Note, it is private becuase we dont want the user of our class(us) to change these variables. Add the following functoin:

Public Function Collided(ByVal character As clsSprite, ByVal otherobject As clsSprite) As Boolean

 

a = Math.Abs(character.CenterX - otherobject.CenterX)
b = Math.Abs(character.CenterY - otherobject.CenterY)

hypoteneuse = Math.Sqrt(a ^ 2 + b ^ 2)

If hypoteneuse <= (character.Width + otherobject.Width) / 2 Or hypoteneuse <= (character.Height + otherobject.Height) / 2 Then
     Return True
Else
     Return False
End If

End Function

I just replaced R1 with "character" and R2 with "otherobject." Now go back to form1_keydown, and type this in:

If physicschecker.Collided(R1, R2) Then
MessageBox.Show("Collision between R1 and R2! ")
End If

That's all for today, if you dont understand anything above, I suggest that you read it through once more.

Note: One main source I used to help me was the book ".NET Game Programming with DirectX 9.0"



The Source Code for this tutorial is located here: (OK there's no source for this tut :p)You can also locate this by logging in to vbProgramming Forums and going to:
Tutorials > Tutorial Source Code > Source Code