VB Advanced Chapter 1
by David McNally
What I would like to do in this series is focus on VB animation techniques and some very interesting projects you can do using VB and Windows API functions. I will need to assume you have a basic understanding of using Windows APIs as I will not get into details about how they work unless I think they might warrant a further explanation.
I will also assume you know your way around VB enough to be able to create forms, modules, etc. and set up basic controls within them if needed.
Our first discussion will focus on the framework I will use to create most of the animations. The framework takes into account that it is a simple structure to implement, flicker less animations, frame rate control, and overall processing speed.
To begin, create a new project in VB 6. Add two forms and a module to your project. Please make sure to make form1 your startup object form in the project properties.
Now add the following code to the modules general declaration section:
' Declare some Windows functions used in the demo
' These functions help increase the speed since VB
' is pretty slow when dealing with graphics
Public Declare Function SetPixel Lib "gdi32" (ByVal hdc As Long, _
ByVal x As Long, ByVal y As Long, _
ByVal crColor As Long) As Long
Public Declare Function BitBlt Lib "gdi32" (ByVal hDestDC As Long, _
ByVal x As Long, ByVal y As Long, _
ByVal nWidth As Long, ByVal nHeight As Long, _
ByVal hSrcDC As Long, ByVal xSrc As Long, _
ByVal ySrc As Long, ByVal dwRop As Long) As Long
'Constants used by the BitBlt Function
Public Const SRCAND = &H8800C6
Public Const SRCCOPY = &HCC0020
Public Const SRCERASE = &H440328
Public Const SRCINVERT = &H660046
Public Const SRCPAINT = &HEE0086
*Note that there is one space in front of each underscore character that is at the ends of each line.
Almost all of the projects I have worked on use the above two functions. The setpixel API has a little bit more speed then the VB pixel command. The BitBlt function is known to be one of the fastest pixel copy routines Windows has.
The idea for most of the animations is simple. Set up a second invisible page (also known as a back buffer) where all your erasing and drawing will take place. Then, use the Bitblt function to copy the results to a second form, which is visible to the user. This will reduce the "flicker" effect and also let you do other more complicated behind the scenes work (such as masking) without being seen. Only the final result of the frame is actually viewed by the audience. You can actually set up an invisible buffer in memory (without using a second form), but this requires a bit more work and will be the topic of a later article.
Now lets look at the two forms we need. Set the properties on the two forms as follows:
Form 1 |
Form 2 |
|
| Clip Control | false | false |
| Control Box | false | false |
| Caption | Erase this field no text | Erase this field no text |
| Scale mode | 3 - pixel | 3 - pixel |
| Show In Taskbar | true | false |
| Auto Redraw | false | true |
Now add three buttons across the bottom of form1. Label them "Start", "Stop", and "Quit" respectively.
Also, you will need to add a timer control to form1 with an interval of 50. The timer control is what keeps the animation rate constant and "on beat" if you will. When the timer is enabled, the interval counts down to zero. When zero is reached, the timers timer event gets triggered. It is here will instruct the computer to prepare the next frame of animation.
Form2 does not need any code, so just add the following code in form1:
' This is in our general declarations section
'
' set a flag to determine if the animation is currently
in progress
Dim animation As Boolean
Private Sub Command1_Click()
' This button press will be used to start the animation process.
' All we need to do is turn on the timer. When the time
' interval has elapsed, the Timer1_Timer function is called.
' As long as the timer is turned on, the animation will
' continue on.
Timer1.Enabled = True
End Sub
Private Sub Command2_Click()
' To stop the animation, just disable the timer control
Timer1.Enabled = False
End Sub
Private Sub Command3_Click()
' Quit the demo
End
End Sub
Private Sub Form_Load()
' The trick here is to make a second form that is
' the same as the first form, but invisible.
'
' That way, we can draw and erase on it without anyone
' seeing what is going on. This is referred to as a
' back buffer.
Form2.ScaleWidth = Form1.ScaleWidth
Form2.ScaleHeight = Form1.ScaleHeight - 50
Form2.BackColor = vbBlack
Form2.ForeColor = vbRed
Form2.Visible = False
'setup random number generator
Randomize
End Sub
Private Sub Timer1_Timer()
' This is where most of the animation work is done.
' A timer provides a way to create a constant frame rate
' within the animation
'
' Since the timer event could fire again before we
' have finished the previous frame, we will set the
' animation flag to true until the new old is done. This
' allows us to skip frames if needed.
If animation = False Then
animation = True
Call Update_Frame
Call Update_Screen
animation = False
End If
End Sub
Private Sub Update_Frame()
' Here is where you add the actual drawing and erasing of
' the new scene. Notice all work is done on the invisible
' back buffer (in this case form2).
'
' For this example, I will plot a bunch of random dots
' using the set_pixel API I declared in the module.
Dim dmy As Long
Dim x, y, c As Long
'clear the old frame if needed
Form2.Cls
'randomly plot some pixels
c = vbRed
For t% = 1 To 200
x = Int(Rnd(1) * Form2.ScaleWidth)
y = Int(Rnd(1) * Form2.ScaleHeight)
dmy = SetPixel(Form2.hdc, x, y, c)
Next t%
End Sub
Private Sub Update_Screen()
' Here is where we copy the invisible back buffer (which
' is now done being redrawn) to the visible page. The
' BitBlt API function is the fastest way to do this.
Dim dmy As Long
dmy = BitBlt(Form1.hdc, 0, 0, Form2.Width, Form2.ScaleHeight - 50, Form2.hdc, 0, 0, SRCCOPY)
End Sub
I have commented the routines well, so you should be able to see what is going on here. I did subtract 50 pixels from the calculations to leave room for the three buttons on the bottom of the form. We will remove those in later animations, but for now it makes it a little easier to demonstrate how the timer events play the important roll of stopping, starting, and controlling frame rate.
Also notice the flag I added into the timer routine. While this has little use now, it helps prevent page tearing and incomplete scenes when working with larger animation projects, so it is a good habit to get into. The idea here is that if another timer event fires before the prior drawing routines have completed, we just skip the event, and wait for the next one. This may be more the case with slower machines.
One last thing to note here is the use of the Auto redraw=true on the second form. While not apparent in this example, this will later allow us to use the persistent bitmap feature in VB to refresh background images for us.
If you want to experiment with this animation framework, all you need to do is add all your drawing commands with the Update_Frame subroutine. I just drew a bunch of random dots to keep things simple and to focus more on the structure and setup of the code. For speed, you may need to experiment with more API functions as well.
< Sample Code >