Archived

This topic is now archived and is closed to further replies.

RegularKid

Game timing...Please help!

Recommended Posts

I have asked this question before but didnt get the response i was looking for. Heres my original post: "Ok, here''s my problem: I''ve seen all these articles on frame rate independent movement that take a velocity and adjust it based on the frame rate. For example, if my target speed is 50fps and the target velocity is 2, then on a machine that is running at 100fps the velocity would be 1 and on a machine that is running at 25fps the velocity would be 4. Get it? OK, thats all perfect for constant velocities, but what about velocities that aren''t constant. For example, let''s say that I have a mario-style side-scroller and when my guy jumps, his vertical velocity starts at -4 and I increment that every frame so that it goes -4, -3, -2, -1, 0, 1, 2, 3, 4 and then he''s back on the ground again. But if the frame rate drops to half the target speed then the velocities look like this: -8, -6, -4, -2, 0, 2, 4, 6, 8. and if the frame rate is double the target speed then the velocity of the guy looks like this: -2, -1.5, -1, -0.5, 0, 0.5, 1, 1.5, 2. See my problem? Any time that the velocity is not constant, things will get messed up. I will appreciate any help on this. Thanks!" I got some help from this post but i need some more. can someone give me some code to show me how to remedy my problem. I know that i need to perform the logic based on how many seconds have elapsed rather than do it every frame, but i''m not exactly sure how to do this. Thanks! Oh yeah, heres the response that some gave me that was the most help: "When you switch to frame rate independent movement, you can''t change variables by a fixed amount each frame. Everything has to be relative to the same real time clock. Instead of modifying the velocity based on how many frames have passed, you instead want to modify it based on how many seconds have passed." THANKS!

Share this post


Link to post
Share on other sites
Obviously you need to use the timGetTime() function. This function returns a DWORD saying how long windows has been running. Have 2 DWORD variables, I will call them a and b. Do a = timeGetTime() at the beginning of the game. When you get to ur physics functions, make b = timeGetTime(). The difference between b and a will be the time elapsed, in milliseconds. Update the velocities an positions accordingly. Make the a = timeGetTime again. You will need winmm.lib and mmsystem.h .

Share this post


Link to post
Share on other sites
When I was first tackling this problem with my sidescroller, I realised that I wouldn''t be able to even use the frames per second value to adjust my velocities/ calculate motion. Doing it according to the "system time", however, worked out perfectly for me. And here is EXACTLY how I did it:

Firstly, there''s the little problem of knowing just how much time your working with. amish1234 is right -timeGetTime() is invaluable for this- it returns your computer uptime, in milliseconds. Here''s the function I wrote, called at the beginning of each engine cycle, to calculate "TickTime"- the number of milliseconds worth of gameplay to take into consideration.
  //-----------------------------------------------------------------------------

// Name: Tick()

// Desc: Calculates how much time has elapsed since the last "game tick"

//-----------------------------------------------------------------------------

void GenusGFX::Tick()
{
// NOTE: every variable here is a class member of type "unsigned long" (or DWORD, IYP)

// Get the current system Uptime in milliseconds

Now = timeGetTime();

// Sanity check: only calculate the time delay for this tick if the last time checked

// happened in the past.

// (the dword millisecond count loops every 49.71 days, but hey- there''s still a

// chance someone has kept windows from crashing that long!)

// ... a very small chance, but a chance nonetheless. ;)

if(LastTick < Now)
TickTime = Now - LastTick;
else
// If we''ve looped, calculate motion for a couple miliseconds. (Just so this tick isn''t a COMPLETE

// waste. :)

TickTime = 2;
// Let LastTick remember for next time.

LastTick = Now;
}

I use that in my sidescroller to keep track of how many milliseconds (usually 20-30 when running on my PIII 667mhz 128 meg Win98 w/ Ge256 32meg) have passed since the last tick (or "frame", if you prefer). That works out to an average of about 40 frames per second in map edit mode, around 80fps running around a map with no active enemies. My characters coordinates "posx/y" are floats ("1.0" == 1 tile width), and velocities "velx/y" are also floats ("1.0" == 1 tile/millisecond). And, knowing that, here is the incredibly convoluted code to calculate where an "entity" (the player) is going to be standing when this tick is over:
  
//-----------------------------------------------------------------------------

// Name: UpdateCharacter()

// Desc: Moves active entity pointed to by Entity* CurrentEntity

//-----------------------------------------------------------------------------

void GenusGFX::UpdateCharacter()
{
// Use the velocity (float velx) times time (unsigned long TickTime)

// to adjust our Entity''s position. (just add it!)

CurrentEntity->posx += CurrentEntity->velx * TickTime;
}

Sorry for such a long post, all... but I hope that helps. Any comments are welcome. Newbie questions are welcome as well. What little I''ll admit to knowing I''ve learned because someone took the time to help pound it into my head- I''m more than willing to return the favor. If anyone would like to see what my actual UpdateCharacters() looks like (caps, collision detection, gravity- all glorious 300 lines of it), you can find semi-current copy of my game here. It''s the whole project directory, developed using MSVC++ 6.0 prof, with DirectX 8.0. (And if you can help me figure out how to get my textures right, I''m all ears!) Thank you for your time.

---email--- Tok ----surf----
~The Feature Creep of the Family~

Share this post


Link to post
Share on other sites
To simplify Tok''s example a little, use floats instead of ints to record position and velocity/acceleration. Then you could reduce the time frame from seconds to miliseconds for these values by dividing by 1000. So if your velocity is 10 pixels/s, then after reduction it becomes 0.001 pixels/ms. Then at the start of each frame, you see how much time has passed since the last frame and multiply that by the result value and add that velocity to the position. So if you are rendering 100 fps, then 10 ms has passed, so 0.001 x 10 = 0.01. So unless someone is running 1000fps, you will be safe with these calculations. Here''s some code to help clear it up:
  
//take the velocity you want to use and

//divide by 1000 to get velocity per milisecond(ms)

//calculate this every time a velocity is set.

velMS=vel/1000;

//game loop

//get time

newTime=getTickCount(); //or whatever time funciton you use

change=newTime-lastTime;
lastTime=newTime;

xPos=xPos+(velMS*change);

//draw scene



I hope that gets my point across. You just have to set all your velocities to miliseconds, instead of seconds. This way you don''t even have to know what the frame rate is, it will be guarunteed to work with all frame rates up to 1000fps.

---
Make it work.
Make it fast.

"Commmmpuuuuterrrr.." --Scotty Star Trek IV:The Voyage Home

Share this post


Link to post
Share on other sites
Thanks. but i still havent figured it out yet. i already know how to calculate the speed factor to multiply my velocities by, but heres the real question...

on a constant velocity, everything is fine, but say i update a characters velocity every frame. this is where things get screwed up. say my jumping character starts out his jump with a vertical velocity of -4 and i increment that value every frame so that it will go -4, -3, -2, -1, 0, 1, 2, 3, 4 and then he''s back on the ground again. but on a machine that is running at half the target frame rate the velocities will be -8, -6, -4, -2, 0, 2, 4, 6, 8. I don''t know how to better explain my problem. can someone help me?

Share this post


Link to post
Share on other sites
This is not only a matter of speed and distance, but also of acceleration. Each frame, the speed of the jumping guy gets changed, in a way that after 4 seconds his velocity is 0, and after 8 seconds his velocity is 4 again and he hits the floor.

So each frame, you''ll have to calculate this:

s = 0.5 * a * t^2 + v * t

a is a constant value (in this case 1, this means the velocity increases with 1 m/s per second). v is the velocity of the guy when he starts jumping (-4), and t is the time that has elapsed since he has started the jump.

By the way, maybe it''s a better idea to just use a single velocity. I know that that''s not the way gravity works, but if I remember correctly, the mario games also used a single velocity.

Share this post


Link to post
Share on other sites
I understand what you are saying, but you should not be thinking in frames. That is why I say to use floats. This is also where acceleration comes in. What you need to decide is how many seconds air time you want your character to have, not how many frames. Then you have a gravity acceleration of say 2 pixels per second to the bottom of the screen(+y). Then you slice it down to miliseconds: .0002 pixels/ms. Now when your guy jumps off, he will have a starting velocity(in physics it would be called instantaneous acceleration) of say -10 pixels/ms vertical(Y) and 10 pixels/ms horizontal(X)(this gives you a nice arc). You slice both velocities down to miliseconds, giving: -0.001 pixels/ms(Y) and 0.001 pixels/ms(X). You then apply your gravity constant to the vertical velocity each time through the game loop, making sure to take into account how many miliseconds passed. At the start of your game loop you find out how many miliseconds have passed(called the delta) and multiply this by all velocities and accelerations, then apply the accelerations to the velocities(These last two steps could be done in either order). Then add the velocity to the position. Then when you draw the position, cast the float to an int(with our without rounding) and your character will move the same on all computers, regardless of frame rate. This way, you should not worry about updating in a certain number of frames, but just finding out how much time has passed since the last time you updated.

In theory, this should work. Give me a couple of days to work it out, and I will give you an example. Unless someone here can give you one sooner.


---
Make it work.
Make it fast.

"Commmmpuuuuterrrr.." --Scotty Star Trek IV:The Voyage Home

Share this post


Link to post
Share on other sites
THANK YOU CAPTAIN JESTER!!!! I finally got the answer i was looking for and it was explained to me very well!! I''m the kind of person who needs stuff pounded into their head in very simple terms before i finally grasp it! Thanks again for all your help everyone!

Share this post


Link to post
Share on other sites
I had a little mistake in my last post. You must divide your velocity by 1000 to convert it to miliseconds, but you have to divide your acceleration by 1,000,000(1000 squared) to make it work. Because acceleration is in pixels/second^2. I finally worked it out. Here is an example:

    
#include <stdio.h>

const int fps=100; //frame rate

const int baseTime=1000; //number of miliseconds in 1 second

int time=0;

int getTime(void);

int main() {
float yPos=0.0f;
float vel=(4.0f/(float)baseTime);
float accel=(-1.0f/((float)baseTime*(float)baseTime));
float lastTime=0.0f,curTime=0.0f,delta=0.0f;

printf("\nyPos\t\t\t\tvel");
do {
printf("\n%4.4f\t\t\t\t%4.4f",yPos,vel);
curTime=(float)getTime();
delta=curTime-lastTime;
lastTime=curTime;
yPos+=(vel*delta);
vel+=(accel*delta);
} while(yPos>0.0f);
return 0;
}

//function to simulate time elapsed at a given framerate

int getTime() {
time+=(baseTime/fps);
return time;
}


---
Make it work.
Make it fast.

"Commmmpuuuuterrrr.." --Scotty Star Trek IV:The Voyage Home

Edited by - CaptainJester on January 16, 2002 10:30:49 PM

Share this post


Link to post
Share on other sites
quote:
Original post by CaptainJester
I understand what you are saying, but you should not be thinking in frames


I was already doing that Well, obviously, it wasn''t very clear what I said but anyway, I perfectly agree with you.

Share this post


Link to post
Share on other sites