Hang on to your hats boys and girls! Knife Thrower is sort of a 2D knife throwing game, only there are no levels, points, or moving up in the game...
Just endless (and pointless!) GDI+ knife throwing bliss...
Please note* In order to fully experience this example, you would need to download the complete example project, located at the link at the bottom of this page. While you're there, be sure to take the time to vote 5 stars!
This example/article will cover some topics in the following areas (but may not cover every constituent of that topic!)
2.) Transformation of polygons.
This section contains shortcuts to key MSDN library entries of some topics mentioned in this article.
GDI+ stands for "Graphics Device Interface". Simply put, GDI+ is an easy way to create graphics in Visual Basic and other languages. GDI+ could be used to modify anything from which you can retrieve a "Graphics" object. This example will be using the Graphics
Object located in the "Protected Overrides Sub OnPaint". The reason we will use that one instead of the Paint event handler is because this will allow our painting to be processed before the "Control.OnPaint" method is invoked. This will ensure our painting
is finished before all the other resultant events in the event chain are raised thus handling our graphics routine first.
Let me back up... Maybe I went too far ahead....
A little more information on GDI+, rather than what we will be doing. GDI+ classes, methods, and objects can be accessed from the following namespaces:
The graphics object is located in the System.Drawing namespace. Once a graphics object is obtained from an (Image, Bitmap, or Control), there are many drawing methods at your disposal, awaiting your command on how to modify the appearance of that (Image,
Bitmap, or Control). There are so many methods in the Graphics Class alone! The graphics class is absolutely essential for doing anything with GDI+. I
suggest familiarizing yourself with those methods, described at the following link:
A polygon is a shape made up of connected lines. In Visual Basic, a polygon is an array of points, those points are later connected using GDI.
I would like to clarify that my example will utilize the following object that I have created. The name of this object is Polygon, but this is only because I use this object to transform the rotation of the actual polygon points, so in a sense, it is a polygon,
but still the polygon is the actual points connected with GDI.
Here is the polygon class I have created for this example (Don't worry, I will explain it later....).
Diff.X = NewOrigin.X
= NewOrigin.Y - Origin.Y
.Points.Count - 1
.Polygon.Count - 1
.sPoints.Count - 1
P.X < Left
Left = P.X
P.X > Right
Right = P.X
P.Y < Top
Top = P.Y
P.Y > Bottom
Bottom = P.Y
= Right - Left
= Bottom - Top
_Angle = 360
_Angle = 0
_Angle = -360
_Angle > 360
_Angle <= 360
_Angle < -360
_Angle >= 0
_Angle < 0
_Angle += 360
(pOrigin.X + (Math.Cos(Rad(Degrees)) * (pPoint.X - pOrigin.X) - Math.Sin(Rad(Degrees)) * (pPoint.Y - pOrigin.Y)))
(pOrigin.Y + (Math.Sin(Rad(Degrees)) * (pPoint.X - pOrigin.X) + Math.Cos(Rad(Degrees)) * (pPoint.Y - pOrigin.Y)))
Degree / 180 *
.Points(Points.Count - 1)
.Polygon(Polygon.Count - 1)
.sPoints(sPoints.Count - 1)
.tsPoints(tsPoints.Count - 1)
' Knife is horizontal
'Knife is vertical
When you want to rotate a polygon, you're really just rotating a set of points around an origin. Therefore you could use a simple one dimensional array of points. You then use the rotation algorithm to rotate each point in that array. The way this works
is to take the following equation (Visual Basic Function):
This is where we will begin. When I create an application, I always try to keep 'Option Strict On'. This is to help prevent type cast errors.
In my Form1 (the main form) I have created some class level variables for settings and whatnot. I will explain what each of those variables is for:
Chronologically speaking, this will basically be our first Event, where all the magic starts.... So let's check out what we need to do in Form1's Load event:
(TargetLimitX, My.Computer.Screen.Bounds.Width - Width)
(0, My.Computer.Screen.Bounds.Height - Height)
Rectangle(Left, Top, Width, Height)
((X) * iScale),
(Y * iScale)))
((X + 20) * iScale),
((Y - 20) * iScale)))
((X + 30) * iScale),
((Y - 8) * iScale)))
((X + 70) * iScale),
((Y - 10) * iScale)))
((X + 80) * iScale),
((Y - 60) * iScale)))
((X + 75) * iScale),
((Y - 65) * iScale)))
((X + 85) * iScale),
((Y - 75) * iScale)))
((X + 95) * iScale),
((X + 90) * iScale),
((Y - 10) * iScale)))
((X + 250) * iScale),
((Y - 18) * iScale)))
((X + 300) * iScale),
(Y * iScale)))
((Y + 18) * iScale)))
((Y + 10) * iScale)))
((Y + 60) * iScale)))
((Y + 65) * iScale)))
((Y + 75) * iScale)))
((Y + 8) * iScale)))
((Y + 20) * iScale)))
((KnifePts(0).X + 200) * iScale), KnifePts(0).Y)
Polygon(KnifePts.ToArray, Origin, SpecialPoints.ToArray)
Somewhere along the line, you may have heard or read that Visual Basic is an Event-Drivin language. What does this mean? This means that unless your application is in the middle of processing an event, then your application is waiting for an event to happen.
This is exactly what it is doing when it is done processing your FormLoad event, it begins waiting for events to fire.
TestOrigin.Y -= oSpeed
Origin.Y -= oSpeed
TestOrigin.Y += oSpeed
Origin.Y += oSpeed
TestOrigin.X -= oSpeed
Origin.X -= oSpeed
TestOrigin.X += oSpeed
Origin.X += oSpeed
= Knife.Angle + sSpeed
Knife.Angle += sSpeed
= Knife.Angle - sSpeed
Knife.Angle -= sSpeed
(SpinSpeed + 0.5)
SpinSpeed > 10
SpinSpeed = 5
(SpinSpeed - 0.5)
SpinSpeed < 0.5
SpinSpeed = 0.5
Velocity = Velocity - 1
Velocity < 1
Velocity = 1
Velocity = Velocity + 1
Velocity > 15
Velocity = 15
"Knife Thrower Help:"
"Press the arrow keys to move the knife"
"to where you want to throw it from."
"Press 'w' or 's' to rotate the knife before throwing."
"Press '+' or '-' to adjust the speed at which the knife spins"
"Press 'q' or 'e' to adjust the velocity of the knife"
"Press 'h' to open Knife Thrower help menu"
"Press 'Space' to throw the knife or to reset the knife after sticking
CurrentTarget = RandomTarget()
Knife.Angle = Knife.Angle
Polygon = Knife.Clone
ClonePoly.Angle = TestAngle
Rectangle = ClonePoly.ContainerRectangle(0)
CandidateRectangle.Right > LimitX
CandidateRectangle.Left < 0
CandidateRectangle.Top < 0
Velocity = Velocity
"Press 'Space' to throw the knife or to reset the knife after sticking it."
Origin.X += Velocity
Knife.Angle += SpinSpeed
TargetHit = RectIntersectsWithPoly(CurrentTarget, Knife.Polygon)
FlewOffScreen = Knife.ContainerRectangle(0).Left >
Knife.KnifeDirection = Polygon.Direction.Right
Origin.X += Velocity * 5
Knife.Angle += SpinSpeed * 5
Origin.X = OriginalOrigin.X
Origin.Y = OriginalOrigin.Y
The Direction Enum, and knife direction property, is somewhat specific to the knife polygon, but could be removed or reused with non-knife polygons, with little issue.
I hope you find this helpful!
Hits On Pages
I didn't read the whole article yet with care, but I do not understand this phrase "pOrigin is the origin that location that which your point is rotating around." Also, in my opinion, we need a space before parenthesis starts. I added them when I saw you started them immediately after some text. That's from the editing point of the view only. The article itself seems like a great one!
Hello Naomi, the phrase your are thinking of is talking about the parameters for the RotatePoint function. This is simply saying pPoint will be rotated around pOrigin. pOrigin is the axis on which the points will be rotated.
Good article! I tried the code sample it was great! Maybe later a points system will entice people to download it more. Either way the article is great!
Thank you thinker :-) Did you read my reply to your question over the gallery entry? I believe a points system could be derived based off of the two points( Knife.Polygon(0) & Knife.Polygon(11)) If these points' Y coordinate is the same then the knife is perfectly even, otherwise the gap inbetween those points could be used to determine a level of accuracy that could be multiplied against the velocity and spinpeed of the knife.
This is hilarious! Great job, Paul!
Thanks ed : -)
Paul, thank you for your contributions on TechNet Wiki! Can you email me? It's edprice at Microsoft.
Could Property Angle be reduced to:
Public Property Angle As Single
Set(value As Single)
_Angle = value Mod 360
If _Angle < 0 Then _Angle += 360
Indeed yes, Richard, I like that.