Drawing & Animation
Using the Win32 GDI #2
This three part tutorial first appeared some years ago on the old VBExplorer.com . Since then several errors and bugs have been discovered by various users on the VBExplorer.com Forums. This version reflects the changes made to overcome the bugs and erros. The text will note where a bug correction and / or update has been done, and of course also why. A big ''''Thank you'''' goes out to all the people reading and reporting the bugs in the previous version of this tutorial. Please provide comments, questions, bug reports etc. at the VBExplorer.com forums in the Graphics & Game Programming section.
Backbuffering, AutoRedraw and Refresh
In the previous section we used the AutoRedraw property and the Refresh function to force a copy of the form to be stored in memory and then updated. The same thing can of course be accomplished on a picture box.
There is also an alternative to this scheme, namely BackBuffering. Backbuffering is a simple technique, where you keep a 慶opy?of the gaming field (display area) in a non-visible area. All the sprites and other drawings are drawn into the backbuffer, which is then drawn all at once onto the display area.
If we could view the process it would look something like this:
Step 1: Draw all the masks and sprites onto the non-visible back buffer
Step 2: Draw everything in the back buffer to the visible area
So why does this produce flickerless animation? The main reason is that we only use one Blit operation to draw everything we need from the backbuffer to the front display area. This disables all the intermediate updates, which might occur between each blit operation, and thus produces a clean drawing. Get it? Basically you are doing all the work with the sprites and masks off screen, where the game player doesn''''t see it and blitting to the play area once. The other way all the operations take place in the visible play area.
Which method is then the best method? Well, it depends on your type of application and the design you have made. The sample project BACKBUF found in BACKBUF.ZIP demonstrates both methods and times the amount of time elapsed. We tested both of the methods with this sample project and with some additional projects. All the empirical data from the tests conclusively demonstrate that neither method is faster. The best would of course then be the AutoRedraw ?Refresh method, since it does not use the extra picture box to store the backbuffer in. But it also depends on the size of the drawing area you have. If it is big, then the Refresh ?Autoredraw method -might appear slightly slower than the backbuffering scheme. So the choice is really up to you, test your game with both schemes and use the one you like best.
Sprite Animations and StretchBlt
Now we know how to move a sprite around the window, but usually that is not enough to form a complete game. Sometimes there is also a need to change the actual sprite image, in order to accommodate certain conditions of a game.
The actual implement of such a scenario is actually quite simple, it is just a matter of changing the actual sprite picture. So if we had a need of a small ball Note: Will change this to a small animated character instead rotating around the screen, we would simply make each rotation in a drawing program and draw each in a specified order. Very much like a little cartoon animation block.
In the sample project ANIMATION contained in ANIMATION.ZIP we will do just that, create a ball with shifting colors. The first thing to note is that so far we have used a picture box to hold each separate bitmap. If we were to do that for each sprite in a game with several animated sprites, we would be using up quite a number of picture boxes. So instead, we will draw all the animation frames of the sprite in just one bitmap, and only draw the part of this bitmap that we need. The bitmap would look like this:
Notice the apparent black sprite, which actually represents a black circle.
We already know that the sprite is 64 pixel wide and 64 pixels high (this was of course preset when the sprites were made). We also know that we need to feed the BitBlt function with information on only the upper left corner of the sprite, and the dimensions of the sprite. So it is just a simple matter of moving this upper left point from each frame to the other. Since the image only has one row of sprites, the Y position will be constant (always 0). So the only really challenging thing is to move the X position a given distance in pixels with each new frame we blit. To accomplish we must keep an eye out for the current frame, which is to be displayed. Using a simple variable as a frame counter does this. This frame counter is updated every time the frame is changed, so it will always have a value equal to the current frame. If we also use this variable as a multiplier to the constant width of the sprite, we actually get exactly what we want.
As you can see from the illustration, then the upper-left X position of a frame is equal to the FrameNumber ?1 multiplied by the width of the sprite. So by using this scheme we move the X position by an amount which is equal to the width of the sprite, on each update of the frame.
FrameNumber = (FrameNumber Mod MaxFrames) + 1
This actually ensures that the FrameNumber variable will never be more than MaxFrames and never less than 1. The method also produces a problem, since when we use it to get the X positions we will get values in the range from 64 (Frame 1) to 640 (Frame 10). These values represent the rightmost X position of the sprites, which is of no use to the BitBlt function. The solution is of course to simply subtract 1 from the FrameNumber when we calculate the X position, and thereby getting X values in the range of 0 to 576, or all the leftmost positions of the sprite frames.
The same thing could be employed to keep the sprite from moving out of bounds of the window:
X = (X Mod Me.ScaleWidth) + 1
Y = (Y Mod Me.ScaleHeight) + 1
So now that we have all this wonderful background information, it should be simple to implement in a timer event and draw the thing:
Private Sub TimerAnimation_Timer()
Static X As Long Static Y As Long
''''Clear the form, since we do not have a background Me.Cls
''''Draw the mask BitBlt Me.hDC, X, Y, SpriteWidth, SpriteHeight, picMask.hDC, _ (FrameNumber - 1) * SpriteWidth, 0, vbSrcAnd
''''Draw the sprite BitBlt Me.hDC, X, Y, SpriteWidth, SpriteHeight, picSprite.hDC, _ (FrameNumber - 1) * SpriteWidth, 0, vbSrcPaint
''''Update frame number FrameNumber = (FrameNumber Mod MaxFrames) + 1
''''Update drawing positions X = (X Mod Me.ScaleWidth) + 1 Y = (Y Mod Me.ScaleHeight) + 1
''''Force an update of the form Me.Refresh
End Sub
We employ the usual scheme of first drawing the mask and then the actual sprite. As you can see from the picture box with the masks, we have created a mask for all the sprites, and draw these mask frames in the same manner as the sprites. This would not be necessary in real life with this particular sprite example. Since each frame of this sprite''''s animation sequence is the same shape and therefore has the same transparent and visible areas we could have used the same mask for each circle.
Run the project and press the Start button. Observe how the sprite changes color as it moves down over the form.
More on Timing
This is all fine and good, but you may run into a situation where you do not want the same frame change (rate) as the timer interval. For example let''''s imagine that you have created a scene with some slow blinking lights which you want to blink once every second. If you were to blink the lights using the game loop, which we''''ll say is firing once every 20 milliseconds, the lights would blink so fast you wouldn''''t be able to see it. The problem here is to delay the blinking of the lamp, so it will only blink, or Blit, once each second and not every time the game loop is fired. That is why we will use the GetTickCount API, to check if a second has elapsed, and if it has, fire the [1] [2] [3] 下一页 [Sql Server]Sql精妙语句--各种求值函数 [网页制作]网页表格之---多个表格纵向排列 [网页制作]JavaScript另类用法--读取和写入cookie [网页制作]号称非常安全的上网工具---360安全浏览器介绍 [办公软件]信息技术教学篇---Word工具栏的显示、隐藏及四种菜… [操作系统]开始菜单---运行命令大总结 [操作系统]网络转载---64位操作系统与32位的区别 [操作系统]ldap:///(没有响应)Windows无法访问指定设备、路径… [网络技术]安全篇---交换机设置方法介绍 [聊天工具]Real10 & Xpdf installation on Linux Box
|