Sign in to follow this  

game loops and time based movement

This topic is 4865 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm trying to get the whole idea of time based movement correct for a game I'm making. Here's what I've concluded so far. Just want to see if this is correct... There are 2 basic things that a game has to do. It has to move objects/do collision detection/misc ops (which I'll just call MOVING OBJECTS) and it has to DRAW them. In a game, the 1st priority is to move objects at the same rate on all computers fast enough to be able to run the game. Rendering the objects is the 2nd priority. That is at least the case if you want time based movement which is key to not only fair game play (if you had a Tetris game you wouldn't want the pieces falling down at different speeds on differing speed computers at the same game level), but also very important for network game play (you want the applications sending data back and forth in a predictable fashion). So this leads to a design as follows... gameCntr : A counter that counts upward based on the system clock and can be subtracted from. gameSpeed : An integer value representing how many counter ticks per execution of the object movement portion of the game loop.
if (gameCntr > 0) {

	MOVE OBJECTS

	if (gameCntr < gameSpeed) {

		DRAW OBJECTS

	}

	gameCntr = gameCntr - gameSpeed

}
This is what I do in my code now at it works well. The problem, though, is that if MOVE OBJECTS always takes too much time (longer than gameSpeed) then the objects move, but they aren't ever drawn. Granted, you can vary things like collision detection and not run them on every game loop to keep the calculation times down. Now, if I do draw the frames I obviously won't be getting consistent time-based movement. So the question is what to do in this case? Draw the frames anyway even though the game will run slower than it's supposed to? Terminate the program? Terminating the program might be bad in that if for just a brief moment a computer cannot keep up it still might be able to catch up because the game counter is cumulative. Maybe I just need to make my game, pick the bare minimum spec machine, adjust the gameSpeed (number of timer ticks per "move" loop) so that the frame rate is just barely playable, and leave it at that? Or do I have the whole time-based movement model incorrect? Thoughts?

Share this post


Link to post
Share on other sites
reality check: what purpose does moving twice and drawing once serve?

In a more proverbial form: "If a program updates it's data, but there is no screen to see the changes, does it really update?"

Now if i missed something please correct me, but otherwise,

move once,
draw once,
repeat.

Share this post


Link to post
Share on other sites
er yep, leiavoia, u missed something. Easiest way to see why is to think of a dedicated server.



MYoung:
Your method is sort-of render-speed-locked. Meaning faster graphics cards are going to make your MOVE OBJECTS routine run more often, thus making your game run at a different rate.

It seems you're using constant physics (like quake) (as opposed to moving units with velocities multiplied by the time between consecutive MOVE OBJECTS calls (like unreal). This is my preferred method, too.



The best way to do what you want to do is to query the system time.

Make a loop like this:


time nextTime = getTime();
time moveFrameTime = ONE_SECOND / number of MOVE OBJECTS per second.

loop {
time currentTime = getTime();
if(currentTime > nextTime) {
while(currentTime > nextTime) {
MOVE OBJECTS
nextTime += moveFrameTime;
frame++;
}
DRAW
} else {
WAIT
// The current MOVE frame is being displayed so there is no point in re-drawing.
// If you want to do this right, you will sleep and ask the operating system to wake you when currentTime > nextTime.
}
}


;
note: substitute the type "time", the constant "ONE_SECOND", and the function "getTime()" with suitable replacements.

This means your MOVE OBJECTS happens at a constant rate, and your DRAW happens as often as possible. You may have more than one MOVE OBJECTS frame per DRAW, but never more than one DRAW per MOVE OBJECTS frame.

BTW, often the MOVE OBJECTS frame is called a PHYSICS frame, and often the DRAW is called a RENDER frame.

Share this post


Link to post
Share on other sites
i suggest using a timer to record the amount
of time passed between each frame, and using
that as a coefficient for all of your game movement.

so the pseudo-code would look something like this...


While(...)
{
CoEfficient = GetTime() - PreviousTime;
PreviousTime = GetTime();
...
Object.Draw();
Object.Position += CoEfficient * Object.Speed;
}


This means you don't have to manually wait for the
frame to finish. it will use high frame rates to their
full potential while keeping the speed always consistent.


Share this post


Link to post
Share on other sites
So you're trying to update as fast as possible for the benefit of clients? That makes sense. Would it also make sense then to package the updating routine as a seperate "server" binary and run the game as a client on the same machine (if playing one-player)? Then you could have the server app *just* update and that also makes it possible to run it by itself without a client on the local machine. I hope that made sense (~?~) I've seen other games do this. When you start the game it also starts another app like "GameServer.exe" and you end up running two programs. But the server program is soley responsibly for updating and data processing with no graphics.

Share this post


Link to post
Share on other sites
Ok, other reasons for updating without drawing.

User ALT-Tabs or switches to another app while playing multiplayer.
Constant physics frames (like quake).
Sound is typically queued in the physics frame.
Preventing rounding bugs when rendering at slow frame rates. (Eg fast projectile weapons passing through enemies because there was no physics update when the projectile was in contact with the enemy.

Typically, physics runs at a higher framerate than render.

[Edited by - Krylloan on August 20, 2004 11:58:53 PM]

Share this post


Link to post
Share on other sites
Hmm, I'm pretty sure my pseudo-code makes for constant speed. My move objects routine runs at a constant speed (with respect to time, as opposed to hardware speed). It "moves objects" every gameSpeed counter increments, where the counter is added to by the difference in values returned by calls to QueryPerformanceCounter (current value - prior value). When I insert a big-ass for loop to bog down the game loop it runs at the same speed -- it just gets pretty choppy. So it's still moving objects at the same speed, it's just not having enough time to move and render so it has to sometimes move twice before rendering again.

So Quake uses the method that I've more or less outlined? Interesting. The problem that I've noticed with this method is that every time there is a move without a render, what I'll call a dropped frame, there is an OBVIOUS hiccup. I guess I'll just have to think of this in line with the "unreal" method. Doh! Krylloan, thanks for the hint into thinking of this in a better way. Back to the drawing board. fproto, that's also an very interesting suggestion I should explore as well!

[Edited by - MYoung on August 21, 2004 12:54:09 AM]

Share this post


Link to post
Share on other sites
Er, sorry I didn't realise your gameCntr was being generated through system calls. (Didn't read well enough).

How big are your move steps?
And how much processing are you doing during them?

If you are noticing a significant jump when only one frame is missed, you _might_ be doing too few MOVE OBJECTS per second. Are you looking for smooth motion? If so you should probably be either doing well over 30 MOVE OBJECTS frames per second or using velocity-and-time-based prediction for rendering, which is a bit of effort.

You should note that much of the rendering can be done after all rendering calls are made (this is especially the case in deferred-rendering cards, but even your typical NVidia/ATI immediate mode cards can still be doing rendering once you have finished issuing instructions and have ordered a framebuffer swap. All this extra work is being done by the GPU and not the CPU.

This means your method may not be making the best use of the GPU power because it could be sitting idle while being a few physics frames out of date. Of course if you are issuing a large amount of instructions to the GPU or performing a large amount of CPU-based rendering calculations such as complex visibility calculations or something then this may still be acceptable.

Currently your game can idle when the display is out of data. This makes for higher average latency and penalises owners of faster graphics cards by limiting the maximum FPS.

Quake uses the method I outlined which is similar but not the same as your method, except for the WAIT state, where I think it is possible for quake to render the same physics frame twice, although with prediction, which is better than my method, but harder.




The unreal method is good for a wide range of processing speeds and if your MOVE OBJECTS takes a long time to calculate. But it has rounding issues:
Servers have to check that clients behave themseves more.
At very low frame rates physics aliasing can occur. (Like the weapon projectile moving through the monster).

Share this post


Link to post
Share on other sites
Yeah, I understand what you mean about penalizing owners of fast hardware because I'm locking the movement speed and that basically locks the frame rate as there's no sense in rendering more than one frame between movements. And if I don't penalize the fast machines then there are hiccups on moderate speed machines. I know that in Quake the movement is smooth at 40 FPS and more smooth at 70 FPS. My method would be smooth with the occasional hiccup on one machine and smooth as silk on a fast machine that can keep up with the move render sequence without "dropping" a frame.

I guess I will abandon this way of doing things. Just for kicks, though, I was running about 178 move loops per second which translated into 178 FPS. But at this stage all I'm drawing is a checkerboard floor and using it with a rotate and translate camera interface that I wrote. So I'm at an extremely preliminary stage. That said, I did write a roller coaster program for a graphics class I took a year ago that uses the method I outlined and I'd get some very pretty annoying hiccups.

It seems like the coefficient method might work. Though it might be hard to use that for more complicated types of object movement. Or not. I dunno. I feel like Wiley Coyote etching out some grand Acme Co. plan on the chalk board only preparing myself to fall off a cliff. But that's half the fun of programming, right?

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Time based movement will only work if the computer is fast enough to at least run the update part at the frequency you want. So that should be the bare minimum of your system requirements.

Then you make the rendering go as fast as it can. If from that point, you don't have enough time to run your update time, then you steal time from the renderer.

So say you want 60 updates per second. That means an update each 1/60 seconds.






updateTime = 1/60;

lastUpdateTime = 0;

while(softwareruns)
{
currentTime = Time.GetCurrent()
if lastUpdateTime - currentTime > updateTime
{
//let's check how many multiples of updateTime we need to run now
multiple = lastUpdateTime - currentTime / updateTime

//round does if .0 to 0.4, lower, if .5 to 0.9 higher
//but if we round down, we need to add the time, so we don't get errors in the update
// so in round, rest will be 0 if we rounded up and the actual extra time when rounding down
rest = 0;
multiple = round(multiple, &rest)

for( update = 0; update < multiple; update++ )
{
UpdateWorld();
}

lastUpdateTime = Time.GetCurrent() - rest

drawWorld();
}
else
{
drawWorld();
}
}




If you renderer speed varies a lot, you should cap it and do a similar "if" then with the update time. That way you will potentialy get a smoother interleaving of render / update time.




Share this post


Link to post
Share on other sites
I tried the "coeffieient" method offered which I guess is good ol' displacement = delta time * velocity. It works very well.

So are there any disadvantages to this way of doing it? Would this method would complicate network play? I guess player data would be a little off because there wouldn't be a way to pin point the other player's position. Though I guess that goes without saying and it wouldn't matter much at all if the game loop is running at a fast rate. Right? I guess that's kinda what was already said. :)

This is exciting! Time to fix my rollercoaster program! Wahoo!

Share this post


Link to post
Share on other sites

This topic is 4865 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this