Home » Community » Forums » » Frame Rate Independent Movement
  Intel sponsors gamedev.net search:   
[Control Panel] [Register] [Bookmarks] [Who's Online] [Active Topics] [Stats] [FAQ] [Search]

Add Forum to Favorites |  Send Topic To a Friend | View Forum FAQ | Track this topic


 Last Thread Next Thread 
 Frame Rate Independent Movement
Post Reply 
I would put the test before the division:


if (speedfactor <= 0)
speedfactor = 1;
fps = targetfps/speedfactor;

prob doesn't matter in this case but since your testing for zero it implies that speedfactor could be zero which would result in a divide by zero error.


Round and round and round we spin
with feet of lead and wings of tin

 User Rating: 1015   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

QueryPerformanceCounter(¤tticks);

should read:

QueryPerformanceCounter(currentticks); <- There should be a ampersand in front of currentticks but I can't get it to display in this message.


Round and round and round we spin
with feet of lead and wings of tin


Edited by - BillB on June 4, 2001 9:35:07 PM

 User Rating: 1015   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Everyone aware of basic game- and realtime-programming should be able to develop such a class. I hope the flooding is realy over now!
BTW good work anyway!
I just think there are too much beginners trying to develop some realtime-programmes with a leak of basic knowledge.
Perhaps they should first read some books and then trying to program. If they are not able to have any idea how to program such a simple class they should start over to read the book again, and again, and again, and again, . . . .

 User Rating: 1012   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

The way I do this is as follows:

  
float time, frameTime, lastTime;
__int64 timerStart, ticksPerSecond;
  

in the init function:
  
QueryPerformanceFrequency( (LARGE_INTEGER *)&ticksPerSecond );
QueryPerformanceCounter( (LARGE_INTEGER *)&timerStart );
lastTime = startTime;
  

each frame:
  
__int64 now;

QueryPerformanceCounter( (LARGE_INTEGER *)&now );
time = float(double(now - timerStart) / double(ticksPerSecond));

frameTime = time - lastTime;
lastTime = time;
  


Now, in my game code, I specify all velocities in units/second, and all accelerations in units/(second^2). So if I want my tank to move at 4 unit per second, I just call:
  
tank.move( 4.0f * timer.frameTime );
  

each frame...


War Worlds - A 3D Real-Time Strategy game in development.

 User Rating: 1015    Report this Post to a Moderator | Link

Three warnings for you all. First of all, never rely on QueryPerformanceCounter() to return a valid value. One machine here in our office responds with an erroneous value about once every four seconds. The value in error is approximately 10,000 times the value received the previous few frames. This was causing major problems with our frame rate system on this one machine. Luckily we caught it before the game's release. Now we use GetTickCount() which has been very reliable.

Secondly, always have a backup for QueryPerformanceCounter(). If QueryPerformanceFrequency() returns zero, then there is no high performance counter available in hardware. Therefore you should drop back to using GetTickCount().

And lastly, another developer here warned us that one of his machines at home returns an incorrect value for QueryPerformanceFrequency(). The value returned is of a magnitude 10 times lower than it should be.


Steve 'Sly' Williams   Code Monkey  Krome Studios

Edited by - Sly on June 5, 2001 7:14:23 PM

 User Rating: 1003   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

I wonder if Quake3 or UT runs perfectly on those systems you talk about there. GetTickCount is way slower than Query... (correct me if im wrong, havent tested it myself)



Pascal vd Heiden
XODE Multimedia
Programmers are artists.


 User Rating: 1015   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

quote:
Original post by Sly

Secondly, always have a backup for QueryPerformanceCounter(). If QueryPerformanceFrequency() returns zero, then there is no high performance counter available in hardware. Therefore you should drop back to using GetTickCount().



Yeah, I have a test for the first QueryPerformanceFrequency(), which if it fails, I use timeGetTime() elsewhere. I didn't include it for clarity's sake. (BTW, I was the Anon Poster...)

I haven't heard about those other two problems. I suppose a solution to the first one is to test your return value compared to the last and if the difference is too big to get another value. Does the problem still show up if you can QueryPerformanceCounter() twice in succession?

I don't know what to do about the other one. Maybe let the user specifiy the frequency in some way if their machine returns the wrong value? I guess if that's the case though, a lot of games won't run properly since so many rely on QueryPerformanceCounter.


War Worlds - A 3D Real-Time Strategy game in development.

 User Rating: 1297   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Random Comments

This is really mean, but...
I think QueryPerformanceCounter should work on any newer system...
so you could make your "minimum system requirements" require a PC that has a performance counter... I think it's anything 586 (Pentium 1) or later, and any 586-compatible...

...but then I've never programmed on an AMD, either (yuck!)

Basically, any method of getting the "time" OTHER than the high-performance counter will have horrible accuracy... possibly as bad as 20-200 ms (!) resolution...

To display an ampersand, use &amp;

--Tr][aD--

 User Rating: 1015   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

quote:
Original post by Dean Harding

Yeah, I have a test for the first QueryPerformanceFrequency(), which if it fails, I use timeGetTime() elsewhere. I didn't include it for clarity's sake. (BTW, I was the Anon Poster...)

I haven't heard about those other two problems. I suppose a solution to the first one is to test your return value compared to the last and if the difference is too big to get another value. Does the problem still show up if you can QueryPerformanceCounter() twice in succession?
There is a KnowledgeBase article about this problem. It is a hardware problem with certain chipsets. The article also has code to detect these leaps in returned values.
quote:

I don't know what to do about the other one. Maybe let the user specifiy the frequency in some way if their machine returns the wrong value? I guess if that's the case though, a lot of games won't run properly since so many rely on QueryPerformanceCounter.
The user will not know what the frequency is supposed to be. This is a problem with some hardware that is noted in this Knowledge Base article, but I do not think that this developer has a Japanese version of Win98 on a NEC PC9800. I don't think that this is a problem to worry about.

Steve 'Sly' Williams  Code Monkey  Krome Studios

 User Rating: 1003   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

This article doesn't address problems when collision detection fails because something moved too far in one frame. I prefer to use frame skip, where the graphics are drawn for a frame only when the frame rate is at the target. Otherwise, only game logic happens until the game has time to draw the graphics.

 User Rating: 1015   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

BTW, for collision detection, use vector intersection. That tells you if and where a collision occurs. So it doesn't matter what your frame rate is.

I personally don't use QueryPerformanceFrequency(), as it can fail. I stick to timeGetTime(). After all, I lock the frame rate so that it never exceeds the screen refresh. So at
65Hz that gives me approx 15 msecs (or ticks) per frame.

Since I am not simulating nuclear rections this precision is good enough for me.


D.V.

 User Rating: 1025   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

I have currently slightly different system to have constant movement.

MoveFrame(float seconds)
{

// Move objects in at speed 100 fps

while( seconds>0.01f )
{
object_list->Move(0.01f);
seconds -= 0.01f;
}

objectlist->Move(seconds);
}

What do you thin about this? If machine is slow, then this is not good, if Object::Move has much calculations. But this keeps objects on same line on slow and fast machines.


 User Rating: 1013   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

While this is fine for any demo or single player game, it's worth a look at fixed timesteps for a performance boost & the ability to make things deterministic - vital for multiplayer.

The idea is to have your game physics update at a constant rate - say 25 times a second. Frames are drawn as fast as possible in between those physics ticks, using interpolation to smooth things out.

I tried this in a simple particle engine I'm using, and got a 50% performance boost, simply because I'm not doing my physics calculations every single frame.

There is a very long forum thread over at flipcode that I would point you to if you're interested:
Main loop with fixed timesteps

Catfish

Edited by - Catfish on June 7, 2001 8:33:23 PM

 User Rating: 1015   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Well, I've not worried much about constant game-rates (yet) - but it seems to be one of those things that come approaching on the horizon.

I've got a few suggestions that should fix up a few issues, particularly with very high fps-rates, and may actually reduce visual jerkiness (on the sub-detectable level):

Without too serious visual artefacts it should be possible to use GetTickCount every 5th or 10th frame. Considering the high fps games achieve nowadays that should not yield visual jerkiness, whilst putting several frames between GetTickCount increases the effective resolution and thus accuracy that GetTickCount can deliver (and correspondingly makes the "SpeedFactor" calculation considerably more accurate as well.

Obviously the same "SpeedFactor" will then be used over 5,10,whatever frames.

I've played around with GetTickCount to get a good measure of my frame-rate, and discovered that GetTickCount is called frequently enough to return values of about 20 or below, then the resultant fps (or SpeedFactor, etc) calculation yields very granular or inaccurate results.

Henri aka A-Lore


 User Rating: 1035   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

On Intel-based platforms, you can get a finer resolution than QueryPerformanceCounter by counting clock cycles with the following code:

void ReadTimeStampCounter(__int64 *apCounter)
{
asm {
push edx
rdtsc
push edi
mov edi, dword ptr apCounter
mov dword ptr [edi], eax // Low 32-bits of the TSC
mov dword ptr [edi+4], edx // High 32-bits of the TSC
pop edi
pop edx
}
}

Then, call the function using the address of a 64-bit integer.

Note that the above code doesn't test to see if the processor supports the rdtsc instruction at ring-3. Most/all Pentium chipsets have support for this...but the OS is a bigger concern since it may have cleared the CR4 register bit to disable the rdtsc instruction.

To use this as a time-based function, calculate how many clock cycles per second it takes (the equivalent function to QueryPerformanceFrequency).


 User Rating: 1015    Report this Post to a Moderator | Link

In my experience, the "distance = velocity*time" method works well if yu have a frame rate that isn't "too high" or "too low". At too high of a frame rate you will run into rounding issues (note I'm talking about VERY high rates). At too low of a frame rate your simulation becomes inaccurate. Imagine a ball flying in an arc. If your update rate is very low you may get something more like a triangle shaped path (update at the start, top, and end of the arc).

The previously mentioned constant frame rate method seems to be the best way to approach things. All you do is calculate how many FRAME_LENGTH boundaries you have crossed since the last update and update your world that many times (passing in FRAME_LENGTH as the length of the last frame). I have applied this method in very low frame rate systems and acheived playability at rates as low as 700ms per frame.

As a side note, there are even more frustrating issues that arise when using the high performance counter. Many systems have a unique high performance counter on every CPU. Windows seems to have no remorse when tossing your thread around from CPU to CPU. When you get moved, your counter will not be the same counter and it is certainly possible to get very large or possibly even negative time deltas. You always double check any time delta you get from a hardware counter and do something smart (use the last delta...throw it out...shrug) if it looks suspicious.

 User Rating: 1015    Report this Post to a Moderator | Link

I already had a frame counter in my engine so I used that instead of the example code. Since my engine already knows the current fps all I had to do was plug that number in:

Vector final_position = m_pos + vel * ((target fps) / current fps);

The goal is that if you're already running at 60 fps, target fps/current fps will give you 1.0f meaning that since you're already at the cap, there's no need to mess with the motion further.



 User Rating: 1018   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

All times are ET (US)

Post Reply
 Last Thread Next Thread 
Forum Rules:
You may not post new threads
You may post replies
You may not edit your posts
You may not use HTML in your posts
Jump To:
Administrative Options: