The Delay Factor

One of the major difficulties in game development is to make your game playable on as many computers as possible, and on as many newer (and possibly older) computers as possible. Games written today need to be programmed to run at the same speed on all PCs. If not, then your game may be unplayable for someone else, ruining their experience.

This is what I call the delay factor: a variable that changes relative to the speed of the computer, to which you base all your time-dependent functions on, such as movement and animation.

Perhaps you can remember playing an old game on a new PC, only to find that you can't play it because it runs too fast. Chances are they didn't program speed control into their game, or they used a hack-method that worked at the time but doesn't work today.

I will start off by telling you what NOT to do. I have made a lot of these mistakes before, and because of that I can't run many of my older games; I have to edit the source code to slow things down, or it's just too much work and I give up.

The WRONG Way

The worst thing you can do is something similar this:

DIM n AS INTEGER
FOR n = 0 TO 500000: NEXT n

This may solve the problem on your computer, and some speed control is better than no speed control. But if you want to share your game, or look back on it in the future, you need something better.

There are a number of ways to approach this issue. You could count how many times a loop runs within one second and use that as your delay factor, or you can make the loop count (500000) a variable and make the user adjust it him/herself. But these are poor methods and will likely frustrate other people, and they'll never truly know what speed you meant the game to be played at.

Another way is to utilize the vertical retrace. The vertical retrace waits for the monitor to refresh. Most monitors refresh at 60hz, or 60 times a second. So programming the speed of your game around this is a more viable option. Today, most computers will probably run your game, but some monitors refresh at 70hz or 75hz. In the future monitors may refresh at 120hz.

The RIGHT Way

The best solution, as far as I know, is to track how much time goes by between one cycle of your main game loop and use that time as the delay factor.

Of course with this way, the faster the computer, the shorter time and thus delay factor. So you can't throw the delay factor into a for loop unless you invert it, but a better way is described below.

Multiply all of your movements, animations, and other time-dependent objects by the delay factor. Don't code any stupid empty loops to slow things down. This way, if your game runs at a quadrillion cycles per second, the delay factor will compensate and everything will run at the same speed.

Another advantage of this method is that slower PCs will run your game also. If you're coding your game on a PC that runs it at 60fps, a PC that can only run it a 30fps will compensate by moving things twice as fast.

You still should test your game on both slower and faster computers as you're making it. Chances are you won't have everything running perfect the first time. Sometimes I make the mistake of slipping in an absolute value without multiplying it by the delay factor, and testing it on other computers is a way to catch that.

Use FreeBasic's TIMER command to do this. First you take the time before executing your game loop. Then once the cycle is complete, call TIMER again to get the new time. Subtract the old time from the new time to get the cycle time.

Example Code

I made a module below that you can use if you don't feel like coding your own.
Save this as DELAY.BAS. Include it in your game, then just call UpdateSpeed() after each cycle. It will return the time between the first time you called it and the second time. You can also call GetDelay() to get the delay factor again without recalculating it.

Keep in mind that you should call it at least once before your main game loop to initialize it. It will return the value of TIMER the first time it's called (which is usually a big number). A good way is to render a couple frames of your game first to get the speed before performing calculations that depend on the delay factor. The rendering process is usually the most processor intensive, so it'll give a fairly accurate time.


DECLARE FUNCTION UpdateSpeed () AS DOUBLE
DECLARE FUNCTION GetDelay () AS DOUBLE

DIM SHARED DelayDelay AS DOUBLE '- odd spelling as to not conflict with other variables in your code

'- Calculate the global delay factor
FUNCTION UpdateSpeed () AS DOUBLE

DIM speed AS DOUBLE
STATIC timex AS DOUBLE = 0.0f

speed = TIMER-timex
timex = TIMER

DelayDelay = speed

RETURN speed

END FUNCTION

'- Return the delay factor
FUNCTION GetDelay () AS DOUBLE

RETURN DelayDelay

END FUNCTION

Fail!

It's now the last week of July. Where's Toadman 3 or that sprite editor I was talking about earlier? They haven't come very far. I typed up some ideas, but Toadman 3 hasn't picked up any momentum. The sprite editor was moving along well, but development on it stopped a while back.

Stuff happens, I guess. It just seems harder nowadays to finish something, or even start something for that matter.

It makes me ponder what has changed since the days when I was really active and coded a number of projects in a small amount of time. Between 2000 and 2002, I finished six games: Toadman, Toadman 2, Unofficial Tournament, Torched Earth, Larry the Dinosaur, and Larry the Dinosaur 2. However, between then and now, nothing, except for a little ascii game I released back in 2007.

So, what's happened? I work and go to school, and sometimes that wears me out, but I'm still a single man with plenty of free time. Maybe I try to make projects that are too big in scale, and I need to cut back and make simpler games.

There's some truth to that. Whenever I finished a game in the past, I felt that I needed to top it in the next game. When I finished Larry the Dinosaur 2, I was pumped up for the sequel. It was going to be in C++, have better graphics, larger levels, faster gameplay. It would put #2 to shame.

Well, I made it pretty far on the sequel, but I bit off a little more than I could chew on that one.

So maybe I need to take a step back. Start with simple games again, original games instead of sequels, so I don't feel the need to top the previous game in the series. I am only one man after all.