Sign in to follow this  

Breakout clone & smooth ball movement & fixed time step loop with varying framerate

This topic is 3193 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 programming a breakout clone and i'm having some unknows regarding random jumps in the animation of the ball. I'm using a fixed time step loop and also do interpolation with the remaining time from every update. I update the ball position as follows: pos = pos + dir*speed*dt; and at render i interpolate the position like so: pos = pos + dir*speed*dt*interpolator. Every now and then i see sudden jumps in ball position and can't really understand why is this happening. Of course it may be from a variety of reasons but i like to think that the code is fairly optimized(using batch rendering and fast collision detection routines using spatial hashing for the broad phase). The application is fairly big (>500kb of code) and i'm wondering if i may be causing a lot of cache misses. So what is the secret of programming a breakout clone with smooth ball animation on a decent processor (2ghz single core) ? :D [Edited by - Deliverance on March 17, 2009 1:15:32 PM]

Share this post


Link to post
Share on other sites
Quote:
Original post by Hodgman
What is the value of "interpolator"?


It is in the range 0..1 and it is calculated as follows:

while (accumulator >= fixedDt)
{
UpdateGame(fixedDt);
accumulator -= fixedDt;
}

interpolator = accumulator / fixedDt;

Also a little correction. at render time i do:

posToRender = pos + dir*speed*dt*interpolator.

not increment the position and use that modified version in further calculations.

Share this post


Link to post
Share on other sites
I could be looking at this wrong but all you should need is:

pos += speed*dt

speed: You can eliminate direction by just negating this value whenever it hits a wall

dt: time since last update

Why wouldn't that work for this situation?

Share this post


Link to post
Share on other sites
Quote:
Original post by DarkZlayer
I could be looking at this wrong but all you should need is:

pos += speed*dt

speed: You can eliminate direction by just negating this value whenever it hits a wall

dt: time since last update

Why wouldn't that work for this situation?


I'm separating between the direction and the magnitude of the direction to easily change travelling speed(ex: speed = 2.0). This way i'm not changing the direction which remains constant.

Share this post


Link to post
Share on other sites
Your equation is fine, unless there is a serious bug in the interpolator, which you should do some simple test to verify its working correctly.

The problem with fixed timesteps is that games rarely go at a fixed timestep. Even with the simplest game, running at 60fps there isn't any guarantee that some blocking processes doesn't hog the CPU causing it to go less than 60fps. When that happens your game will stutter, the human eye is quite sensitive to changes in update rates, more so than the update rate itself. A game going at 15fps consistently is smoother than one going inconsistently at 30fps.

You'll need to take into account when the game stutters for whatever reason and if so update multiple fixed timesteps until you catch up to the realtime passage between the frames. Be sure to keep track of the time remainder to add to the next frame time if there is any. That way your game will try to run realtime even though it is using a fixed timestep. If for whatever reason you can't catch up to realtime, just update as much as u can that frame and try to catchup over time.

Good Luck!

-ddn

Share this post


Link to post
Share on other sites
I think i have nailed the reason why i see the annoying stuttering on the screen.
First of all here is the fixed time step loop i'm implementing:


void CGameEngine::Update()
{
float amount = 1.0f / 400.0f;

static float accumulator = 0.0f;

accumulator += timer.GetTime();
timer.Reset();

numUpdates = 0;

while (accumulator >= amount)
{
UpdateSimulation(amount);

accumulator -= amount;

numUpdates++;
}

interpolator = accumulator / interpolator;
}






TESTING ON: Xeon quadcore, 2.1ghz, nvidia geforce 8500 GT

I did some profiling with VSync on and saw that on my system numUpdates is 6 or 7 most of the time. But quite often it burst to a value greater than 10 or 20, let's say 16 or 23. It is very clear that the ball position would be updated proportional to numUpdates.
Now the fps is high enough, without VSync off it is over 2000 FPS.

If i turn VSync off the the choppiness is gone: looking to numUpdates it's says that it is between 0-2 all the time. The choppiness is gone.

Of course i have a constant there: amount = 1/400. How should i choose this value and how should i solve the issue in the case where VSync is off and numUpdates varies often with big amounts? Also the issue with the choppy animation appears with vsync off on a lower spec hardware; this means that i found a good parameter set that works well only on the system mentioned above. What about generality? It seems that the fixed time step loop is not enough here. :D

EDIT: Also the interpolation trick does not have much to say because of the very small constant time step.

Share this post


Link to post
Share on other sites
Your time function could be to blame, I hear timeGetTime (if your using the win32 function of the same name ) has a accuracy of 1-10 ms! which if your running at 60fps, can give u variance of 6-60% between each frame, stutter time!

You can use the performance counters for higher accuracy but they too have a strange bug where they would return anomalous values occasionally, so you need to verify the values you get back.

See site for overview:

http://www.geisswerks.com/ryan/FAQS/timing.html

Good Luck!

-ddn

Share this post


Link to post
Share on other sites
Quote:
Original post by ddn3
Your time function could be to blame, I hear timeGetTime (if your using the win32 function of the same name ) has a accuracy of 1-10 ms! which if your running at 60fps, can give u variance of 6-60% between each frame, stutter time!

You can use the performance counters for higher accuracy but they too have a strange bug where they would return anomalous values occasionally, so you need to verify the values you get back.

See site for overview:

http://www.geisswerks.com/ryan/FAQS/timing.html

Good Luck!

-ddn


I see, though i'm using sfml for timing and sfml uses internally high precision timers(if available). To be sure i'm using such timers i've written this piece of code:


{
sf::Clock c;

float a = 0.0f;
c.Reset();
for (int g=0; g<10000000; g++)
{
float d = sqrtf(g);
a += d*d*d;
}
char mess[256];
sprintf(mess, "time=%f a=%f", c.GetElapsedTime(),a);
MessageBox(NULL, mess, mess, MB_OK);
}




The message box proudly says: time=0.269663 a=1274143319414508570.000000 (hope i did copy correctly in case someone tries the code :D ).

So i don't think it's the timer's precision.

[Edited by - Deliverance on March 17, 2009 8:27:58 PM]

Share this post


Link to post
Share on other sites
Your timer might be to blame then, if it's using HPC behind the scenes. See this page for discussion:

http://www.virtualdub.org/blog/pivot/entry.php?id=106

Try this code to see if it's possibly the case, I didn't test this, but its just your code running in infinite loop checking if the results from timeGetTime vs c.GetElapsedTime agree over a long running period. You can adjust the number of iteration of the loop to increase the step time.


{
sf::Clock c;

while (1)
{
float a = 0.0f;
DWORD start = timeGetTime();

c.Reset();

for (int g=0; g<100000; g++)
{
float d = sqrtf(g);
a += d*d*d;
}

float dur = c.GetElapsedTime();
float dur2 = (float)(timeGetTime()-start)/1000.0f;

if (abs(dur-dur2)>.01)
{
char mess[256];
sprintf(mess, "detected anomalous time error greater than .01 sec");
MessageBox(NULL, mess, mess, MB_OK);
}
}
}



Good Luck!

-ddn

Share this post


Link to post
Share on other sites
Quote:
Original post by ddn3
Your timer might be to blame then, if it's using HPC behind the scenes. See this page for discussion:

http://www.virtualdub.org/blog/pivot/entry.php?id=106

Try this code to see if it's possibly the case, I didn't test this, but its just your code running in infinite loop checking if the results from timeGetTime vs c.GetElapsedTime agree over a long running period. You can adjust the number of iteration of the loop to increase the step time.

*** Source Snippet Removed ***

Good Luck!

-ddn


Ran the code for a few minutes and there was no popup warning. Guess the timer's fine. The only problem is the variable framerate that introduces seldom big times between frames. I don't know how to deal with varying framerates in a fixed time step loop. :D

Share this post


Link to post
Share on other sites
Thinking about it I know the issue. Your updating way too much per frame. Normally games update 1x per tick. A tick is defined by the desired update rate, which in your case is probably 1/60 sec. So the code should be like this.



void CGameEngine::Update()
{
const float updateRate = 60.0f;
const float tick = 1.0f/updateRate; //note the new tick measure

static float accumulator = 0.0f;

accumulator += timer.GetTime();

timer.Reset();

// NOTE: you have to sleep off the excess time if your updating too fast for
// ur desired update rate, otherwise you'll just burn CPU waiting to update a single tick.

if (accumulator < tick)
{
//NOTE: we add 1 because a sleep value of 0, gives a non-deterministic sleep time, which isn't
//desirable, time is converted into miliseconds, for Sleep.
float sleepTimeMs = ((tick-accumulator)*1000)+1.0f;
Sleep((int)sleepTimeMs);
}
else
{
//this should occur at a near constant rate of 1 update, occasionally spiking if the interval is
//longer than a tick.

numUpdates = 0;

while (accumulator >= tick )
{
UpdateSimulation(amount);

accumulator -= amount;

numUpdates++;
}
}

interpolator = accumulator / interpolator;
}




That should do it, this is untested but you should get the gist of it. Update at a desired update rate, sleep off excess time to give other threads CPU and reduce your own CPU usage. Your update rate affects all your simulation, so you might need to recalibrate forces and velocities to fit within the new update rate.

Good Luck!

-ddn

Share this post


Link to post
Share on other sites
Quote:
Original post by ddn3
Thinking about it I know the issue. Your updating way too much per frame. Normally games update 1x per tick.
-ddn


You are correct, that's what's happening.
The issue with your code though, is, presuming we are running single threaded, that Render() will also be called at updateRate, rate per second and this is undesirable. The interpolator would have no use now, his sole purpose was to predict what update will do next frame all this happening while update wasn't doing any updates(accumulator being < updateRate)

Share this post


Link to post
Share on other sites
Yah you can decouple the rendering and update rates, that's fairly common. Games can render at floating rate ( anywhere from 60-15 fps ) while the simulator updates at a fixed rate of 10 ups for instance. Just adjust the sleep time to be your desired framerate vs update rate and that should do it. You still want to have a consistent framerate even though your game can run at 1000fps, it really doesn't matter when you have a vsync of 60hz. If you enable vsync you're application will be effectively blocked during the vsync and you're just burning CPU then which could be used otherwise, this might be a DirectX setting where it will return immediately from flush instead of blocking, I don't remember.

Good Luck!

-ddn

Share this post


Link to post
Share on other sites

This topic is 3193 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