Writing code to move things around on screen always seems more
difficult than it should be. Windows creates the illusion that
displayed objects are layered with closer things sitting on top of
more distant things. We "grab" and "drag" on-screen
objects every day. Objectively, we know that there is only one layer of
pixels on the screen and making something "move" around must
require some
behind the scene tricks. And that's the topic of this page.
I do not claim much expertise in this area. I do like to experiment and animation provides lots of opportunities for that.
Making objects move on the screen is similar to making one of those "Claymation" movies created by taking a series of still pictures, moving the figure a small amount between shots. All of the techniques tried here use the same loop for the animation:
The programs below include some ellipses and letters that float randomly around the screen. I don't have an "official" definition for the term "sprite" - they definitely move about the screen and are usually non-rectangular. Must they include multiple images to imitate some internal motion (walking, flying, etc.)? Not sure. Just to be safe, I've included a "walking man" sprite that uses six different images to give the illusion that his legs are moving.
If we try drawing sprites without using masking, the visual aspect of all of your sprites had better be rectangles. Windows drawing routines only know rectangles, so the trick is to let the background show through those areas of of our rectangular sprite that are not supposed to be there. Masking is the technique that accomplishes this. Delphi has simplified masking by including the mechanics into several of it's visual controls, so if you find a Draw method and a Transparent Color property, you're in business. I may do a "behind the scenes look" at this magic trick one of these days. (Sept 11: Done! See Masking Unmasked! for more information.)
In these programs I tried combinations of several variables to draw each frame:
There are five source programs are available below for downloading. They are all similar with variations of the parameters described above.
I measured the maximum measured frame rates for the five programs with 2 and 14 sprites displayed and on three of my computers. Results are shown in the table below. Upstairs is an 800mhz Celeron system with a 32mb AGP video car driving a monitor at 1024X768 resolution. Downstairs is a 433mhz Celeron with a 2X AGP video card and driving 800X600 resolution, Basement is a 300mhz AMD K6 with a S3 PCI video card driving a 100 year old 640X480 Dell VGA monitor. The two numbers in each box are the maximum observed frames per second with 2 sprites and 14 sprites running.
| Sprites1 | Sprites2 | Sprites3 | Sprites4 | Sprites5 | |
|---|---|---|---|---|---|
| Upstairs | 60,54 | 212,103 | 256,166 | 258,166 | 1662,322 |
| Downstairs | 68,45 | 94,77 | 130,77 | 148,88 | 682,114 |
| Basement | 67,34 | 245,48 | 222,91 | 324,108 | 518,70 |
Frames per second for several programs and systems
(2 sprites, 14 sprites)
While taking these measurements, I noticed a few anomalies that deserve more study. The Aopen PA3000 video card driver on Upstairs seems not very well behaved for some operations, Sprites3 and Sprites4 both start out with very high calculated frame rates even though the sprites are not moving exceptionally fast, as if the card is caching the requests in its own memory and returning to the program before the operation is complete. Once that cache is full, the program becomes very non-responsive to button clicks (until the cache catches up I assume). Further information - the problem is related to the pixel format of the background image. When loaded from the resource file, the bitmap type is DDB (device dependent bitmap) and the Pixelformat property is set to pfDevice. When I change Pixelformat to the current screen Pixelformat, the response problems disappear, but frame rates are somewhat slower. Opting for usability over speed, I am setting Pixelformat to match the screen color depth in all five programs. Comment out the statement bg.pixelformat:=pixelformat;; in the FormCreate method to see the effect on your system.
Also, on the old Basement system, the full redraw TPaintBox program, (Sprites4) outruns partial redraw TPaintBox program (Sprites5) for the 14 sprites case. This probably reflects the relative power of the VGA card in drawing over the AMD processor in recalculating which pieces to redraw.
There are a number of other techniques used here that I may want to reference one day. I'll mention them here so that Google can index them for me.
Adding bitmaps to a resource and loading bitmaps from a resource,
Using OnResize exit to adjust displayed image sizes,
Controlling the speed of an animated sprite. The walking man moves much too fast if images are changed for each frame, I defined a ManRest variable to control how many frames pass before the man image changes.
Getting the bits per pixel for the current display screen using GetDC and GetDeviceContext API calls, (See demo program Sprites3)
Use of QueryPerformanceCounter and QueryPerformanceFrequency API procedures to calculate frame rates.
The Tag property is used as a "Stop" flag to tell the animation to quit.
Using OnCloseQuery exit to set the "Stop" flag to allow graceful program exits when the animation is running.
Using the Sleep procedure to control animation speed.
Saving a bitmap image to disk with a unique name.
(Commented in Sprites1 after use to make the image at the top of this page.)
image1.picture.bitmap.savetofile(extractfilepath(application.exename)
+ 'Spritepic'+ inttostr(trunc(now*secsperday)) + '.bmp');
Download source code for programs Sprites1 - Sprites5
Download bitmaps and files to build a resource. (Not required to compile or run the programs above. The image resource file, Sprites.res, is included with the source download.)
|
I included a separate speed control for the little man and spent too much time trying to keep him from getting hit by the flying sprites. It could a good first game. With a little more work, the guy could back up or jump. And we would need some collision detection. Difficulty levels could be controlled by the number and speed of the "attack" sprites. Scoring could be based on time between hits (plus points) and hits (minus points). |
|
|
The letters included spell a word - it would be cool to have the letters gradually come to a halt forming the word across the center of the display after a minute or so. |
| Created: September 8, 2002 |
Modified: November 07, 2008 |