Create Your Own Animated Sprite Screen Saver

Its easy to create a screen saver with Visual Basic. All you need is a few forms and a little imagination. Here I have created a screen saver that displays several sprites which move around the screen glancing off its edges and each other.

Once you have a Windows screen saver enabled, your Visual Basic application can Instruct Windows to Run the Screen Saver.

Also discussed is how to work with Animated Sprites. This part can get a little tricky and uses memory device contexts and many of Window's GDI functions such as BitBlk (Bit-Block Transfer), StretchBlt, CreateCompatibleDC, CreateCompatibleBitmap, etc. As a side effect, this program shows how to create transparent bitmaps. You may want to see my Create a Transparent Bitmap sample for a more thorough discussion of that topic.

First I will discuss screen saver basics then I will talk about sprites.

Create your own animated sprite screen saver with Visual Basic.
Download Source Code

Applies To

Note: This is still a VB executable and requires the Visual Basic runtime files. To use your screen saver on another PC, create installation disks and install it on the target PC.

The screen saver program and its executable (.Scr file) were developed in VB6. If you have an earlier version of VB, just open the program and recompile it.

What's Needed for a Screen Saver
  • A Main form to act as the screen saver itself -- let your imagination be your guide.
  • A Configuration or settings form to let the user customize your screen saver. The configuration form must save its settings either in an .Ini file or the registry. I used the registry since you need to read a value from there anyway.
  • A Change Password form. Windows screen savers use ScrnSave.Lib to manage passwords. However, this is unavailable to VB. You will need to display your own dialog and save the password somewhere. Again, I use the registry. (I used my Registry.bas module to read and write the registry. It contains all the necessary functionality).
  • A Password Entry dialog. When the user attempts to disable the screen saver you need to prompt them for a password.

I modeled all my password forms and message boxes to look exactly like Windows. Right down to their size, shape and message text.

Screen Saver Discussion

Here are the basics of what your screen saver should do. See the source code for details.

Your Main form should have the following properties:

BorderStyle

None

Caption

""

ControlBox

False

Icon

None

ShowInTaskBar

False

WindowState

Maximized

The MouseDown, MouseMove, Click, Double Click, KeyPress
and KeyDown events must End the program.

Because the MouseMove event fires when the form is maximized, you must write code to handle this. Otherwise on the initial load your screen saver will exit. For example:

 Private Sub Form_MouseMove(Button As Integer,
       Shift As Integer, x As Single, Y As Single)

   Static iCount As Long

   If iCount > 2 Then
      End
   Else
      iCount = iCount + 1
   End If

   End Sub

Use Sub Main to start your program.
Since when previewing your screen saver you must Load your main form then make it a child of the Preview window prior to showing it.

Parse command line parameters.
Windows passes command switches to the screen saver to tell it what to do. The following switches are used:

When You:

Windows Passes:

You Should:

Select a screen saver from the drop down

/p <hwnd>

Run your screen saver in the Preview window.

Click the Preview button

/s

Run your screen saver normally.

Stop previewing the screen saver

/p <hwnd>

Run your screen saver in the Preview window.

Click the Settings button

/c:<hwnd>

Show your configuration dialog.

Close your Configuration form

/p <hwnd>

Run your screen saver in the Preview window.

Click the Change Password button

/a <hwnd>

Show your change password screen.

Click the Apply button

/p <hwnd>

Run your screen saver in the Preview window.

Pick a screen saver and leave the PC idle

/s

Run your screen saver normally.

Where <hwnd> is the handle of the Preview window. The Preview window is the small window on the Screen Saver tab of the Display Properties applet.

Run your screen saver in the small PreviewWindow.
Here is the cool part. When Windows sends a "/p<hwnd>" to your screen saver it needs to run in the Preview window. To do this you must know the preview window's size. Calling GetClientRect with the Preview window's handle (the <hwnd> value Windows passed you) populates a RECT structure with the window's dimensions.

You then have to set the Preview window to be the parent of your main form. A call to SetWindowLong with the GWL_STYLE command retrieves your form's current window style. This must be done after you Load your form. By OR-ing the style with the WS_CHILD flag and calling SetWindowLong with the new style your form is converted a child window.

Now you can set its parent with SetParent passing it the handle of the Preview window. One last call to SetWindowLong with the GWL_HWNDPARENT flag fills your form's window structure with the handle of the Preview window. Your form is now a child of the Preview window.

To show your form, call SetWindowPos passing it the dimensions of the Preview window. When your screen saver is displayed, it will appear within the boundaries of the Preview window.

Windows will automatically shut down your program when you close the Display Properties dialog or select another screen saver.

Prevent multiple instances of your screen saver - sometimes.
When your PC is idle for the specified time period, Windows launches the screen saver continually passing it the "/s" switch. Your program must check to see if it is already running and, if so, terminate.

When the Display Properties dialog is shown and you click the Preview button, Windows also starts your screen saver with the "/s" switch. This time, however, an instance of your screen saver will already be running in the Preview Window. You need a second instance to run full screen. A call to FindWindow looking for a window with the title "Display Properties" will distinguish between these two scenarios.

   Public Sub Main()
      . . .
      sOption = Left$(Command, 2)
      lHwnd = FindWindow(vbNullString, "Display Properties")
      
      If App.PrevInstance And sOption = "/s" And lHwnd = 0 Then
         End
      Else
         frmMain.show
      End If
   End Sub

Read the registry to see if password protection is enabled.
The HKCU\Control Panel\Desktop\ScreenSaveUsePassword value indicates if the screen saver is password protected. You can use my Registry.bas module to read and write the registry.

Display the Password form modally.
So users are forced to enter a password.

About Passwords.....

Screen savers written in C use the SCRNSAV.lib to process passwords. You cannot call this from VB. However on Windows 9x you can use 2 undocumented functions:

Declare Sub PwdChangePassword Lib "mpr.dll" Alias "PwdChangePasswordA" _
  (ByVal lpcRegkeyname As String, ByVal hwnd As Long, ByVal uiReserved _
  As Long)

Declare Function VerifyScreenSavePwd Lib "password.cpl" (ByVal hwnd As Long) As Boolean

To change the password:

Private Sub cmdChange_Click()
   Call PwdChangePassword("SCRSAVE", Me.hwnd, 0, 0)
End Sub

Call VerifyScreenSavePwd on mouse or keyboard activity:

Private Sub cmdTest_Click()
    Dim bRes As Boolean

    bRes = VerifyScreenSavePwd (Me.hwnd)
    MsgBox bRes
End Sub

Disable Ctl-Alt-Delete and Alt-Tab task switching.
So users cannot switch to another application or kill the screen saver if it is password protected.

   Call SystemParametersInfo(SPI_SCREENSAVERRUNNING, True, lPrev, 0)

Make your screen saver always on top.
Set your main form to be always on top using:

   Call SetWindowPos(frmMain.hWnd, HWND_TOPMOST, 0&, 0&, 0, 0, SWP_NOSIZE)

Hide the cursor.
Using the ShowCursor API so when the screen saver is displayed you do not see it.

WARNING:
Disabling Ctl-Alt-Delete, making your screen saver the top most window which covers the entire desktop and hiding the cursor makes debugging impossible. If you lock up during development you will not be able to kill your program with out pressing the reset button on your PC! I recommend commenting out these features during development.

Set the Title of your application.
Go to Project | Properties | Make. Make sure the title begins with SCRNSAVE: (the colon is required). For example, I used SCRNSAVE:TheScarms.

Compile your application with an extension of .SCR.
Control Panel looks in the Windows and Windows\System folders for *.SCR. It also checks the header of these files for the SCRNSAVE: code.

Copy your .Scr file to the Windows folder.

Starting a Screen Saver from Visual Basic

You can start a screen saver from VB application (provided, of course, you have a screen saver enabled).

Const WM_SYSCOMMAND = &H112&
Const SC_SCREENSAVE = &HF140&

Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
(ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, _
ByVal lParam As Long) As Long

Call SendMessage(frmMain.hWnd, WM_SYSCOMMAND, SC_SCREENSAVE, 0&)

What is an Animated Sprite?

A Sprite is a picture with an irregular shape, possibly with transparent holes in it, that can be moved left, right, up, and down on the screen, and that has depth, which is called z-order. So a sprite is a picture with x, y, and z coordinates. 

A sprite can be a set of images combined vertically or horizontally into a single bitmap. You can then load the image and tell the sprite how many frames or phases to divide the image into. As the sprite moves, it changes the current phase of the image. In this way, you can create moving images such as the animated Gifs so popular on the web.

When moving a sprite, the location just vacated by the sprite is typically reset back to the original image. As an added effect, this location can be left to display the old sprite. The result is that the sprite leaves a trail or tracer behind it showing the path it moved along.

Sprite Images.

The images for my sprite are stored as one bitmap. I created the individual images and pasted them together vertically into one bitmap file. Try to keep your bitmap small by using as few colors as you can and storing it at a low resolution.

Once the bitmap is created it is stored in a Resource (.Res) file. The Resource Editor is available as a Visual Basic 6.0 Add-In. Under earlier versions of VB you have to use the resource compiler.

To create multiple copies of the sprite, it is implemented as a class. Then, it can be instantiated as many times as desired. Using properties of the class for the sprite's characteristics means each sprite is self contained and can take on a different size, move with a different speed, etc.

Sprites, or their handles, are stored in an array. Each sprite can then be positioned by looping through the array and calling the sprite's move method. A timer is used to continually invoke the sprites' move method. In an ideal world, each sprite would reside on its own thread and there would be another thread to handle the screen repaints...

Customizing Sprites.

The screen saver's configuration screen allows the user to select the number of sprites, their size, speed, timer interval (refresh rate) and whether or not to use tracers.

Displaying the Sprites.

The first thing the screen saver does is capture the current desktop and store it in memory as a bitmap. Your sprite is then painted on this bitmap and the bitmap is redisplayed on your main form. Your main form is sized either to cover the entire desktop or to fit within the preview window. In the later case, the desktop bitmap and your sprite must be resized accordingly. Thus, we need a method to resize bitmaps.

When the timer fires your sprite is moved according the algorithm you choose. Moving the sprite means determining its current position, calculating its new position, getting the next sprite frame, drawing the sprite in the new location on the bitmap, restoring the area the sprite moved from to the original desktop image, and repainting the bitmap on your form.

As the number of sprites increases, more processing is required. In this sample I allow each sprite to have a different size, speed, and refresh rate. As you probably guessed, this must be done as efficiently as possible if you want any semblance of animation. As a result, the Window's GDI (Graphic Device Interface) functions are used extensively.

Details

This section talks about each of the main routines used to manipulate and display the sprites. See the source code for more details. Here are the .bas module routines.

pInitDesktop

This procedure creates and returns a bitmap that looks like the current desktop but that is stretched or compressed to a pre-defined width and height as specified by its input parameter. The steps involved are:

  1. Get a handle to the desktop window using the GetDesktopWindow API. Use this handle to get the desktop window's Device Context (DC) via GetWindowDC and write the window's dimension into a RECT structure using GetWindowRect.
  2. Using CreateCompatibleBitmap, a desktop window compatible bitmap is created based on the desktop window's device context and dimensions.
  3. Fill the output bitmap's structure with the width, height and color information of the newly created compatible bitmap with a call to the GetObject API function.
  4. Create a memory device context compatible with the desktop window DC with a call to CreateCompatibleDC. The memory DC's display surface is one monochrome pixel wide and one monochrome pixel high.
  5. The desktop compatible bitmap is copied into the new memory DC using SelectObject.
  6. Finally, using the StretchBlt function the desktop compatible bitmap is copied to the output DC stretching or compressing it as required by the specified dimensions.

pDrawTransparentBitmap

This procedure copies the foreground image of the sprite onto a bitmap representing the desktop. The background of the sprite is removed allowing the underlying desktop image to show through. Basically it masks out the background of the sprite, masks out the portion of the bitmap where the sprite's foreground will be placed, and merges the two images together. The heart of this routine is the BitBlk (Bit-Block transfer) function. For a more detailed explanation of this routine see the source code or my Create a Transparent Bitmap sample.

fShrinkBmp

Shrink bitmap takes a bitmap and scales it vertically and horizontally and returns a handle to the new smaller bitmap. 

It does this by creating two memory device contexts, compatible with the DC of the original bitmap, via CreateCompatibleDC. Attributes of the bitmap are retrieved using the GetObject function and stored in a Bitmap structure. The structure is copied to a second structure whose the bitmap dimensions are modified according to the x and y percentages passed in. Based on this modified information, a new bitmap is created by calling the CreateBitmapIndirect API.

The new and original bitmaps are selected into the memory DCs with calls to SelectObject. Using handles to these DCs, the StretchBlt API copies the original bitmap to the new bitmap compressing it to its new dimensions.

Sprite Class Methods

CreateSprite

As its name implies, this method creates a new sprite, initializes its properties and returns its handle. For easy access the characteristics of the sprite are implemented as properties (well, public variables of the class). 

The CreateSprite method performs three main functions:

  1. Loads the bitmap image from the resource file if it is not already loaded.
  2. Scales the sprite to the proper dimensions via a call to fShrinkBmp. The dimensions to scale the sprite to are calculated based on the sprite size option selected by the user on the configuration screen.
  3. Calculates the width, height, number of vertical and horizontal frames, first frame to display and other properties of the sprite.


CollisionTest

This method examines the coordinates of two sprites to see if they overlap and returns true if they do.

ResolveCollision

Called when 2 sprites collide, this method moves them until the collision is resolved. The logic to determine how to move the sprites relies on a bit of physics.

UpdatePosition

Updates the x and y position of a sprite reversing its direction when it hits a border.

DrawNext

This routine performs the bulk of the work when moving a sprite to a new location. It first gets the location of the next sprite frame to display from the bitmap. Then it draws the sprite frame in the new location via a call to pDrawTransparentBitmap.

If the user did not select the "Use Tracers" option, DrawNext calculates the area just vacated by the sprite. This area must be reset to the image originally displayed in that location. Again, this is done with a call to pDrawTransparentBitmap. This time passing it the handle to the device context of the original image.

AutoMove

This method calls DrawNext to draw the sprite in a new position. It then calls ResolveCollision to see if the sprite has collided with another sprite and subsequently calls UpdatePosition if it has.

Instructions

Download the sample and copy the .SCR file to your Windows folder. Right click on the desktop, select properties, pick the screen saver tab and choose TheScarms Screen Saver from the screen saver drop down.

Click the Preview button to see a demo. Click Settings to display the configuration dialog. Finally, check the Password Protected box and click on Change to bring up the change password dialog.




About TheScarms
About TheScarms


Sample code
version info

If you use this code, please mention "www.TheScarms.com"

Email this page


© Copyright 2024 TheScarms
Goto top of page