Jump to content
  • Advertisement
Sign in to follow this  
MYoung

game loops and time based movement

This topic is 5174 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
Advertisement
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
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!