Author : Filipe Medeiros
Page : << Previous 5 Next >>
/* slow it down */
circlefill (screen, x - 1, 100, 5, 0); /* erase from last place */
circlefill (screen, x, 100, 5, 15); /* redraw at new place */
}
Try this out to see that it works. Now make the circle bigger (radius 50, say) and see what happens.
4.4.3 Reducing flicker
Okay, so that started flickering quite a bit. If it didn't, make the circle bigger. If it still doesn't, well, it would if you had used a high-resolution screen mode (try it). If you can't make it flicker at all then congratulations, you have a supercomputer.
Us mere mortals, though, have to do something about the flickering. There are many approaches; some are more complicated than others, some are more effective, and some are simply good because they don't take much CPU time (and so the game runs at a higher frame rate).
4.4.3.1 Synchronising to the vertical retrace
This is the most important thing to do. The vertical retrace is the time period during which your monitor is preparing to draw a new frame. During this time nothing is drawn on the screen, so if we can erase the object and redraw it before the monitor starts the next frame our problem is solved. The `vsync' function waits until the start of the next vertical retrace period. When this happens, we want to update the screen display as quickly as possible. So, replace the `delay(10)' with `vsync()' and try it again. Much better. Note that the vertical retrace is like a timer in a way; it occurs at a fixed rate (for each video mode; it varies between modes). Consequently, since we have to sync with it anyway, it can be a useful way to regulate the game speed.
4.4.3.2 Maximising drawn time
If we are drawing so much that we can't get it all done during the retrace, we need to reduce flicker in some other way as well. Consider what the situation would be if we had a lot of circles, and we erased all of them before redrawing them all. They would all be undrawn for a relatively long time; there's a high chance that the monitor will try to display their area of screen when they aren't drawn.
We could solve this problem by erasing and redrawing them one by one. This would help reduce flicker, but it would introduce other problems. The point, though, is that maximising the amount of time things are drawn for is a good aim. For example, if you erase all your objects and then work out where you should redraw them, they're off the screen while you're calculating. If you can do the calculations before erasing them, they'll spend more time on the screen and less time off it.
4.4.3.3 Optimising drawing order
As we noted above, sometimes we might not get all our drawing done during the retrace period. So what happens next? The monitor begins displaying the screen, from the top down. So, if we're only going to get some of the drawing done in time, it would be better to get the stuff at the top ready so that the monitor can draw it.
In fact, the important thing is not to erase something while the monitor is trying to display it. We can't know for sure which part of the screen is being updated at any time though, unless we use a very high-resolution timer, which has other drawbacks.
Several drawing orders are sensible. You can draw things strictly from the top down. This also improves the frame redraw speed in higher resolution/colour depth modes by reducing video bank switches. You could consider how complex each object is, and perhaps draw the simplest ones first. A technique I haven't tried is to draw from the *bottom up*. This may sound odd, but if you do this you'll only run into the retrace once. If you go top-down, it may overtake you on a slow object, but then you might have enough fast objects that you catch up with it again. This is purely theoretical, I've never seen it done; it is surrenduring to the fact that you'll get some flicker, but attempting to reduce the amount of flicker.
4.4.3.4 Double buffering
This is a very popular, simple system, which works well in fast (i.e. low resolution) video modes.
The technique of double buffering involves writing all the graphics to a temporary bitmap, until a whole frame has been updated, and then copying the final image to the real screen memory in one big chunk.
This can be effective for several reasons. Firstly, it means that we are no longer erasing anything from the screen -- we are just replacing one image (with everything drawn) with another (still with everything drawn).
Secondly, video memory is rather slow. If we tend to overwrite areas of the screen a lot when drawing the frame, it will be faster to do all these writes to the (fast) system memory and then copy that to the (slow) screen memory than it would be to perform the writes directly to the screen memory. In addition, reading from screen memory is often even slower; so if we will want to read from the image it is far better to read from a copy in system memory. This is very much like disk caching, where copies of bits of the disk are kept in system memory to avoid having to access the disk directly.
So, instead of drawing to the screen, we draw to another bitmap in memory. Then, when we're ready, we `vsync()' and then copy the entire buffer onto the screen. In the 320x200x256 mode this fits well inside the retrace interval, so there should be no flicker at all. In higher resolutions or colour depths there is of course more image data to copy, so it takes longer. The effects of this range from shearing (where the top part of the screen shows one frame and the bottom part shows another) to drops in frame rate (where it takes so long that we miss the start of the next retrace period, so `vsync' waits for the one
after that).
See the example program `src/4.4/dblbuff'. We won't look too deeply at memory bitmaps and the blit function yet; they'll come up in the sprites section later on.
4.4.3.5 Dirty rectangles
This is a variant of double buffering that doesn't require such a fast video mode, but it is trickier to implement. The theory is that you do exactly as you would then double-buffering, i.e. do all your screen updates to a memory bitmap, but you only copy to the screen those regions which have changed.
This involves keeping track of which areas have changed; the simplest way is to mark rectangles as "dirty" when you write to them. Then you can easily blit all of these rectangles to the screen.
This is better than double buffering if not much has changed, but if a lot has changed it can be worse, partly because a single blit of a large area is more efficient than a lot of blits of smaller areas, and partly because you may have two dirty rectangles which overlap; unless you take care to avoidthis, that region of the image will be copied twice. However, if you can detect situations where double buffering would be more efficient, you can simply blit the whole image in one chunk, as you would when double buffering. The two techniques have a lot in common.
Dirty rectangles are more awkward to implement, and if you make small mistakes you can get strange results.
4.4.3.6 Alternate line blitting
Again, this is a variant of double buffering. The idea here is to copy to the screen only every other line. You can copy just the even numbered lines, and leave the odd ones blank; then you get a slightly darker picture with effectively half the vertical resolution. You could also copy the odd lines in one frame and the even lines in the next; this way you don't lose any brightness, but some lines are `older' than others, resulting in some blur. These techniques work best for things like video.
5.1 Moving more circles
This sounds pretty simple, and it
Page : << Previous 5 Next >>