|
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
|
|