Timing - again

Started by
13 comments, last by kieren_j 24 years, 1 month ago
Hw d you use the multimedia timer functins? I tk a lk in the SDK but it''s really cnfusing. Has anybody used them befre? thx
Advertisement
I used timeGetTime() to return a time in milliseconds that is accurate to +- 1ms under windows9x and to +- 5ms under windowsNT. Now I use QueryPerformanceCounter() and QueryPerformanceFrequency() to get resolution below 1ms, since my frames are rendered in less than 1ms and will be lower on faster cpus. Only pentium cores (cyrix, amd, and other higher clocked cpus) support these two functions, so check a return value and if zero than no support and you must use timeGetTime(), however be carefull since change in milliseconds since your first query of timeGetTime() and this query of timeGetTime() might return zero ms.

I use the above functions to time a piece of code that draws the frames of my game. Let''s say this value is 20ms. Now you must convert this value to time in seconds. Multiply 20ms by 0.001 and out comes float value showing time in seconds it took to render one frame. Multiplication is faster than division so it''s better then 1/1000 and just write 0.001 .

Now that we have time in seconds, multiply this value by your velocity you give to your object. Let''s say that a fast cpu can render one frame in 1/10 of a second or 100ms per frame and slow cpu will do the same in 1/5 of a second or 200ms. The fast cpu spits out 10 fps and slow cpu spits out 1000ms/200ms = 5 fps. If you multiply object''s velocity by time in seconds the object will move the same distance per second on a fast or slow computer. The object will move more per frame on slow cpu and less per frame on fast cpu, but since fast cpu can spit out 2x more frames than slow cpu (per same unit time)the object will move the same distance as that on the slow cpu.

This way the object moves independent of the cpu speed. The object will move in jerky motion on slow cpu and smooth on fast cpu. Basically I let the video card to spit out as many frames as it can and try not to limit the fps to some value by throwing out the frames on fast cpu. This might sound confusing but it really works

You want to try to avoid moving the object by velocity only. This has the effect of working ok on your cpu but on a faster cpu the object will move faster since the cpu is spitting more frames per unit time than your cpu and thus the object will move very fast and becomes uncontrollable. I''ve seen some dos games do that.

Now some code:

NOTE: LARGE_INTEGER is a struct.

LARGE_INTEGER count, last_count;
LARGE_INTEGER delta_count;
LARGE_INTEGER counter_frequency;

if(FALSE == QueryPerformanceFrequency(&counter_frequency))
{
WriteErrorToFile(FALSE, "No high performance counter frequency detected...", __FILE__, __LINE__);

DestroyObjects();
return NULL;
}

I write the above before going into message loop. It''s necessary to obtain counter_frequency first since every cpu will have this value different.

Once in message loop I check how long it took to render one frame. This must be done for every drawing of the frame because in beginning this value will be large and will stabilize after few frames have been drawn. Some people want to get this value only once but since on first time it''s soo large it won''t accurately represent time it took to draw the other frames that come later in time.

// Now get cycles before rendering the frame
if(FALSE == QueryPerformanceCounter(&last_count))
{
WriteErrorToFile(FALSE, "No high performance counter found...", __FILE__, __LINE__);
DestroyWindow(hWnd);
}

NOTE: rv is HRESULT value declared before main message loop.
rv = RenderWorld();

if(FALSE == rv)
{
// Error was logged to file so exit the game
DestroyWindow(hWnd);
}

QueryPerformanceCounter(&count);

// How many cycles it took to draw the frame?
delta_count.QuadPart = count.QuadPart - last_count.QuadPart;

// Convert cycles to value in seconds
time_in_sec = FLOAT(delta_count.QuadPart) / FLOAT(counter_frequency.QuadPart);

Now comes the part where I move my object:
//-------------------------------
// CALCULATE SHIP''S X_POSITION
//-------------------------------
MoveShip(&time_in_sec);

NOTE: time_in_sec is a FLOAT value

Now comes the MoveShip(FLOAT* time_in_sec) code(is not complete):

// 20 here is a velocity
g_enemy_ship.x_displacement = *time_in_sec * 20;

// Add displacement to the x_position of the ship
if(TRUE == g_enemy_ship.moving_right)
g_enemy_ship.x_pos += g_enemy_ship.x_displacement;
else
g_enemy_ship.x_pos -= g_enemy_ship.x_displacement;

x_pos variable is a FLOAT type and it will keep adding displacements even if below zero so at start x_pos is zero but later it''s 0.0001, 0.0002, 0.0003 until 1.000 then 1.0001, 1.0002, and so on... My ship actually moves on 1.0 then 2.0, the values in between don''t count since blitting function takes in an INT and if it''s FLOAT passed in then it will get converted to an INT and the decimal part will be stripped. So 2.5 becomes 2. Hope this helps.

Jerry
I would like to make that stuff for moving my objects, but what about the collision detection? If I dont have a 3d accelerated graphics board I can have an object "jumping" over another.

How can I solve this problem, besides I have to know the position of the hit...
http://www.stas.net/rafael/icq: 11915640
JD, that was a very nice post. I have 1 comment though. Instead of timing your fps and then deciding how many pixels an object should move for the next frame, wouldn''t it be better to base an objects velocity on the timer, and keep fps totally out of the picture?

For example:

DWORD x = getTimeDifferenceWithPreviousFrame();
object->setPos(x * object->velocity);

That way, you don''t need to know fps to set new position for an object, and it would still be accurate.

LaterZ,
Moritz
dirtydevil,

You're right when you say the objects will jump over another since you're moving in larger steps on slower cpus. However the displacement is only telling you where your object is going to be when you draw it. You can store the old position in another variable then take the displacement and add it to the old position. If old position + displacement lie in another object then you use the old position and draw the object while triggering a collision flag. Let me know if I answered your question correctly or if you're unclear about something.

Anon,

That would also work providing that your velocity is a small value and is measured in meters/pixels per millisecond in case of timeGetTime() or meters/pixels per cycle in QueryPerformaceCounter(). If your code relies on accurate/real world interaction between objects then I would just bite the bullet and obtain the time in seconds it took to render the frame and work with velocities in meters per second to make the calculations easier to compute. Thanks for the kind words

I just noticed the division by counter_frequency could also be improved by multiplication of a 1/counter_frequency and then multiply by the delta_count instead of dividing it.

Jerry

Edited by - JD on 2/29/00 12:38:13 PM

Edited by - JD on 2/29/00 12:44:38 PM
JD:
Just a few days ago i did excatly what uve just described. Basicly it works. My only problem is that the movement isnt 100% smooth (but almost). Ever experienced any problems with the method u described ?

NewDeal,

I''ve used this method in my 3d engine, in Genesis3d, in my 2d game and never had problems. It was easier in 3d engines because everything is in FLOAT. In my 2d game I forgot to make the variable that stores the accumulated displacements into a FLOAT type and this produced no movement. I let the blit function to convert this FLOAT variable into an INT so on fast cpus it will take some frames until the accumulated values reach 1, 2, 3, etc. pixel move. I downclocked my cpu to see if there is a jerkiness and it wasn''t noticeable at all, well maybe just a little Hope this helps.
JD:
Let me know if ive missed something.
I have a theory that could might explain the jerkiness. If my sprite moves round 1.5 pixels each frame (sometimes 1.4, sometimes 1.6) wouldnt this cause the blt-func to almost randomly round up or round down the actual displacement. If that is the case that should be able to cause some jerkiness. if the ship moves 1 pixel 1 frame and 2 the next.
Get the idea ?

Thanx.

Ur initial post helped me alot btw.
Boy, it''s unbelievable how many new post arrive at this message board each day

Anyway, the way I see it is that the decimal part gets stripped off and instead of moving 1.5 pixels your object moves 1 pixel instead. Currently, my object moves one pixel every 60 frames and this produces a jerky appearance. When increasing the velocity the object''s motion becomes smoother and this is running on the same cpu at fixed frequency. Anotherwords, the only jerkiness I see is when my boat goes too slow and has to sit idle for several frames before it moves one pixel. I suppose that on very fast cpu the jerkiness will appear again to some degree because the boat will be sitting idle for 100 frames instead of 60 as is now doing, weird but true

NewDeal, I''m glad that my post helped you. I''m going to check for QueryPerformanceBlahBlah() and if it isn''t detected I will fall back on timeGetTime() and hopefully my code will take longer to render than 1ms minimum on the older cpus. I think that only 486, 386, 286, etc. don''t support QueryPerformance...() functions. Happy coding

Jerry
If you want, check out the article on game timing over at the Programmer''s Vault. I''m not positive of the URL, but head to http://www.chesworth.com/pv/ and look for the Game Timing article. The code is for DJPGG with Allegro, but the concept is applicable to any environment as long as you can get a timer with decent resolution going. Also, I''d love it if you could tell me what you think, being as it''s the first article I''ve written =] Actually, I''ve been meaning to update it(read: rewrite). It seems the best way to do things would be to go the mathematically correct way. Keep track of a time variable, and factor time into all your translation equations. For example, the good ole'' d=rt. This could get a little tricky at times, but once you have that in place, you can do some cool stuff. Imagine throwing your entire game into slow motion with 1 line of code =]

At any rate, some feedback on the article would be great, and I''ve got some more on the way, namely a tutorial on a decent particle system, and hopefully a new timing article.

-Lutrosis
-Lutrosis#define WHOOPS 0class DogClass {public: CDog() { printf("Ruff!"); } Run() { printf("Run!"); } Crash() { printf("%d",100/WOOPS); }};DogClass CDog;CDog.Run();CDog.Crash();

This topic is closed to new replies.

Advertisement