To get started go a head and create a new VB.NET Windows Forms application. I called mine VBGameProgramming. Now you will want to add a single Picture Box control to the form and call it pbSurface. What you will want to do next is set two properties of pbSurface. The first one will be the Dock property. You want to set the to be Fill. The other property you want to set is the BackColor property. Set that to be Black. There is just one other thing that I want to do in the designer. I want to create an event handler for the Shown event for when the form is first open. Click on the Title bar of the form to make sure that it is selected. Now, click the little lightning bolt in the properties window to bring up the list of event handlers. Create a new event for the Shown event. You can create the default event by double clicking the word Shown. That is all I'm going to do with the designer. For the rest of the tutorial I will be working in code.
I'm not going to create a class for the timer. I'm just going to use the Stopwatch class. There are some variables that you will want to add to the code for Form1. In my tutorials I like to show the code and then explain the way things work.
Dim timer As Stopwatch Dim backBuffer As Image Dim graphics As Graphics Dim clientWidth As Integer Dim clientHeight As Integer Dim interval As Long Dim startTick As Long Dim imageRect As Rectangle Dim direction As Point
The first field timer will be used to have the game run at the same speed on different computers. If you don't preform timing and your computer is a slower or faster than a friend's computer and you try and run it on their computer it would be too fast if yours is much slower or too slow if your is much faster. It is typically better to draw to a back buffer and then flip the back buffer to the screen. This will help to reduce flicker. That is why there is a variable called backBuffer and is of type Image. To do the rendering with GDI+, which is slow by the way, you need an object of the Graphics class. That is what the next variable is for. The next two variables, clientWidth and clientHeight, will hold the size of the drawing area. I will need them frequently and it is usually better to assign the values to variables rather than using magic numbers. The next two fields will be used to control the speed at which the game runs. The interval variable will hold how long to pause the game for. The startTick variable will hold in microseconds when each pass through the game loop starts. The next two variables are for drawing a rectangle on the screen and bouncing it around the screen. The imageRect will be the actual rectangle and direction will control the speed at which the rectangle moves around the screen.
You need to set up a few things in the Form1_Load event and initialize a few variables. This is the code for the Form1_Load event.
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Me.DoubleBuffered = True Me.MaximizeBox = False Me.FormBorderStyle = Windows.Forms.FormBorderStyle.FixedSingle timer = New Stopwatch() clientWidth = 300 clientHeight = 300 interval = 16 Me.ClientSize = New Size(clientWidth, clientHeight) backBuffer = New Bitmap(clientWidth, clientHeight) graphics = graphics.FromImage(backBuffer) direction = New Point(2, 3) imageRect = New Rectangle(0, 0, 55, 65) End Sub
The first line sets the DoubleBuffered property of the form to True to keep it from flickering. I don't want the size of the form to change so I set the MaximizeBox property to False and the FormBorderStyle property to FixedSingle. Since timer is an object you need to create a new instance for it. I set the clientWidth and the clientHeight variables to 300. I then set the interval variable to 16. That is 16 milliseconds which is about 1/60 of a second giving a frame rate of about 60 frames per second. I set the ClientSize property of the form to be 300 by 300. I also create a new Bitmap for backBuffer to be the same size. There is no constructor for the Graphics class. I use the FromImage method using the backBuffer image for this. I then set the direction of rectangle to be (2, 3). That means the X position of the rectangle will initially move 2 pixels to the right. This is because when you are drawing the X axis increases as you move from the left side of the screen to the right side. The rectangle will also move 3 pixels down the screen initially because Y increases as you move from the top of the screen down. I know this is different than in math. I also create a rectangle that will start at X and Y coordinate (0, 0) and is 55 pixels wide and 65 pixels high.
There is only one line of code in the Form1_Shown event handler. It just calls a sub that I will right that will handle the game loop. You can create games using the Timer control that comes with the .NET framework. A game loop is just more accurate as the accuracy of the Timer control isn't as good as the accuracy of the Stopwatch. This is the code for the Form1_Shown event handler
Private Sub Form1_Shown(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Shown GameLoop() End Sub
The GameLoop sub is where I will control the game. In a game there is always a game loop. What happens in the game loop is that you update any objects in the game. You then draw the objects of the game. You preform any events that are in the event queue and you preform your timing to control the speed the game runs at. This is the code for the GameLoop sub.
Private Sub GameLoop() timer.Start() Do While (Me.Created) startTick = timer.ElapsedMilliseconds GameLogic() RenderScene() Application.DoEvents() Do While timer.ElapsedMilliseconds - startTick < interval Loop Loop End Sub
What this code does is first start the timer so that it is running and I can use it to control the speed at which the game runs. Now comes the actual game loop. What happens here is that this will loop while the form is in existence. The first thing you need to do is determine which millisecond the loop starts at. You then call the GameLogic sub, which I will show you shortly, to perform the logic of the game. Next you call the RenderScene sub, which I will show you shortly, to draw the scene. The next line will process any events that are of interest to the form. The next loop will just loop until the current millisecond of the timer minus the startTick is less than the interval variable. This is what has the game run at a constant speed on slower and faster computers. I will let you know that using GDI+ is slow. Having a frame rate of 60 frames per second for a large game that draws a lot of objects will not be possible. You will more than likely have to go for a lower frame rate. There are a few tricks that you can do to speed this up a little. I will get into that in future tutorials.
Now it is time for the GameLogic sub. Like I mentioned, this sub is where I will control the moving of the object on the screen. This is the code for the GameLogic sub.
Private Sub GameLogic() imageRect.X += direction.X imageRect.Y += direction.Y If imageRect.X < 0 Then imageRect.X = 0 direction.X *= -1 End If If imageRect.Y < 0 Then imageRect.Y = 0 direction.Y *= -1 End If If imageRect.X + imageRect.Width > clientWidth Then imageRect.X = clientWidth - imageRect.Width direction.X *= -1 End If If imageRect.Y + imageRect.Height > clientHeight Then imageRect.Y = clientHeight - imageRect.Height direction.Y *= -1 End If End Sub
If you are not used to game programming that might look a little intimidating. What this does is each time the code runs is add the X and Y properties of direction to the X and Y properties of imageRect. If this was to continue the rectangle would eventually disappear. So I will do some bounds checks to see when the rectangle reaches the edges of the window. Checking for the left hand side and top of the form are the easiest cases. You know the X and Y coordinates of the rectangle because they are properties of the rectangle. To see if the rectangle will go off the left side of the screen you check to see if the X property is less than zero. If it is you set it to zero to keep it from going off the screen. Just staying in the same position is boring though. So what I do is multiply direction.X by -1 which will reverse the direction. To keep it from going off the top of the screen what you do I check to see if the Y property is less than 0. If it is set it 0 and then multiply direction.Y to reverse the direction the square is traveling. Checking for the right hand side is a little more difficult what you do is check to see if the X property plus the width of the rectangle is greater than the width of the client area. If it is set it to the X property to the width of the client area minus the width of the rectangle and again reverse the direction. Finally for the bottom of the window you check to see if the Y property of the rectangle plus the height is greater than the height of the client area. If it is set the Y property to the height of the client are minus the height of the rectangle and reverse the direction.
That just leaves the RenderScene sub. This is the code for that sub.
Private Sub RenderScene() backBuffer = New Bitmap(clientWidth, clientHeight) graphics = graphics.FromImage(backBuffer) pbSurface.Image = Nothing graphics.FillRectangle(Brushes.Blue(), imageRect) pbSurface.Image = backBuffer End Sub
This is the code that does the actual drawing. Each frame I create a new image for the backBuffer. What this does is create a blank drawing surface to draw to. Since I created a new image I need to create a new graphics object to draw with. I then set the Image property of pbSurface to be Nothing and that will remove the image from pbSurface effectively erasing the image. I then draw a filled rectangle to the image using a blue brush and the rectangle. I then set the Image property of pbSurface to be the new image drawn backBuffer.
Well, that is it for this tutorial. I plan to write a few more of these to help those VB.NET programmers that would like to create games with VB.NET.