Results 1 to 7 of 7

Thread: Tutorial [Snake using BitBlt] NMs Game tutorial 0.5

  1. #1

    Thread Starter
    Retired G&G Mod NoteMe's Avatar
    Join Date
    Oct 2002
    Location
    @ Opera Software
    Posts
    10,190

    Tutorial [Snake using BitBlt] NMs Game tutorial 0.5

    This is a tutorial on how to make a BitBlt snake game. This is not the simplest way to do it. If you are completely new to game programming in VB. You should try to look at part 1 of my game programming tutorials. It is using pictureboxes, it is a little bit easier. If you have read that tutorial, this one should be easy. This one is not that much longer, but it is using one API call. But if you feel ready, I think we should just jump in to it. I am using VB6 in this example. But VB5 should do fine too.


    Setting form properties:

    When making games it is always easier to handle the coordinates of the screen in pixels than twips (twips is the default one). So start by changing the ScaleMode property of the form to Pixels. And if you don't know it from before, it is nice to know that in VB the coordinates of your form is (0,0) in the upper left corner. It is not easy to think this way of the coordinate system at once, but you will get used to it.
    Last edited by si_the_geek; Feb 27th, 2004 at 12:56 PM.

  2. #2

    Thread Starter
    Retired G&G Mod NoteMe's Avatar
    Join Date
    Oct 2002
    Location
    @ Opera Software
    Posts
    10,190
    Start Coding:

    We will split the snake in small parts. Lets call one square of the snake for a BodyPart. The head is one BodyPart. The next square of the snake is a body part and so on. Our game has to always remember the coordinates of this BodyParts and what way they are heading (facing). This way we can get the body to follow the head. To do this we will use a User Defined Type (UDT). It is more or less the same thing as any data type like String, Integer, or Double. But with the UDT we can make our own. Do to this we first have to add a module to the game. Open up the code for the module and enter the following code.

    VB Code:
    1. 'User Defined Type for a snake part.
    2.  
    3. Type SnakePart
    4.     X As Long               'X-Coordinate
    5.  
    6.     Y As Long               'Y-Coordinate
    7.  
    8.     Facing As Long          '1 = left, 2 = up, 3 = right, 4 = down
    9.  
    10. End Type


    We now have made our own UDT that is called SnakePart that can hold the X,Y coordinates and the way the part is facing (1 = left, 2 = up, 3 = right, 4 = down). But we will also know how many body parts the snake has to draw it in the right length. So lets add an other line of code to the module.

    VB Code:
    1. 'Length of the snake
    2.  
    3. Global Length As Long


    When we are using BitBlt we also need some way to know where the food is on the form. So we have to add one more UDT. It only holds the X and Y coordinate. And it looks like this

    VB Code:
    1. 'User Defined Type for the food
    2.  
    3. Type FoodXY
    4.     X As Long               'X-Coordinate
    5.  
    6.     Y As Long               'Y-Coordinate
    7.  
    8. End Type


    Now the tricky part comes. You can place the next bit of code in the top of the module. It is the API call. You need to declare it so VB knows where to look for it. It looks like this.

    VB Code:
    1. Public Declare Function BitBlt Lib "gdi32" (ByVal hDestDC As Long, ByVal X As Long,  
    2. ByVal Y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long,
    3.  ByVal xSrc As Long, ByVal ySrc As Long, ByVal dwRop As Long) As Long


    It's not pretty, but it is just copy paste material. You don't have to remember this. You declare it as public because it is in a module rather then the same form as you are writing the rest of the code in. The GDI32 part tells VB in witch library it shall look for the function that is called BitBlt. In the parentheses you have the parameters this function is using. The first one is a hDC. If you don't know what that is, don't get panic. It is just a reference to a memory location, that you are going to use to copy the picture. The second and third one is the X and Y coordinate where you want to copy the image too. The forth and fifth parameter is the width and height of the picture you want to copy. The sixth and seventh parameter is the X and Y coordinate of the upper left point of the picture where you are copying from. Most often this is 0 and 0, that means the whole picture. And the last parameter is the type of copying you want to use. We are only going to use one way (vbSrcCopy), it is only copying what you are seeing, to where you want it. Simple as that.

    So that was all the code that we need to put in the module. Now we will head back to the form.
    Last edited by si_the_geek; Feb 27th, 2004 at 12:58 PM.

  3. #3

    Thread Starter
    Retired G&G Mod NoteMe's Avatar
    Join Date
    Oct 2002
    Location
    @ Opera Software
    Posts
    10,190
    Making the SnakeParts:


    We need to make some variables that can hold the SnakeParts. For that we will use an array. We don't know how long the maximum snake will be, so we probably should have made the array dynamically. But to simplify things here, let’s say that it is 100. So add this line to the top of your app.

    VB Code:
    1. Dim Snake(99) As SnakePart


    Now we have 100 variables of the SnakePart type that is ranged from 0 to 99. So now let’s get busy with the snake.

    We also need to make a variable for the food coordinates. you do that like this:

    VB Code:
    1. Dim Food As FoodXY



    Drawing the snake:

    We have to decide the size of each part of the snake. You can change this later. But leave it to be 10*10 pixels for now. You can change it if you want when you understand how all the code is working.
    Draw a small (10*10 pixels) drawing of a body part of a snake in MSPaint or any other paint app that you have installed. Then go back to VB and put an picture box on your form. Set the name property of the image control to "imgSnake".
    Now go to the paint app you used to make the bodypart for the snake. And use a selection tool to select the whole picture. Then press [Ctrl]+[C] (copy). Then move to VB and choose one of the image controls that you just made and then press [Ctrl]+[V] (paste) to paste the picture to the picture box. Then set the autosize property of the picture box to true, and the autoredraw property to true, then set the visible property to false. You don't want it to be visible. That's it. You have made a snake. It may not look like a snake, but in a moment we will try to make it at least move like a snake.



    Writing some code:


    Now we will write some code for the game. We need first some code in the form load event. To make all the starting values of the variables. We need to tell the game the starting length of the snake. Where the "head" is placed. Where the body is placed. And what way the snake is moving. We also needs to move all the image controls to a starting place and update all the UDT for the body. We will do this in a loop. Write down this code in the form load event:

    VB Code:
    1. 'Sets the initial values of the snake
    2.  
    3.     Length = 5
    4.     For i = 0 To 4
    5.         Snake(i).X = (120 - 10 * i)
    6.         Snake(i).Y = 120
    7.         BitBlt Form1.hDC, Snake(i).X, Snake(i).Y, imgSnake.Width, imgSnake.Height, imgSnake.hDC, 0, 0, vbSrcCopy
    8.         Snake(i).Facing = 3
    9.     Next i


    the code loops from 0 to 4. That makes the loop execute 5 times, once for each part of the body. In the loop it is in the first line setting the X coordinate of the body. You can see the i in the parentheses, it will change every time the loop is executed. So the first time it will be 0 and the next 1 and so on. So the first time it is executed we change the properties of the "head". First the X coordinate, then the Y coordinate. Then it places the first image control at the X,Y coordinate. And then it tells it what way that snake part is heading. Then the loop is executed with the i as 1. It will get all the same properties fixed except the X coordinate. This time the X coordinate is (10 * i) 10 pixels more to the left. Remember the width of a body part is 10pixels. So the net time the loop is executed it will be placed 10pixels to the left of that one too. This will make all the image control line up like a snake. The new part here is the BitBlt call. It has a lot of parameters, but it is really simple. As I said earlier the first parameter is the memory location where we want the picture to be copied to. This time it is the Form, but it could be lot of other things. The of the form is what we are suing. It holds the memory location where we want to copy it to. The two next parameters is the X and Y coordinates for that body part. Remember i is increasing every time, so the first time it is the first parts X and Y coordinate and the second time it is the seconds part and so on. Then it is the width and height of the copy we want to copy, and then the memory loaction of the source image. We are using the imgSnakes hDC to get it. Then we tell that we want to copy the entire picture, hence the 0,0. And last but not least what way we want to copy it. We use vbSrcCopy, the normal way to do it.
    Last edited by si_the_geek; Feb 27th, 2004 at 01:00 PM.

  4. #4

    Thread Starter
    Retired G&G Mod NoteMe's Avatar
    Join Date
    Oct 2002
    Location
    @ Opera Software
    Posts
    10,190
    Food:


    Now we are going to think of the food for a while. First we need a picture of the food. By thinking of the size of that picture, we can simplify a lot of things here. Let’s make the pic 10*10 pixels and in another color then the body. When you have painted it, go back to VB, place a new picture box on the form. Set the name property to imgFood. Go back to paint, select the pic and copy it the same way as before. Then paste it to the image control on the form in VB. Then set the autosize property of the picture box to true, and the autoredraw property to true, then set the visible property to false. You don't want it to be visible. Now we need to think of a place to put the food. Lets do that in code. Add this code to the form load event.

    VB Code:
    1. 'Randomizing the Rnd function.
    2.  
    3.     Randomize
    4.      
    5.     'Placing the food for the game to start
    6.  
    7.     Call PlaceFood


    The first statement is called and uses the time from the computer as a seed, to make the Rnd function random. So every time you start the app, the Rnd function will give a different value.
    the second statement is a call to a Sub. It is calling some code that we are going to write now. The code is going to put the food a place on the form, so the snake can get it. We want to move the snake a certain amount of pixels every time, so to simplify things a bit, we will do some code in the PlaceFood sub that might seem to be a little to much. But it is actually pretty smart. OK, make a new sub and call it PlaceFood. And then add this code to it.

    VB Code:
    1. 'Finds a new X-coordinate for the food.
    2.  
    3.     Do
    4.         Food.X = Rnd * (Form1.ScaleWidth - imgFood.Width)
    5.     Loop While (Food.X Mod 10 <> 0)
    6.      
    7.     'Finds a new Y-coordinat for the food.
    8.  
    9.     Do
    10.         Food.Y = Rnd * (Form1.ScaleHeight - imgFood.Height)
    11.     Loop While (Food.Y Mod 10 <> 0)
    12.      
    13.     BitBlt Form1.hDC, Food.X, Food.Y, imgFood.Width, imgFood.Height, imgFood.hDC, 0, 0, vbSrcCopy



    It is made by two loops that is looping until we have found a place we can put the food so the snake can get it. The first loop, finds the X coordinate of the food. It uses the Rnd function to find a number between 0 and 1 and multiplies that number to the width of the form. It also takes away some of the width of the form, so the food is not going to be placed so some of the food is not placed out of the form. It then checks to see if we constantly moves the snake with 10 pixels, if it is possible for the snake to land on the exact same position as the food. The Food coordinate variable earlier is storing the values so we can use it in the BitBlt function afterwards. As you can see the destination in the BitBlt function is still the form. The coordinates and size is now for the food and not the snake. And the hDC is also from the food picture box. The rest is the same as before.



    Game Loop:


    We need a game loop that is running as long as the game is running. The simplest way to do this is to use a timer. Put a timer on the form. Sat the Name property to tmrLoop, and the interval to 150, and enabled to true. This is now our game loop that is called every 0.15 second. We are going to use this "loop" to move the head of the snake, move the body, and to check if the snake found the food. Add these lines of code to the tmrLoop timer event. We also need to clear the screen, because we have already copied pictures to it. And don't want them there any more, only the new one. The form has a function called Cls for doing this, so we will use that one.

    VB Code:
    1. 'Clear the screen
    2.  
    3.     Form1.Cls
    4.      
    5.     'Check to see if the snake finds food
    6.  
    7.     Call FindFood
    8.      
    9.     'Moves the snake
    10.  
    11.     Call MoveSnake


    These lines call two subs that we are going to make soon. The first one checks to see if the snake found food. And the other one is moving the whole snake. Let’s do the fun part first.
    Last edited by si_the_geek; Feb 27th, 2004 at 01:01 PM.

  5. #5

    Thread Starter
    Retired G&G Mod NoteMe's Avatar
    Join Date
    Oct 2002
    Location
    @ Opera Software
    Posts
    10,190
    Move Snake:

    We are going to move the body first and then the head. So the first loop is moving the body. It moves the back of the snake first, and then moves the part that is in front of that one and so on. The part of the snake that is in the rear gets the coordinates of the part that is in front of it. And it also gets the facing variable of the part in front. This movement is made in this loop. Make a Sub called MoveSnake, and then add this code:

    VB Code:
    1. Dim i As Long
    2.      
    3.     'Move body
    4.  
    5.     For i = (Length - 1) To 1 Step (-1)
    6.         Snake(i).X = Snake(i - 1).X
    7.         Snake(i).Y = Snake(i - 1).Y
    8.         Snake(i).Facing = Snake(i - 1).Facing
    9.         BitBlt Form1.hDC, Snake(i).X, Snake(i).Y, imgSnake.Width, imgSnake.Height, imgSnake.hDC, 0, 0, vbSrcCopy
    10.     Next i


    It is first giving the x coordinate of the UDT in front of it selves, then assigning it to the image holding that snake part. Then it does the same with the Y coordinate before it gives the facing variable to it. Then we once again use the BitBlt function to copy all the body parts.



    Move the head:

    But we need to treat the head a bit different, because the head has no body part in front of it to give him his coordinates. So we need to find another way of moving him. We are going to use the heads facing variable to just move it in that direction the head is facing. This is very straight forward, so just add this code under the code for the body in the same Sub.

    VB Code:
    1. 'Move head
    2.  
    3.     If Snake(0).Facing = 1 Then         'Moves left
    4.  
    5.         Snake(0).X = Snake(0).X - 10
    6.     ElseIf Snake(0).Facing = 2 Then     'Moves up
    7.  
    8.         Snake(0).Y = Snake(0).Y - 10
    9.     ElseIf Snake(0).Facing = 3 Then     'Moves right
    10.  
    11.         Snake(0).X = Snake(0).X + 10
    12.     ElseIf Snake(0).Facing = 4 Then     'Moves down
    13.  
    14.         Snake(0).Y = Snake(0).Y + 10
    15.     End If
    16.      
    17.     'Draw the head
    18.  
    19.     BitBlt Form1.hDC, Snake(0).X, Snake(0).Y, imgSnake.Width, imgSnake.Height, imgSnake.hDC, 0, 0, vbSrcCopy


    If the facing variable of the head is 1 (left) then it moves 10 pixels to the left. If it 2 (up) then move 10 pixels up and so on. Nothing magical to it at all. We are also here using the BitBlt function to draw the head.


    The last thing we are going to do here is to draw the food again. Yes we have done that earlier, but we cleared the screen remember? So once again we are using the coordinates of the food to draw it again like this.

    VB Code:
    1. 'Draw the food again on the same place, because we cleared the screen
    2.  
    3.     BitBlt Form1.hDC, Food.X, Food.Y, imgFood.Width, imgFood.Height, imgFood.hDC, 0, 0, vbSrcCopy



    Moving the head in another direction:

    But we want to be able to move the head in another direction. To do this we are going to use the arrow keys. To use the keyboard in VB we need to set the KeyPreview property of the form to true. Then we need to write some code to change the facing variable of the head. Do that in the form keydown event.

    VB Code:
    1. 'Turning the face of the snake
    2.  
    3.     If KeyCode = 37 Then            'Left
    4.  
    5.         Snake(0).Facing = 1
    6.     ElseIf KeyCode = 38 Then        'Up
    7.  
    8.         Snake(0).Facing = 2
    9.     ElseIf KeyCode = 39 Then        'Right
    10.  
    11.         Snake(0).Facing = 3
    12.     ElseIf KeyCode = 40 Then        'Down
    13.  
    14.         Snake(0).Facing = 4
    15.     End If


    This code changes the heads facing variable according to the arrow that is pressed. If the left arrow (keycode 37) is pressed, the facing variable is changed to 1. And so on. This is also very straight forward. And now when you have added these lines of code you can for the first time try the game, and move the snake in the direction you want it to. But there is one thing left before the game is playable at all. That is that the snake can at the moment not eat food and grow.
    Last edited by si_the_geek; Feb 27th, 2004 at 01:04 PM.

  6. #6

    Thread Starter
    Retired G&G Mod NoteMe's Avatar
    Join Date
    Oct 2002
    Location
    @ Opera Software
    Posts
    10,190
    Eat food:

    Start with making a Sub called FindFood. The first thing we need to figure out is if the head of the snake is the same place as the food. And finally our hard coding work is finally paying off. Because we made sure that the food is placed exactly at a place where the snake can get, this is really easy to do. So to check if the snake found food, We only need to do a small if test. Place this code in the FindFood Sub.

    VB Code:
    1. Dim i As Long
    2.          
    3.     'If the head finds food
    4.  
    5.     If Snake(0).X = Food.X And Snake(0).Y = Food.Y Then
    6.          
    7.     End if


    This if test are testing if the snakes head X and Y coordinates is at the exact same place as the foods X, Y coordinates. We now need some lines of code to make the snake bigger, and then place the new body part of the snake in a new place.

    We don't need to make any new body part, we will just add one more to the length and then place it on the end of the snake. You do that like this.

    VB Code:
    1. 'Placing the new body on it's place
    2.  
    3.         If Snake(Length - 1).Facing = 1 Then        'Looking left
    4.  
    5.             Snake(Length).X = Snake(Length - 1).X - imgSnake.Width
    6.             Snake(Length).Y = Snake(Length - 1).Y
    7.         ElseIf Snake(Length - 1).Facing = 2 Then    'Looking up
    8.  
    9.             Snake(Length).X = Snake(Length - 1).X
    10.             Snake(Length).Y = Snake(Length - 1).Y + imgSnake.Height
    11.         ElseIf Snake(Length - 1).Facing = 3 Then    'Looking right
    12.  
    13.             Snake(Length).X = Snake(Length - 1).X - imgSnake.Width
    14.             Snake(Length).Y = Snake(Length - 1).Y
    15.         ElseIf Snake(Length - 1).Facing = 4 Then    'Looking down
    16.  
    17.             Snake(Length).X = Snake(Length - 1).X
    18.             Snake(Length).Y = Snake(Length - 1).Y - imgSnake.Height
    19.         End If
    20.          
    21.         'increase the length of the snake
    22.  
    23.         Length = Length + 1


    This code is checking to see what way the back of the snake is heading. If it is heading left the new part is added to the right of the snake. If the rear is heading up, the new bodypart is added to the bottom of the snake. It is then setting the visible variable to true of that image control.

    And then we have to place the food in a new place on the form. But because we did this in a other sub early in the game. We only need to write one line of code to make it work. This is a really powerful programming practice. So just add this line to the If statement as well.

    VB Code:
    1. 'Placing the food an other place
    2. Call PlaceFood


    You have now finished the "game", and can try it. It is still tons of things that you can add to it to make it a game. But I will leave it here for now. And later when I have more time, I will add more to it. But I can make a list of things that you can try to add to the game.


    - Make it impossible to walk through your own body
    - A picture of an actual head of the snake.
    - A picture of an actual tail of the snake.
    - Make the game stop if the snake hits the outer bounds of the form.
    - Make walls in the game.
    - Add levels to it, with different speed and walls.
    - Add a two player game.


    If you have more questions, please ask in the forum, where there is probably more than just me that can answer to the questions.

    The "complete game" you can find
    here in the attachment.
    Attached Files Attached Files
    Last edited by si_the_geek; Feb 27th, 2004 at 01:06 PM.

  7. #7
    Super Moderator si_the_geek's Avatar
    Join Date
    Jul 2002
    Location
    Bristol, UK
    Posts
    41,929
    The files within this thread (submitted: 25 Feb 2004) have been checked for malware by a moderator.

    Disclaimer: This does not necessarily mean that any compiled files (DLL/EXE/OCX etc) are completely safe, but any supplied code does not contain any obvious malware. It also does not imply that code is error free, or that it performs exactly as described.

    It is recommended that you manually check any code before running it, and/or use an automated tool such as Source Search by Minnow (available here or here).
    If you find any serious issues (ie: the code causes damage or some sort), please contact a moderator of this forum.

    Usage of any code/software posted on this forum is at your own risk.



    The above posts have been edited (with NoteMe's permission) to correct spelling/grammar errors.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width