From: greg@dent.uchicago.edu (Greg Kuperberg)
Subject: The Correct Way to Control Game Speed
Organization: Dept. of Mathematics, U. of Chicago
Date: Sat, 19 Dec 1992 19:02:58 GMT

My experience in game programming has led me to a very strong opinion
about how to control game speed:

There is a right way and a wrong way to control the speed of an animated
computer game.  The right way is to synchronize the game with a clock.
The wrong way is to use delay loops.

If you play music or listen to music carefully, you should already know
that it's infuriating to hear music slow down when it gets to a
complicated part.  Good music is set to a regular tempo, and the best
music is usually faithful to that tempo even when it seems to speed
up.  Although there are exceptions, most classical music never speeds
up or slows down slightly; it either stays at exactly the same speed
while getting louder or more complicated or moving to a higher pitch,
and at some point the tempo may increase by exactly a factor of 2 or
exactly 3/2, but rarely just slightly.  Humans have an innate sense of
tempo which is worth reinforcing.

It's the same way with computer games.  You should be able to make 
your game slow without being sluggish or fast without being hurried.
And above all, your game should play at the same speed even when run
on a faster computer.  These problems are fatal diseases for games
that use delay loops.

Someone here said that he tried to use clock interrupts but that it
didn't work.  You must find a way to make it work.  It is very
important.  I have written four computer games on two platforms
and I always found a way.  In fact there never was any other way;
synchronization was there from square one.

To repeat advice that also came with the advent of structured programming,
modular programming, and object-oriented programming, you must change
your way of thinking to incorporate synchronized animation.  Here
are some examples of good and bad terminology to express various
design concepts of an animated game:

Concept            Good terminology         Bad terminology
-------            ----------------         ---------------
Unit of time       Animation frame          Main code loop
Waiting            Wait for clock tick      Delay loop
Velocity           Pixels per frame         Milliseconds per redraw
Slow down game     Decrease all velocities  Increase main delay loop

The one problem that the delay-loop method solves, sort of, is the
situation where a game has too much to do and won't finish its work in
one frame.  The general method for avoiding this problem is to create a
mental CPU cycle budget and then roll up your sleeves and make sure
that in the worst reasonable case on the slowest platform, your game
either stays within the budget or comes close.  When I programmed the
IBM PC my budget was as it happened exactly 256K clock ticks.  In my
case I actually counted the number of clock ticks for each instruction
in my inner-most loops to make sure that the game would not go grossly
into time debt.  If you are writing in C or relying on someone else's
library, this can be impractical, but if you find that your game is
running in the red on time, you can do some time tests to find out
why.

Now for some specifics.  The eye and the visual cortex integrates
separate images into continuous motion if the images appear at a rate
faster than 5 or 10 times per second or so.  15 or more frames per
second looks good, and it is a waste of CPU time to have more than 30
redraws per second.  However, sound is a completely different matter.
The ear uses time in a much fancier way than the eye, and animation
frames are the wrong paradigm for servicing audio.  In general your
animation loop can make requests for sound which are then handled by
other software or hardware.  It can't play sound itself.

The general structure of a synchronized animation game is this:

for(;;)
{
	oldtime = gettime();
	...       /* Draw and recompute everything and service sound. */
	while(oldtime == gettime());
}

What this loop will do if the game starts to fall behind is jump immediately
to the next animation frame to catch up.  If it falls an entire tick
it reprieves itself that tick.  

If you decide that the clock ticks are too short, a simple patch
will multiply the tick length.  Here is how you double it:

for(;;)
{
	oldtime = gettime()/2;
	...       /* Draw and recompute everything and service sound. */
	while(oldtime == gettime()/2);
}

If you want to know whether or not your game is falling behind you can
put in a simple time monitor:

for(;;)
{
	oldtime = gettime();
	...       /* Draw and recompute everything and service sound. */
	if(oldtime != gettime()) showlag(gettime()-oldtime);
	while(oldtime == gettime());
}

On the IBM PC there is a clock chip which is set to tick 18.2 times per
second.  The clock chip invokes a timer interrupt which is, by default,
handled by BIOS.  When you ask BIOS for the time, it will give it to
you in hundredths of a second.  Don't let that fool you; it doesn't
know the time that well and since 1/100 of a second doesn't even divide
evenly into the clock tick, BIOS rounds time to the nearest 1/100th.
None of that will prevent the above code from working.  However, since
BIOS is a dog of a ROM OS library, you might want to commandeer the
interrupt and even the clock itself.  If PC clones are still using the
same clock chip or a compatible one, the chip has an internal clock
that ticks exactly 64K*18.2 times per second, and you can set your
clock speed to any multiple up to 64K of this cycle length (it's
exactly four times as long as the clock cycle of the original IBM PC's
8088 CPU).  For some reason the designers of the PC set the clock to
the slowest that the clock chip could possibly provide.  If you
commandeer the interrupt, what you should not do is put your whole
animation loop into the interrupt routine.  All the interrupt should do
is update the game's internal time-of-day variable and quit.  Finally,
if you muck with either the clock or the interrupt, BIOS and DOS will
have the wrong time.  It's up to you to fix them when your game
finishes.

On the Mac, the Toolbox, which is a very good RAM/ROM OS library, can
easily provide you with 60 clock ticks per second.  They at
least used to be synchronized with the monitor's raster refresh.  It's
better to work with the Toolbox than to work around it, and whatever
you do, don't work against it.
Newsgroups: rec.games.programmer