2D Engine Main Loop - Need advice

Started by
22 comments, last by BeerNutts 11 years, 11 months ago
Fixed everything with floats. Movement still feels a little bit off. Not sure why...

Anyone willing to test and let me know what you feel.

Feels a lot smoother now compared to what it used to be but a weird issue seems to involve the character moving faster in the open part of the level. It check collision less there when aligned with the grid but I thought the whole purpose of this kind of loop was to remove temporal antialiasing (which is has) and make it steady rendering throughout. How would I modify my main loop to work at a fixed frame rate?
Advertisement
EDIT: Wait, I see a major problem. SDL_GetTicks returns the time in Milliseconds. You are assigning it to a newTime, and subtracting. The times are going to be large numbers, not fractions. If you want to use SDL_GetTicks(), I'd suggest doing this:

double currentTime = (double)SDL_GetTicks()/1000; // converts to time in seconds, so 8 milliseconds is .008 seconds


Same with other places you use SDL_GetTicks in your code.




// Time variables
double t = 0.0;
const double dt = 0.01;
double currentTime = SDL_GetTicks();
double accumulator = 0.0;

// Run until the user has requested a quit
while ( !quit )
{
double newTime = SDL_GetTicks();
double frameTime = newTime - currentTime;

if ( frameTime > 0.25 )
frameTime = 0.25;
currentTime = newTime;

accumulator += frameTime;

// Loop until we are ready to render, updating as we go
while ( accumulator >= dt )
{
update( dt );
t += dt;
accumulator -= dt;
}

// Get the time we use for interpolation
const double alpha = accumulator / dt;

// Interpolate the values
player.interpolate(alpha);

// Render the scene
renderScene();

}
return 0;
}



I'm confused just trying to understand what your loop is doing. It doesn't make sense to me. What are you trying to accomplish? Explain each step, and what you think it's supposed to be doing. I don't think it's doing what you hope.

I would take a different approach. In my original game, I had frame-rate independent movement, but I updated the screen as often as possible.

The basic premise was this (using your SDL as an example):

// subtract .01 seconds so 1st loop has some elapsed time
double lastTime = (double)SDL_GetTicks()/1000 - 0.01;
double currentTime;
double elapsedTime;

while(!bQuit) {
currentTime = (double)SDL_GetTicks()/1000;
elapsedTime = currentTime - lastTime;
lastTime = currentTime;

Player.HandleInput();
Environment.Update(elapsedTime);
Items.Update(elapsedTime);
Enemies.Update(elapsedTime);
Player.Update(elapsedTime);
Render();
}

// My player Update would be this
void Player::Update(double elapsedTime)
{
// XLocation, YLocation, XVel, and YVel are all doubles
// XVel and YVel are in pixels/second
XLocation = XLocation + XVel* elapsedTime;
YLocation = YLocation + YVel * elapsedTime;

}

The elapsed time is the time it takes to handle the player's input, update everything in the game, and render. Everything will move the correct distance regardless how long the frames are.

I would suggest you look at using this to begin with. You can then extend it as you see fit, but get this working, then try others if you want.

Since you aren't using a physics engine, this is fine. Once you move to using physics engine for simulations, you should fix your timesteps.

Render just renders at the objects' XLocation and YLocation.

Just my suggestion, but, whatever you do, good luck.

My Gamedev Journal: 2D Game Making, the Easy Way

---(Old Blog, still has good info): 2dGameMaking
-----
"No one ever posts on that message board; it's too crowded." - Yoga Berra (sorta)

He is using fixed time steps (just look at JTippets post), which is a common way to avoid all the potential issues of just scaling everything by frame time.

Why? Think of throwing a rock and just updating velocity and position each frame. It would follow two very different paths on different machines. In a game it can make the different between throwing a grenade into a window or hitting the wall. Even the old X-Wing games had problems that led to fixed time steps. What happened? Because of issues like that, the "replay" of a mission would sometimes be completely different from what actually happened. Inconsistent time steps caused a missile to miss and suddenly there was a ship that should have been gone.

In short: update your physics in fixed steps and don't allow an arbitrarily large or tiny delta time. Unless you don't care about results being somewhat deterministic (which is often the case if your game is offline, non-competitive and has no replay function).

Of course it might seem like overkill for bomberman, considering there should be only simple and linear movement involved. Yet, it's probably better to get used to it on simple projects. Decouple physics and rendering. Generally there is absolutely no reason to run physics updates at maximum rate (ie. every single frame).
f@dzhttp://festini.device-zero.de

He is using fixed time steps (just look at JTippets post), which is a common way to avoid all the potential issues of just scaling everything by frame time.

Why? Think of throwing a rock and just updating velocity and position each frame. It would follow two very different paths on different machines. In a game it can make the different between throwing a grenade into a window or hitting the wall. Even the old X-Wing games had problems that led to fixed time steps. What happened? Because of issues like that, the "replay" of a mission would sometimes be completely different from what actually happened. Inconsistent time steps caused a missile to miss and suddenly there was a ship that should have been gone.

In short: update your physics in fixed steps and don't allow an arbitrarily large or tiny delta time. Unless you don't care about results being somewhat deterministic (which is often the case if your game is offline, non-competitive and has no replay function).

Of course it might seem like overkill for bomberman, considering there should be only simple and linear movement involved. Yet, it's probably better to get used to it on simple projects. Decouple physics and rendering. Generally there is absolutely no reason to run physics updates at maximum rate (ie. every single frame).


Trienco, did you look at his code? He may THINK he's doing fixed timesteps, but he's implemented it incorrectly. And, he's not using any physics in his game, so it's not a requirement. I fully understand having a constant delta for running physics simulations is the best possible solution, but that's not the case here.

I typically have the philosophy of KISS for beginners. Once they are more comfortable making games, they can move to more complex solutions.

My Gamedev Journal: 2D Game Making, the Easy Way

---(Old Blog, still has good info): 2dGameMaking
-----
"No one ever posts on that message board; it's too crowded." - Yoga Berra (sorta)


Trienco, did you look at his code? He may THINK he's doing fixed timesteps, but he's implemented it incorrectly.


Then I'm not seeing it. The only problem I see is with ignoring that SDL_GetTicks returns milliseconds as uint32 and not seconds as double.

Apart from that, what am I missing? He calculates the frame time, caps it just in case, adds it to last iterations remainder, does his updates as many times as fit in and then draws his scene.
f@dzhttp://festini.device-zero.de
Everything you need to make a proper fixed-step implementation can be found here: Fixed-Time-Step Implementation

Particularly you need to pat attention to the Implement with Care section.
#1: Never store raw time in floats or doubles. Only deltas (time since the last update).
#2: Don’t cap the frame rate.
#3: Fix your interpolation. You use “const double alpha = accumulator / dt;” If you were using floats, it should be, “const double alpha = accumulator;”.



L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid


#3: Fix your interpolation. You use “const double alpha = accumulator / dt;” If you were using floats, it should be, “const double alpha = accumulator;”.


It's not that his version is wrong. He is passing a relative percentage of one time step. The real problem I'm seeing is that the called function can only make sense of this value if it is aware of how long a time step is.

Point is: you can't say his alpha is wrong without knowing what his interpolate function is doing (at least "mathematically" a value between 0-1 actually does make more sense than an absolute time value).

I'm not sure where you see him capping the frame rate. He is capping the number of updates per frame, which makes sense. If your game really stalls for such a long time, you don't want it to make a huge jump forward, taking an endless amount of time for the physics update and stuttering along for several frames until it finally caught up. The idea is that the game experience is ruined anyway, if a frame suddenly takes that long. Freezing and then continuing as normal is a much lesser evil than turning the game into a slide show and crashing the player into whatever he was headed for.

Besides: look at JTippets code. I don't think it's helping much if he does an implementation based on something a moderator posts and then having other people say "no, that's wrong" without going into any detail. That's just going to be confusing.
f@dzhttp://festini.device-zero.de

It's not that his version is wrong. He is passing a relative percentage of one time step. The real problem I'm seeing is that the called function can only make sense of this value if it is aware of how long a time step is.

Point is: you can't say his alpha is wrong without knowing what his interpolate function is doing (at least "mathematically" a value between 0-1 actually does make more sense than an absolute time value).

You are correct.



I'm not sure where you see him capping the frame rate. He is capping the number of updates per frame,

You are correct.



which makes sense. If your game really stalls for such a long time, you don't want it to make a huge jump forward, taking an endless amount of time for the physics update and stuttering along for several frames until it finally caught up. The idea is that the game experience is ruined anyway, if a frame suddenly takes that long. Freezing and then continuing as normal is a much lesser evil than turning the game into a slide show and crashing the player into whatever he was headed for.

It doesn’t make sense, or it is somewhat subjective. I certainly would never take that route.
It causes the game to go into slow-motion which in itself causes problems, particularly for network games. The simulation is still stable, but you are still going to crash into that wall. Your game can’t lag behind the server and expect your input to remain real-time. You will still be trying to steer clear of the wall, mashing buttons or pulling back on the stick, even though you have really already crashed, and are just waiting longer to realize it. Stretching out one’s doom and giving him or her false hope of survival would be considered more of an annoying side-effect of game lag.

Offline games don’t need to be so strict on this, but most serious games are built around a fully real-time simulation partly because it just makes sense, but also because it leaves them open to the addition of network play later. Even if they never plan to go online with one game, it only makes sense to build an engine around the idea that it may one day be used to make an online game.



Besides: look at JTippets code. I don't think it's helping much if he does an implementation based on something a moderator posts and then having other people say "no, that's wrong" without going into any detail. That's just going to be confusing.

One of the problems with his reproduction of JTippetts’s code is his use of doubles for storing absolute time.
I write articles covering these kinds of things so I don’t have to repeat them every time. And, frankly, that is something I would encourage him to research on his own anyway instead of just feeding it to him; that tidbit can be found anywhere online, and he should be doing the simple research on his own.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid


It doesn’t make sense, or it is somewhat subjective. I certainly would never take that route.

It causes the game to go into slow-motion which in itself causes problems, particularly for network games.


Actually, it will cause only a single hick up. Look at his cap. If your frames seriously take more than 250ms, the game is absolutely unplayable. Slow motion would require that this happens frequenty in which case no method is ever going to fix anything.

Yes, if there is multiplayer involved then pretending that less time has passed on the client would be a problem. I'd expect the server to override the client state, unless the client is trusted enough to just "believe" it.

Still, for a simple offline game, imagine what happens if the game freezes for a second (because some other application misbehaves or what-not). You would end up doing a ton of physics updates in a single frame. In a complex scene that will take forever, causing the _next_ frame time to be way to large again. You are trading a single long freeze vs. a long freeze followed by prolonged stuttering.

As for doubles, in his current code it would be a trivial change to calculate the frame time before casting it to double. If he was using a library like GLFW he wouldn't even have that option (it always returns time in double). I remember a table listing which type would last how long before precision becomes a problem. While floats are pretty horrible, doubles will still be a decent choice. Especially since SDL returns time since initialization and not 1970, so he would probably have to run his game for days or weeks before noticing the effects.

I'll just blindly quote from Game Programming Gems 4 about doubles for time measuring: "after 80 hours, it's sub-second accuracy is an amazing 0.00000023ms)". I doubt his game will ever be running long enough to notice precision issues.
f@dzhttp://festini.device-zero.de
Hi all. Sorry I ducked out for a bit. The first implementation was a bastardization of the main loop on the article: http://gafferongames...-your-timestep/

To be honest, the physics and calculus nature of his implementation confused me so although I implemented it, I know it was incorrect.

I have since redone the loop using dewitters last game loop (is this also fixed time step), and have gotten good results...

Here's the thing. I am not here to make a quick game, I want this to be a solid engine I can produce a game on, extend and reuse. I know physics will eventually be part of the game and although I don't anticipate writing my own physics engine, I would love to know the actual logic behind it. Because I have been less than clear and confused, and you have all been incredibly helpful in providing articles and source, let me break down my understanding of it and I will ask questions. Again, my apologies.


I will be basing my questions, understand off of: http://gafferongames...-your-timestep/
Please tell me what I have missed. tongue.png
--------------------------

Fixed delta time

So the inherent problem here is the rate at which you update your game. You shouldn't just update the game as fast as possible as that will make programs/games run faster on faster computers and slower on slower computers. Not ideal.

Variable delta time

The logical solution (what I tried as well) is to measure the time it takes for each frame and record the time change (the delta). Then pass this into the physics simulation. This will provide consistent speed but differing physics results on different framerates. Also for some reason, they are keeping track of the total time represented by t, for some reason. Why is this? It is used in the integrate function which I understand to be an integral from Calculus.

Semi-fixed timestep

The solution to this is to ensure the delta time is small and the results will be (nearly) identical every time. If I understand correctly, this loop takes the last frame rate (i think) and then updates the physics continuously, decrementing the frametime until it is zero and finally renders the frame.

FINAL SOLUTION

The final solution is to have a fixed delta time (time between updates) so that your physics are run at regular intervals BUT at the same time, render independently. Again, I am confused by some of his functions like integrate. I assume this is his physics update, just the same as I would update a player's position.

To do this, he caps the delta time which will affect physics at a specific time to avoid the spiral of death.

The next part is where I am confused:


while ( accumulator >= dt )
{
previousState = currentState;
integrate( currentState, t, dt );
t += dt;
accumulator -= dt;
}




When he integrates, what exactly is he updating? How would that apply to moving a character across the screen rather than a calculus function?

Then, when the accumulator is smaller than delta time, he calculates the alpha value which is interpolation?

Then, he interpolated the position of the ball (and in my case, a character) by the value alpha (this makes sense)

My main question about this last loop is about the code I linked above is what exactly is it doing. What does integrate do? Is it just a general physics function? What would I do instead for a simple bomberman clone during that time.

----------------


Thank you again. It's quite funny. I have pretty much everything else coded and yet, my main loop keeps changing. I really appreciate the time you guys have spent helping me. Sorry that I have learned this a bit slow. I will make sure to pay it forward and help others.

Thanks!

This topic is closed to new replies.

Advertisement