Animation in VB (Part 2)

Previously, you looked at doing stick figures and simple 2D vectors; this time, you are looking at animating simple raw images. You also are going to be using a few rather interesting APIs, mostly BitBlt. By the end of this article, you will have covered enough to write a simple Platform game.

Animating the Bitmap Image

In the first example, you are going to use a single image built up of multiple frames of your animation. By selecting just the piece of the image that contains your current frame, and displaying it in sequence, you can create a simple animation.

Why use a single image to hold all the frames? Simple. It's only a single image that needs to be loaded and it's easy to switch and change graphics as needed. Also, a single image and an array of locations uses less memory that an array of images.

So, what are you animating? This is the image that you built up for your animation. Each frame is 64 by 64 pixels, and spaced 64 pixels apart.

After you have your frames set up in the image, you need to load them into your application. You create a picturebox that is hidden to hold the full image. Then, you set up an array of pointers to the top left corner of each frame within the image.

Public Type Cords
   X As Long
   Y As Long
End Type

Public ScrLoc(5) As Cords

Public Sub Init()
ScrLoc(0).X = 0
ScrLoc(1).X = 64
ScrLoc(2).X = 128
ScrLoc(3).X = 0
ScrLoc(4).X = 64
ScrLoc(5).X = 128
ScrLoc(0).Y = 0
ScrLoc(1).Y = 0
ScrLoc(2).Y = 0
ScrLoc(3).Y = 64
ScrLoc(4).Y = 64
ScrLoc(5).Y = 64
End Sub

After everything is set up and loaded, you can start your timer and step through the frames to complete your animation.

Private Sub Timer1_Timer()
   BitBlt Picture1.hdc, 0, 0, 64, 64, Picholder.hdc, _
      ScrLoc(Img).X, ScrLoc(Img).Y, vbSrcCopy
   Img = Img + 1
   If Img > 5 Then Img = 0
End Sub

In this code, you are simply copying each frame over the previous. This is sufficient for simple static framed animations, useful for About and Startup pages in your applications.

Download the Simple BLT Sample below to see this animation at work.

Moving the Animated Image

Okay, so now you need to make this animation move around the screen. The first thing you need to do is clear out the previous frame and place the next frame in the new location. For this, you need to set up a few coordinate variables, one for the new image location, and one for the old image location, as well as your Tristate variables to enable you to move the image.

You also add a second timer to the form so that you can adjust the speed that the image moves separately from the speed of the animation.

Public Enum Tri_Stat
   neg  = -1
   Zero = 0
   pos  = 1
End Enum

Private Xmove As Tri_Stat
Private Ymove As Tri_Stat

Private Old_Loc As Cords
Private New_Loc As Cords

Next, you set the form's Keypreview property to True so that you have a single event to handle all the key presses in your project. You add the relevant code to the KeyDown event.

Note: this code is almost identical to the code in the Asteroids Demo in Part 1.
Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
Select Case KeyCode
   Case 37
      Xmove = neg
   Case 39
      Xmove = pos
   Case 38
      Ymove = neg
   Case 40
      Ymove = pos
   Case 32, 12
      Xmove = Zero
      Ymove = Zero
End Select
'32 = Space
'38 = up
'40 = Down
'39 = right
'37 = left
End Sub

Because you are blanking large sections at a time and not just single pixel lines, you use a buffer picturebox to do the hard work on, and simply overlay the complete buffer onto your display. This might sound complicated but it's as simple as drawing the image in the background, and once you're complete, show it. This is done to limit the amount of flicker you would see when the image is cleared.

In your animation Timer, you set your image source as you did previously, but into your buffer image, and then transfer the buffer to your picturebox.

Private Sub Timer1_Timer()
   BitBlt Buffer.hdc, New_Loc.X, New_Loc.Y, 64, 64, _
      Picholder.hdc, ScrLoc(Img).X, ScrLoc(Img).Y, vbSrcCopy
   BitBlt Picture1.hdc, 0, 0, Buffer.ScaleWidth, _
      Buffer.ScaleHeight, Buffer.hdc, 0, 0, vbSrcCopy
   Img = Img + 1
   If Img > 5 Then Img = 0
End Sub

In your movement timer, there is somewhat more that needs to be done. You first update the location according to your movement variables. You check whether the frame is still within borders, and change the direction around if the image is on the edge. Once you know where the new image is going to be, you White out the previous image at the old coordinates, and place the current frame at the new coordinates. Then, finally, you transfer the buffer to the display.

Private Sub Timer2_Timer()
   With New_Loc
      If .X < 0 Then Xmove = pos
      If .Y < 0 Then Ymove = pos
      If .X > Picture1.ScaleWidth  - 64 Then Xmove = neg
      If .Y > Picture1.ScaleHeight - 64 Then Ymove = neg
      .X = .X + Xmove
      .Y = .Y + Ymove
   End With
   BitBlt Buffer.hdc, Old_Loc.X, Old_Loc.Y, 64, 64, Buffer.hdc, _
      0, 0, vbWhite
   BitBlt Buffer.hdc, New_Loc.X, New_Loc.Y, 64, 64, _
      Picholder.hdc, ScrLoc(Img).X, ScrLoc(Img).Y, vbSrcCopy
   BitBlt Picture1.hdc, 0, 0, Buffer.ScaleWidth, )
      Buffer.ScaleHeight, Buffer.hdc, 0, 0, vbSrcCopy
   Old_Loc = New_Loc
End Sub

To see this animation method at work, download Step 2 below.

On the next page, you are going to look at moving a non-square image over a color background. You will use masks and double buffering.

Animation in VB (Part 2)

Moving Over Other Images

Previously, you were moving images over a white background, and simply whiting out the old image. You now will look at moving one image over another image, without destroying or reloading the complete background image. For this, you are going to move a ball around on a beautiful background picture.

Because you've gone over the animating on the previous page, I am not including it in this example; however, later you will be doing animation, masking, and backgrounds all in one smooth moving game. This is been done so that you can clearly see what is needed for each part of each step.

Having looked at what is involved in moving an image over a white background, you can see that once an image is placed, anything on the background is effectively replaced by your new image. The background will need to be replaced with the original; however, reloading the background is tedious work, especially if the background is not static. So, in this example, you will be working on the presumption that the background is not static, even though it is.

First, you have to set up your form and general details. This example uses the same Coordinate and Tristate variables, as well as the key press subroutines as in the previous example, so I will not be repeating any of that here.

After setting up the form and variables, you will need to set up up your buffers and image holders. For now, I'm using additional picturebox controls so that if you want to check to see what's happening at any point, you can make them visible. At a later stage, I will show you how to create them dynamically.

PicHolder.Picture  = LoadPicture(App.Path & "\ball.bmp")
MaskHolder.Picture = LoadPicture(App.Path & "\ballmask.bmp")
Buffer1.Width      = PicHolder.Width
Buffer1.Height     = PicHolder.Height
Buffer2.Width      = Screen.Width
Buffer2.Height     = Screen.Height
Buffer2.Picture    = LoadPicture(App.Path & "\sunset.jpg")

Buffer 1 will be used to store the original background of where your image will be placed, and Buffer 2 is where you will do all the BitBlt work. The static background is loaded into Buffer 2 directly so that there is no need to load it for every frame.

The idea here is to work out where the image will sit, copy the section of the background where you will be working, and put your image in its place. Once done, all that is left is to display the final image.

To accomplish this, you use the following in your timer event.

Private Sub Timer1_Timer()
   With Location
      If .X < 0 Then XMove = pos
      If .Y < 0 Then YMove = pos
      If .X > Buffer2.ScaleWidth  - 64 Then XMove = neg
      If .Y > Buffer2.ScaleHeight - 64 Then YMove = neg
      .X = .X + (2 * XMove)
      .Y = .Y + (2 * YMove)
      BitBlt Buffer1.hdc, 0, 0, 64, 64, Buffer2.hdc, .X, .Y, _
         vbSrcCopy
      BitBlt Buffer2.hdc, .X, .Y, 64, 64, MaskHolder.hdc, 0, 0, _
         vbSrcAnd
      BitBlt Buffer2.hdc, .X, .Y, 64, 64, PicHolder.hdc, 0, 0, _
         vbSrcPaint
      BitBlt Screen.hdc, 0, 0, Buffer2.Width, Buffer2.Height, _
         Buffer2.hdc, 0, 0, vbSrcCopy
      BitBlt Buffer2.hdc, .X, .Y, 64, 64, Buffer1.hdc, 0, 0, _
         vbSrcCopy
   End With
End Sub

It might not look like much, but there is plenty happening in the BitBlt section. In order, the BitBlt commands do the following:

  1. Copy the original background to our first buffer, only as big as your image.
  2. Apply your mask image to the area to whiteout parts of the background that your image will occupy, on the second buffer.
  3. Paint your image into the whitened-out area of the background.
  4. Display the completed image.
  5. Restore the background in the second buffer because the completed image has now been displayed.

There's a lot that needs to be done before you can use this sequence of BitBlts. The original image I created for this example looks like this.

[OriginalBall.jpg]

From this, I created the mask.

[BallMask.jpg]

And then, I made the final overlay image.

[Ball.jpg]

How Does Masking Work?

The purpose of masking is that often the images that we want to overlay are not perfect rectangles, but, as in this case, they are circular. And when we place them, there will be areas that we want to make transparent. When working with VB and BitBlt, unfortunately we do not have the facility to set a transparent color on the image.

The basic principle here is logic bit manipulation. AND and OR logic takes place manipulating the color pixels to effectively merge the two images. The Mask is used to blank out the area where our image is going to be displayed. With the logical AND any area of the mask that is white (or Hex FFFFFF) will be left untouched and the area that is black (or Hex 000000) will be blacked out.

After the mask has been applied the overlay image can be applied with a logical OR. Where the overlay is black (or Hex 000000), the background image is left unaltered. Anything logical OR'ed with 0, return the original value. However where the background is now Black the overlay image will be placed.

To see how masking works, download the Step 3 example below.

On the next page, you are going to start working on your Demo game, using what you have learned up to now, and a few little extras.

Animation in VB (Part 2)

The Game

For this exercise, you will be creating a clone of the classic platform game Donkey Kong. Again, as done with the 2D Vector game in Part 1, I will only cover the essentials and give you a demo of the game, leaving some of the final details and animation up to you to complete. Donkey Kong has many animations that take place simultaneously, and requires a lot of graphical work to complete. Below is a screen shot of the original game that you will be cloning.

[DonkeyKong.jpg]

As you can see, there are many animation aspects that need to done to complete this game. Parts that require animation are the Hero, Barrels, Drumfire, burning barrel, Gorilla, and Maiden.

One critical area in creating a platform game is the playing field. It is often difficult to decide on whether to create a static background per stage or to dynamically create it from a few smaller parts. This is generally dependant on how you plan to show the playing field to the user, and how you want to animate the platform.

In the case of Donkey Kong, many will remember the short animated video that starts the game with the platforms all moving as the gorilla stomps on the top level. You could draw the platform frame for frame; or rather, using what you've learnt so far, you animate a few dozen smaller pieces of the platform. At the same time, by keeping a list of each smaller piece of the platform, you also have a coordinate that your player can move over.

The complete platform in this case can be built up of two simple pieces.

[Floor.jpg]    [Ladder.jpg]

These two basic parts can be placed across the entire playing field to create your platforms. Essentially, if done correctly, this method would also use up a lot less memory than a complete pre-drawn platform.

The character animation is a lot more complex than the background. Each frame of the character needs to be drawn for each direction that the character needs to move in. Then, the mask needs to be drawn up for each frame.

Here is the hero character's full animation frames for left, right, and up/down, as well as the mask you are going to use for it.

[Hero.jpg]    [Heromask.jpg]

Sometimes, if the animation allows it, you only need a simple mask. As is the case for the barrel animation, each frame has the same mask; you only need a single mask that you can use for all the animated frames.

[Barrel.jpg]    [Barrelmask.jpg]

Loading the Images to Memory

As was discussed earlier, there is a method to create the image holders and buffers dynamically; this method makes extensive use of APIs. Essentially, you need to create a Windows handle to a memory location, and then allocate an image to that memory location. The only thing you need to remember is to clear and release these handles after you are done. The APIs you are going to be using are CreateCompatibleDC, CreateCompatibleBitmap, SelectObject, DeleteDC, and DeleteObject.

Here is how the APIs are defined.

Public Declare Function CreateCompatibleDC Lib "gdi32" _
   (ByVal hdc As Long) As Long
Public Declare Function CreateCompatibleBitmap Lib "gdi32" _
   (ByVal hdc As Long, _
ByVal nWidth As Long, ByVal nHeight As Long) As Long

Public Declare Function SelectObject Lib "gdi32" _
   (ByVal hdc As Long, ByVal hObject As Long) As Long

Public Declare Function DeleteDC Lib "gdi32" _
   (ByVal hdc As Long) As Long
Public Declare Function DeleteObject Lib "gdi32" _
   (ByVal hObject As Long) As Long

To load a few of the images and create the buffer, you use the following code.

FloorDC = CreateCompatibleDC(0&)
SelectObject FloorDC, LoadPicture(App.Path & "\floor.bmp")
LadderDC = CreateCompatibleDC(0&)
SelectObject LadderDC, LoadPicture(App.Path & "\ladder.bmp")
OilDC = CreateCompatibleDC(0&)
SelectObject OilDC, LoadPicture(App.Path & "\oil.bmp")

Buffer.Bmp = CreateCompatibleBitmap(Screen.hdc, _
   Screen.ScaleWidth, Screen.ScaleHeight)
Buffer.DC = CreateCompatibleDC(0&)
SelectObject Buffer.DC, Buffer.Bmp

And obviously, you have to release these memory resources when finished; else, you have a memory leak. To do this, you use the following code.

DeleteDC FloorDC
DeleteDC LadderDC
DeleteDC OilDC

DeleteDC Buffer.DC
DeleteObject Buffer.Bmp

After looking over the code, you realize that in fact it's not that difficult to create memory blocks to use for your application, and this method also overcomes the 64K limit that VB has.

Once you've loaded all your graphics and set up the buffers, BitBlt can use them exactly the same way as you did with the pictureboxes.

Note: When using the CreateCompatibleBitmap API, the Hdc provided needs to be a DC of a valid PictureBox, because it's used as the compatibility base for the image. Also, the width and height are important; they cannot be altered afterwards.

On the next page, you will look at some of the core code needed to make your Platform game.

Animation in VB (Part 2)

Essentially, until now you've covered many of the key aspects of the animation core, but each individually. Now, you are going to see how to put everything together in a single core animation sub that will handle all of the animation for the game. One important factor to remember here is that the same way that each form has a Z order to decide which form is on top of which, each of your animated images has a Z order. You have to decide in what order to display the images because the first placed images will be behind the last placed images.

It's important for you to work out what order you are going to place the images, before you start coding. The first images to be drawn should be the background and platform; thereafter, other static items that may or may not be animated, and then your animated man and enemies.

In the core of this game, I've included the floor and ladder images into the animation code to allow the animation of the platforms as well. In the original game, the first stage started with the gorilla climbing up ladders and stomping his feet, causing the platforms to move up and down to create the stage.

I, however, used a slightly different animation to show the flexibility of the core code and create the first stage by dropping the platforms one at a time, and sliding the ladders in from the side.

Firstly, set up your project and some of the variables and types that you are going to be using. Your form will require only a single PictureBox and a timer class. Nothing more is actually required because all your images will be loaded dynamically. Again, you set the form's keypreview to true, and use much the same code as before.

You also will be adding a few new types and variables to your declarations. Here they are.

Public Enum Tri_Stat
   neg  = -1
   zero = 0
   pos  = 1
End Enum

Public Type Cords
   X As Long
   Y As Long
End Type

Public Enum Game_Part
   Ani_Test
   Intro
   Start1
   Stage1
End Enum

Public Enum Direction
   up
   down
   Left
   Right
End Enum

Public Type Buffer_Type
   Bmp As Long
   DC As Long
End Type

Public Type AnimDC
   MainDC As Long
   MaskDC As Long
End Type

Public Type Anim_BufDC
   MainDC As Long
   MaskDC As Long
   Buffer As Buffer_Type
End Type

Public Type Sprite_type
   Direc As Direction
   Xmove As Tri_Stat
   Ymove As Tri_Stat
   Locat As Cords
   AniPos As Long
   Ani_Loc As Long
   AniDir As Tri_Stat
   PicSize As Cords
   Visible As Boolean
   Destination As Cords
End Type

Public Xmove As Tri_Stat
Public Ymove As Tri_Stat
Public Location As Cords

' Graphic DC variables
Public Buffer As Buffer_Type
Public HeroDC As Anim_BufDC
Public BarrelDC As AnimDC
Public Barrelbuf() As Buffer_Type
Public FireDC As Anim_BufDC
Public FlameDC As Anim_BufDC
Public FloorBuf() As Buffer_Type
Public LadderBuf() As Buffer_Type
Public FloorDC As Long
Public LadderDC As Long
Public OilDC As Long

Public HeroCords(5, 2) As Cords
Public BarrelCords(7, 1) As Cords
Public FireCords(2) As Cords
Public FlameCords(3) As Cords

As usual, you have your Tristate and Coordinate variables. Most of the declarations are self explanatory, but some do need a little explanation.

The Sprite Type holds all the relevant information for each of our objects, one per object displayed on screen. It holds the current location, facing direction, animation group used, animation image rotation, size, and a few other variables.

You also create a list of coordinates for each of the animated images, to store the location of each frame within the image.

Now, look at the animation core. Below is the Core code that does all of the animation.

If Hero_Loc.Visible Then BitBlt HeroDC.Buffer.DC, 0, 0, _
   Hero_Loc.PicSize.X, Hero_Loc.PicSize.Y, Buffer.DC, _
Hero_Loc.Locat.X, Hero_Loc.Locat.Y, vbSrcCopy
If Fire_Loc.Visible Then BitBlt FireDC.Buffer.DC, 0, 0, _
   Fire_Loc.PicSize.X, Fire_Loc.PicSize.Y, Buffer.DC, _
Fire_Loc.Locat.X, Fire_Loc.Locat.Y, vbSrcCopy
If Flame_Loc.Visible Then BitBlt FlameDC.Buffer.DC, _
0, 0, Flame_Loc.PicSize.X, Flame_Loc.PicSize.Y, Buffer.DC, _
   Flame_Loc.Locat.X, Flame_Loc.Locat.Y, vbSrcCopy
For Loop1 = 0 To 5
   If Barrel_Loc(Loop1).Visible Then BitBlt Barrelbuf(Loop1).DC, _
      0, 0, Barrel_Loc(Loop1).PicSize.X, _
Barrel_Loc(Loop1).PicSize.Y, Buffer.DC, Barrel_Loc(Loop1).Locat.X, _
   Barrel_Loc(Loop1).Locat.Y, vbSrcCopy
Next Loop1
For Loop1 = 0 To UBound(Floor_Loc)
   If Floor_Loc(Loop1).Visible Then BitBlt FloorBuf(Loop1).DC, _
      0, 0, Floor_Loc(Loop1).PicSize.X, _
Floor_Loc(Loop1).PicSize.Y, Buffer.DC, Floor_Loc(Loop1).Locat.X, _
   Floor_Loc(Loop1).Locat.Y, vbSrcCopy
Next Loop1
For Loop1 = 0 To UBound(Ladder_Loc)
   If Ladder_Loc(Loop1).Visible Then BitBlt LadderBuf(Loop1).DC, _
   0, 0, Ladder_Loc(Loop1).PicSize.X, _
Ladder_Loc(Loop1).PicSize.Y, Buffer.DC, Ladder_Loc(Loop1).Locat.X, _
   Ladder_Loc(Loop1).Locat.Y, vbSrcCopy
Next Loop1
' Oil drum does not get buffered because it does not move or get
' animated.

'Place animation on the buffer.

For Loop1 = 0 To UBound(Ladder_Loc)
   If Ladder_Loc(Loop1).Visible Then BitBlt Buffer.DC, _
      Ladder_Loc(Loop1).Locat.X, Ladder_Loc(Loop1).Locat.Y, _
Ladder_Loc(Loop1).PicSize.X, Ladder_Loc(Loop1).PicSize.Y, _
   LadderDC, 0, 0, vbSrcCopy
Next Loop1
For Loop1 = 0 To UBound(Floor_Loc)
   If Floor_Loc(Loop1).Visible Then BitBlt Buffer.DC, _
      Floor_Loc(Loop1).Locat.X, Floor_Loc(Loop1).Locat.Y, _
Floor_Loc(Loop1).PicSize.X, Floor_Loc(Loop1).PicSize.Y, FloorDC, _
   0, 0, vbSrcCopy
Next Loop1
For Loop1 = 0 To 5
   With Barrel_Loc(Loop1)
      If .Visible Then
         BitBlt Buffer.DC, .Locat.X, .Locat.Y, .PicSize.X, _
            .PicSize.Y, BarrelDC.MaskDC, _
BarrelCords(0, .AniPos).X, BarrelCords(0, .AniPos).Y, vbSrcAnd
         BitBlt Buffer.DC, .Locat.X, .Locat.Y, .PicSize.X, _
            .PicSize.Y, BarrelDC.MainDC, _
BarrelCords(TBarlPos(0), .AniPos).X, _
   BarrelCords(TBarlPos(0), .AniPos).Y, vbSrcPaint
      End If
   End With
Next Loop1
With Flame_Loc
   If Flame_Loc.Visible Then
      BitBlt Buffer.DC, .Locat.X, .Locat.Y, .PicSize.X, ._
   PicSize.Y, FlameDC.MaskDC, FlameCords(TFlamPos).X, _
FlameCords(TFlamPos).Y, vbSrcAnd
      BitBlt Buffer.DC, .Locat.X, .Locat.Y, .PicSize.X, _
         .PicSize.Y, FlameDC.MainDC, FlameCords(TFlamPos).X, _
FlameCords(TFlamPos).Y, vbSrcPaint
   End If
End With
If Oil_Loc.Visible Then BitBlt Buffer.DC, Oil_Loc.Locat.X, _
   Oil_Loc.Locat.Y, Oil_Loc.PicSize.X, Oil_Loc.PicSize.Y, _
OilDC, 0, 0, vbSrcCopy
If Fire_Loc.Visible Then
   BitBlt Buffer.DC, Fire_Loc.Locat.X, Fire_Loc.Locat.Y, _
   Fire_Loc.PicSize.X, Fire_Loc.PicSize.Y, FireDC.MaskDC, _
FireCords(TFirePos).X, FireCords(TFirePos).Y, vbSrcAnd
   BitBlt Buffer.DC, Fire_Loc.Locat.X, Fire_Loc.Locat.Y, _
   Fire_Loc.PicSize.X, Fire_Loc.PicSize.Y, FireDC.MainDC, _
FireCords(TFirePos).X, FireCords(TFirePos).Y, vbSrcPaint
End If
With Hero_Loc
   If Hero_Loc.Visible Then
      BitBlt Buffer.DC, .Locat.X, .Locat.Y, .PicSize.X, _
         .PicSize.Y, HeroDC.MaskDC, _
HeroCords(THeroPos(0), .AniPos).X, _
   HeroCords(THeroPos(0), .AniPos).Y, vbSrcAnd
      BitBlt Buffer.DC, .Locat.X, .Locat.Y, .PicSize.X, _
         .PicSize.Y, HeroDC.MainDC, _
HeroCords(THeroPos(0), .AniPos).X, _
   HeroCords(THeroPos(0), .AniPos).Y, vbSrcPaint
   End If
End With

'Place on screen
BitBlt Screen.hdc, 0, 0, Screen.ScaleWidth, Screen.ScaleHeight, _
   Buffer.DC, 0, 0, vbSrcCopy

'revert the buffer to original state
If Hero_Loc.Visible Then BitBlt Buffer.DC, Hero_Loc.Locat.X, _
   Hero_Loc.Locat.Y, Hero_Loc.PicSize.X, _
Hero_Loc.PicSize.Y, HeroDC.Buffer.DC, 0, 0, vbSrcCopy
If Fire_Loc.Visible Then BitBlt Buffer.DC, Fire_Loc.Locat.X, _
   Fire_Loc.Locat.Y, Fire_Loc.PicSize.X, _
Fire_Loc.PicSize.Y, FireDC.Buffer.DC, 0, 0, vbSrcCopy
If Flame_Loc.Visible Then BitBlt Buffer.DC, Flame_Loc.Locat.X, _
   Flame_Loc.Locat.Y, Flame_Loc.PicSize.X, _
Flame_Loc.PicSize.Y, FlameDC.Buffer.DC, 0, 0, vbSrcCopy
For Loop1 = 0 To 5
   If Barrel_Loc(Loop1).Visible Then BitBlt Buffer.DC, _
   Barrel_Loc(Loop1).Locat.X, Barrel_Loc(Loop1).Locat.Y, _
Barrel_Loc(0).PicSize.X, Barrel_Loc(0).PicSize.Y, _
   Barrelbuf(0).DC, 0, 0, vbSrcCopy
Next Loop1
For Loop1 = 0 To UBound(Floor_Loc)
   If Floor_Loc(Loop1).Visible Then BitBlt Buffer.DC, _
      Floor_Loc(Loop1).Locat.X, Floor_Loc(Loop1).Locat.Y, _
Floor_Loc(Loop1).PicSize.X, Floor_Loc(Loop1).PicSize.Y, _
   FloorBuf(Loop1).DC, 0, 0, vbSrcCopy
Next Loop1
For Loop1 = 0 To UBound(Ladder_Loc)
   If Ladder_Loc(Loop1).Visible Then BitBlt Buffer.DC, _
      Ladder_Loc(Loop1).Locat.X, Ladder_Loc(Loop1).Locat.Y, _
Ladder_Loc(Loop1).PicSize.X, Ladder_Loc(Loop1).PicSize.Y, _
   LadderBuf(Loop1).DC, 0, 0, vbSrcCopy
Next Loop1

After looking over this, you will notice that everything is done in steps. First, all the secondary buffers are filled with the original bits of the background, and then all the images are placed in their respective places on the screen buffer. After that, you can display the buffer onscreen and revert the buffer from the secondary buffers.

This, you will notice, is completely different from the core of the Asteroids game you did in Part 1. The main reason for this is the way that you are drawing the images. Had you used the same process, you would pick up a lot of noticeable flicker as the images change and move. The Asteroids game also can use the buffering method to display the graphics although there would be very little difference in the final displaying.

On the final page, you look at how to animate using your animation core code.

Animation in VB (Part 2)

Having set up your graphics, and our core, all that is left is to place items and call the core to animate them. Placing any item on the game field is as simple as setting the location for the object, giving it a direction, and setting its visible property to true. The following code will place the Hero in your game on the game board, and set his movement to walk to the left.

With Hero_Loc
   .Visible = True
   .Locat.X = 500
   .Locat.Y = 190
   .Xmove   = neg
   .Ymove   = zero
End With

Any of your objects can be placed using the same method, but your animation needs a little extra. When a movement direction is chosen, you need to choose an appropriate image and animation direction. The animation direction is basically this: Should the animated frames be played forwards or backwards? So, to handle all of this, you create a short simple function as follows.

Public Sub Hero_Ani_Line(ByVal X_move As Tri_Stat, _
   ByVal Y_Move As Tri_Stat, _
ByRef Direct As Direction,  ByRef Ani_Pos As Long, _
   ByRef Ani_Dir As Tri_Stat, ByRef P_Size As Cords)
   Select Case Y_Move
      Case pos
         Direct = down
      Case neg
         Direct = up
   End Select
   Select Case X_move
      Case pos
         Direct = Right
      Case neg
         Direct = Left
   End Select

   Select Case Direct
      Case Left
         Ani_Pos = 1
         Ani_Dir = pos
      Case Right
         Ani_Pos = 0
         Ani_Dir = pos
      Case up
         Ani_Pos = 2
         Ani_Dir = pos
      Case down
         Ani_Pos = 2
         Ani_Dir = neg
   End Select
P_Size.X = 30
P_Size.Y = 50
End Sub

In this Sub, you look at what direction the hero is moving in, if there's vertical and horizontal movement you use the horizontal animation, because the animation used for vertical does not animate well with horizontal movement. After deciding which direction the animation will be following, you set the relevant variables to select the correct set of frames, and set the correct direction. You also set the image sizes here because some animations have different sizes depending on the direction of movement.

After placing and getting animation info about the object, you need to check that your object does not exceed certain boundaries, or walk off the floor. For the Intro animation on the game, you simply move the hero left and right on a straight floor, so you only check to make sure he does not pass over these limits.

With Hero_Loc
   If .Visible Then
      Hero_Ani_Line .Xmove, .Ymove, .Direc, .AniPos, _
         .AniDir, .PicSize
      If .Locat.X <= 0 Then .Xmove = pos
      If .Locat.X > 565 Then .Xmove = neg
      .Locat.X = .Locat.X + (.Xmove * 2)
      .Ani_Loc = .Ani_Loc + .AniDir
      If .Ani_Loc > 5 Then .Ani_Loc = 0
      If .Ani_Loc < 0 Then .Ani_Loc = 5
   End If
End With

In this code snip, you will notice that you call the sub that works out the animation images and direction. You check the limits of the hero and change the movement direction accordingly. And then, you step the animation frame.

When it comes to moving the object according to user input, you have to take several items into consideration. You need to check whether the object is standing on a floor tile, or in front of a ladder tile. You also need to adjust the vertical movement independently to user input in the case of the Hero object no longer standing on top of a floor tile.

Suggested Additions to the Game

There are several other items that are needed to complete the game; however, they do not affect the animation. The Donkey Kong project download below has everything discussed in these pages, and a bit more. It will give you a very good indication on how the animation core works.

The Game, however, is not complete. I've animated the Barrels, Burning barrel, and the Hero. Animation still needs to be drawn up for the Maiden and the Gorilla that stand at the top of each stage. I've also only drawn up and animated the first stage of the game to show how easy it is to do.

Collision detection has not been done; however, it is just as simple as the code used on the Asteroids game from Part 1. The only thing to remember here is that the X and Y locations are for the top left corner and not the center of the object. You will have to use half the picwidth values to find the centers of the objects. Then, use the mathematics from Part 1 to find the distance between the objects. Another method to do collision detection is simply to check whether the objects overlap each other, with a little leniency to allow for the blank areas around the objects.

Another item that has not been included is scoring. You can add a second timer that is enabled when the stage starts, and counts down the bonus score. See The basics of Timers for how to include this. Also, in the collision detection you can include code to add to the score every time the hero jumps over a barrel.

Conclusion

The animation of bitmap images actually requires only a single API, BitBlt. Several other APIs can be used to make the process a little cleaner, and reduce memory usage. All that is required to successfully animate in this way is a well setup image and mask, and a vivid imagination.

Here are few guidelines to follow when setting up your animation frames.

Keep all the frames the same size. As you will have noticed, all the frames for the hero are the same size, 30 * 50 pixels. However, the frames for the barrels follow two different sizes, 30 * 30 and 30 * 40 pixels. In some cases this is necessary, but the frames within a single line of animation are the same size.

Keep the difference between frames down to a minimum. If the movement or change is too drastic, the movement/motion effect is lost and the image switching is very clearly visible.

Next time, you will be looking at doing some 3D vectors, with rotation and movement.



About the Author

Richard Newcombe

Richard Newcombe has been involved in computers since the time of the Commodore 64. Today, he has excelled in programming, and designs. Richard is in his mid 30's and, if or when you looking for him look no further than his computer. Always willing to help and give advice where he can in regard to computer related subjects. At present he is working as a .NET 2008 Software Developer for Syncrony Web Services, South Africa.

Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Java developers know that testing code changes can be a huge pain, and waiting for an application to redeploy after a code fix can take an eternity. Wouldn't it be great if you could see your code changes immediately, fine-tune, debug, explore and deploy code without waiting for ages? In this white paper, find out how that's possible with a Java plugin that drastically changes the way you develop, test and run Java applications. Discover the advantages of this plugin, and the changes you can expect to see …

  • Savvy enterprises are discovering that the cloud holds the power to transform IT processes and support business objectives. IT departments can use the cloud to redefine the continuum of development and operations—a process that is becoming known as DevOps. Download the Executive Brief DevOps: Why IT Operations Managers Should Care About the Cloud—prepared by Frost & Sullivan and sponsored by IBM—to learn how IBM SmartCloud Application services provide a robust platform that streamlines …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds