- Making the paddle move according to the mouse position.
- Creating a computer player.
- Moving the ball.
- Making the ball bounce.
- Game Flow.
- Hiding the cursor.
- Ending the game on an Escape KeyPress.
- Keeping the controls in the right place when the form is resized.
I chose to create a tutorial on creating a Pong game as I have only seen snippets on the internet, and I thought it would be relatively hard for someone new to game design to understand how they worked. This tutorial won't require any 3D libraries such as DirectX or OpenGL, as we will be using PictureBox controls. Without any further ado, lets start creating our pong game!
1) Setting up our game room!
A vital part of any game is a place to play! Basketball requires a basketball court, boxing requires a ring (well it doesn't really but you get the picture), and this is no different. We will start by setting the Form's Size to 640x480. This will give ample playing space for a game of pong. Set the BackColor property to something nice, I chose LightSkyBlue, but again thats a personal preference thing. Set the Text property to Pong, obviously because this is a Pong game, and set the Form's name to pongMain, simply because this is the main room of the pong game.
Now, whats a room without furniture? We need to add some controls now, so add three PictureBox controls to the form. Set the Name properties to paddleComputer, paddlePlayer and gameBall. For both paddlePlayer and paddleComputer, set the Size property to 16x128. For gameBall, set the Size property to 20x20. Select all three controls, and set the BackColor to a color of your preference; I chose LimeGreen.
Now add two Label's to the Form. These will display the score of both the Computer and the Player. Name them compScoreDraw and plrScoreDraw. Set the Font property to a style you'd prefer. I chose Size 18 with Bold and kept the Microsoft Sans Serif font, but you can set it to whatever you'd like.
Now, move the controls so the form looks something like this:
Now add a timer control to the form. This will be our main game loop. Name it gameTimer, and set the Interval property to 20ms, and the Enabled property to True. Now that we have our controls and form set up, we can move on to developing the actual game.
2) Making the paddle move according to the mouse position.
Making the paddle move according to the mouse position isn't as hard as you'd think, however it requires a little bit of math (not a lot) to ignore the mouse if it is below the minimum or maximum height we want. We will want to use pongMain's MouseMove event so we can easily get the Cursor position and we won't need to be checking for movement all the time, and make good use of the .NET Framework.
Assuming you named the controls correctly, you can copy and paste the following into your code.
#Region "Move the paddle according to the mouse" ' Move the paddle according to the mouse position. Private Sub pongMain_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseMove If e.Y > 5 And e.Y < Me.Height - 40 - paddlePlayer.Height Then _ paddlePlayer.Location = New Point(paddlePlayer.Location.X, e.Y) End Sub #End Region
I like to use Region's as it is easier to browse through segments of code, and makes reading the source easier for people new to it. Breaking down the code, if e.Y (the Y position of the cursor) is more than 5 (it looks better having it more than 5) and e.Y is less than 40 (to be honest I am unsure why I needed to take 40 off, but it seemed too look right when I did so) minus the height of the paddle, then set the paddle's position relative to it's X position, but relative to the mouse's position on the Y Axis. This is how we make it follow the mouse.
3) Creating a Computer Player.
The computer player is our opposition, this is the person we want to beat. Unfortunately, the computer player we will be making is a god at pong. It would seem that it doesn't get tired or bored, doesn't rage quit quite as often as a human, so beating this player is relatively impossible. But don't fret, you can always move the code provided here into a new timer so you can adjust the difficulty by the speed of the timer. But that is something to worry about later on, now we just want to be able to play the game.
To move the computer player, we are going to want it to move according to the ball's position. Add the following code to make the paddle follow the ball. We will be using the gameTimer control we made previously when we were designing the form.
#Region "Main Timer" Private Sub gameTimer_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles gameTimer.Tick 'Set the computer player to move according to the ball's position." If gameBall.Location.Y > 5 And gameBall.Location.Y < Me.Height - 40 _ - paddlePlayer.Height Then _ paddleComputer.Location = New Point(paddleComputer.Location.X, gameBall.Location.Y) End Sub #End Region
The code is almost exactly the same as the player following the mouse, rather we will be following the ball with the computer player. Well, if the ball were moving it would be following it.
Now we need to create the code to move the ball.
4) Moving the ball.
Moving the ball requires some basic mathematic skills. We will be slightly touching Cosine's and Sine for the X and Y velocity of the ball, and the speed of the ball. However, you don't require any knowledge of Cosine and Sine with this tutorial, unless you'd like to understand what you were doing.
We are going to need some global variables so we can accomplish this, so we will add these just after the Public Class pongMain declaration.
#Region "Globals" Dim speed As Single = 10 ' Ball Speed Dim rndInst As New Random() ' Random instance Dim xVel As Single = Math.Cos(rndInst.Next(5, 10)) * speed Dim yVel As Single = Math.Sin(rndInst.Next(5, 10)) * speed #End Region
speed is obviously the rate of movement the ball will be performing. The higher the faster, the lower the slower. rndInst is our object for the randomisation of the angle of movement we will be undertaking on creation. xVel and yVel are given a random angle, with a minimum value of 5 and a maximum of 9. We will multiply this value by the speed.
Now we need to add the following line of code to the gameTimer timer to move the ball.
' Move the game ball. gameBall.Location = New Point(gameBall.Location.X + xVel, gameBall.Location.Y + yVel)
Now we should have a ball that moves, a computer player that follows the ball, and a paddle that follows the mouse. Now we need a way of keeping the ball in the playing area, and bouncing it off so we can continue playing without being so rudely interrupted.
5) Making the ball bounce.
Making the ball bounce is easier than you may initially think. We need to put code in the gameTimer timer again, because we need it constantly checking if the ball needs to bounce or not.
To make the ball bounce, it is just a matter of setting the ball's position to contact the surface of the wall, and set the velocity opposite to it's current velocity. How would we set it opposite you ask? By making it negative. So hypothetically, we will be testing to see if the ball hits the wall, and if it does make it bounce off the wall. Here is how we would do this using the top wall. Note that you will need to add this to the Main Timer region, inside the timer subroutine.
' Check for top wall. If gameBall.Location.Y < 0 Then gameBall.Location = New Point(gameBall.Location.X, 0) yVel = -yVel End If
Note that we only want to invert the Y Axis as we aren't dealing with any advanced spinning physics. The ball should now bounce off the wall and continue in it's path; which is obviously what we want.
Now we will need to make the code for the bottom wall. When checking for the collision, we need to to check if the ball is higher than the form's height minus the height of the ball, and we will take off 45 to line it up.
' Check for bottom wall. If gameBall.Location.Y > Me.Height - gameBall.Size.Height - 45 Then gameBall.Location = New Point(gameBall.Location.X, Me.Height - gameBall.Size.Height - 45) yVel = -yVel End If
Now we have the ability to make the ball bounce up and down. Now that we can do this... I think it would be a good time to demonstrate how we can make the ball bounce off the paddles. It is actually relatively simpler than the form's walls, so don't worry, the head scratching is done for this section.. kinda. We will start with the player paddle. We will need to add the following to the main timer, as the main timer is for collisions and other miscellaneous tasks.
' Check for player paddle. If gameBall.Bounds.IntersectsWith(paddlePlayer.Bounds) Then gameBall.Location = New Point(paddlePlayer.Location.X - gameBall.Size.Width, _ gameBall.Location.Y) xVel = -xVel End If
This will check if the ball has intersected with the paddle object and bounce it off if it did. It is the same with the computer paddle too; the process involved is the same pretty much.
' Check for computer paddle. If gameBall.Bounds.IntersectsWith(paddleComputer.Bounds) Then gameBall.Location = New Point(paddleComputer.Location.X + paddleComputer.Size.Width + 1, _ gameBall.Location.Y) xVel = -xVel End If
Now that we have the ball bouncing, we need to add Game Flow.
6) Game Flow.
Game Flow in pong is basically, you lose if the ball goes past the paddle. When it goes past the paddle we want the ball to go back to the middle of the form, and give the winner a point, which will be displayed in the label.
In order to give the players a score, we will need two global variables indicating them. In the Globals region, add the following code so we can start using these score variables.
' The player's scores. Dim compScore As Integer = 0 Dim plrScore As Integer = 0
Now we want to do something with these variables, otherwise we would just be wasting precious memory. Add the following to the main timer, as we are checking for a collision with the left wall.
' Check for left wall. If gameBall.Location.X < 0 Then plrScore += 1 gameBall.Location = New Point(Me.Size.Width / 2, Me.Size.Height / 2) plrScoreDraw.Text = Convert.ToString(plrScore) End If
We will add one to the player's score, as he/she has scored, set the ball's position to the middle of the form and we will set the plrScoreDraw.Text property to display the player's score.
We will do the same thing if the computer wins, except we will be checking to see if the right wall has been hit.
' Check for right wall. If gameBall.Location.X > Me.Width - gameBall.Size.Width - paddlePlayer.Width Then compScore += 1 gameBall.Location = New Point(Me.Size.Width / 2, Me.Size.Height / 2) compScoreDraw.Text = Convert.ToString(compScore) End If
Now that we have this functionality, the game is in a playable state. Now to a couple of 'nice to have' features.
7) Hiding the cursor.
Hiding the cursor is probably the easiest subject we will be covering in this tutorial. Basically, we want to hide the cursor while it's in the form, but not when it's outside it. We will want to add this functionally as soon as the form is loaded, so add the following code to the class.
#Region "Hide Cursor" ' Set up the game. Private Sub pongMain_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Windows.Forms.Cursor.Hide() End Sub #End Region
With this done, lets move on to ending the game on an Escape KeyPress.
8) Ending the game on an Escape KeyPress.
Ending the game on an Escape KeyPress is a pretty simple task, where we will be checking if the Escape key is down and we will close the form if this is the case. We will be using the KeyDown event, so please copy and paste the following code into your class.
#Region "End Game on Escape Press" ' Escape the game when escape has been pressed. Private Sub pongMain_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown If e.KeyValue = Keys.Escape Then Me.Close() End If End Sub #End Region
This will add the functionality of escaping the program when Escape is pressed.
9) Keeping the controls in the right place when the form is resized.
When the form is resized, if the form is made smaller than our 640x480, the player's paddle will be lost, thus making it insanely easy for the computer to score. A free shot if you will. Now, being the human we rather this not happen to us (I'm sure there is a competitive side to everyone), so we would rather the paddle adjusted itself when the form is resized. Also, when the form is resized the the player's score may be hidden, or not situated on the screen properly. Because we'd rather have this functionality, lets add it now. We will be using the SizeChanged event kindly provided from our form, so checking if the form has been resized is easy. Add the following code to your class.
#Region "Keep the paddle and score labels in the correct position when the form is resized." Private Sub pongMain_SizeChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.SizeChanged paddlePlayer.Location = New Point(Me.Width - 44, paddlePlayer.Location.Y) plrScoreDraw.Location = New Point(Me.Width - 54, plrScoreDraw.Location.Y) End Sub #End Region
You should now have a fully functional pong game. I hope you had fun creating this game, and will have learnt something from this. Thanks for reading, and enjoy.