Advice With Game Loop

Started by
17 comments, last by Pink Horror 10 years, 9 months ago

Have a look here for sample code of how you should be structuring your game loop:

http://gafferongames.com/game-physics/fix-your-timestep/

That's a perfect article to explain this.


One question about this article though, is the integrate() function basically the Update() function i'm trying to use?

Putting it simply, yes. It is using mathematical terminology.


it checks to see if a second in time has passed, and if so, updates the title bar.

What I was saying, is that the code is broken if that's what you're expecting it to do.


updateCount++;

time = SDL_GetTicks();

if(time - timeBase > 1000) {

UPS = updateCount * 1000.0 / (time - timeBase);

FPS = frameCount * 1000.0 / (time - timeBase);

timeBase = time;

updateCount = 0;

frameCount = 0;

}


While this might not be 100% related, this is a more correct way to tell if a second has passed since the last update, regardless of why you're doing it.


the FPS is about 270, while the UPS is about 7000. This seems a little off, but I could be wrong.

This is perfectly normal. While I don't quite see how the two snippets fit together, it is very obvious how in the update loop it is possible to update more than once before it finishes and the rendering begins, and this is the way it should be. If it is updating once or less before rendering, I'd start to show concern, because if the world isn't crawling along, a spike in activity from another process could easily cause the game to border on unplayable.

Advertisement

Ectara, I see what you meant now, sorry if it sounded like I came off as arrogant on that one! I didn't mean to at all, I just thought that what I did wasn't really clear, since you don't have all my code in front of you, just snippets.

Thanks for the replies, I went ahead and spent a day working through the link you guys posted trying to understand it better than I did before. I found a silly mistake that caused those updates and render cycle numbers to be off. The timer's he uses is based on seconds, even though they aren't real timers. They clearly say, "hi_res_timers_in_seconds", and no matter how many times I read it, nothing clicked. I finally saw it about two or three hours ago and got it fixed. Now my Update() runs properly at about 60 times per second, or whatever I set it to!

I only have two more questions, and i'll post my current loop below. First, will this loop run the update function on most computers the same amount of times? I know if a computer can't handle it, the frame rate will drop, which is expected. But will the game logic run at the same speed even when the frames drop? I probably sound like a broken record, and for a 2D game it probably won't even be a huge problem, but if i'm going to learn something, I want to learn it right :P

Second, i'm not sure if this is based on my UPS calculations, or the game logic itself, but my UPS runs between 59.99xxx and 60.00xxx, where the xxx are any numbers, usually the same each time it switches. Is this really a huge problem, or can I ignore that? It switches between the two constantly, and i'm not sure if that is going to effect my game logic. I set up a test where each update loop i have an x variable that increases by 1 each pass, so I could see how many times per second it updates. In theory, it should update by 60 each time if i'm running the update loop 60 times per second. It does a pretty good job of that, but every few seconds it will increase by 61 or so, meaning sometimes Update() will run an extra time. Will this end up being a huge problem, or is it normal and I should just ignore it?

Thanks for all the help, I really appreciate the time and effort you guys are putting in to assist! :)

Here's my code, I grabbed the timer function online somewhere, as I was having some trouble grasping the QueryPerformanceCounter() function.

Main Loop:


const float dt = 1.0f / 60.0f;  //60 Would be desired updates per second

float currentTime = game->Time();  //Get the current time.
float accumulator = 0.0f;

while(game->IsRunning())
{
while(SDL_PollEvent(&e)) 
{
//Exit the game if the user hits the 'X' button on the window.
if(e.type == SDL_QUIT)
game->SetIsRunning(false);
} 

//Game update, render loop 

float newTime = game->Time();
float frameTime = (newTime - currentTime); //Convert to seconds
if(frameTime > 0.25f)
frameTime = 0.25f;
currentTime = newTime;

accumulator += frameTime;

while(accumulator >= dt)
{
game->Update();
accumulator -= dt;
}

game->Render();

} // End main loop

Timer Function:


//hi res timer in seconds
float Game::Time()
{
    static __int64 start = 0;
    static __int64 frequency = 0;

    if (start==0)
    {
        QueryPerformanceCounter((LARGE_INTEGER*)&start);
        QueryPerformanceFrequency((LARGE_INTEGER*)&frequency);
        return 0.0f;
    }

    __int64 counter = 0;
    QueryPerformanceCounter((LARGE_INTEGER*)&counter);

return (float) ((counter - start) / float(frequency));
}

UPS/FPS Tracking:


updateCount++;
	time = Time();
	UPS = updateCount / (time - timeBase); 
	FPS = frameCount / (time - timeBase);

	//Set the title each second
	if(time - timeBase >= 1) {
		timeBase = time;
		updateCount = 0.0f;
		frameCount = 0.0f;

		std::stringstream strs;
		strs << "FPS: "<< FPS << " UPS: " <<  UPS;
		std::string temp = strs.str();
		char* theTitle = (char*)temp.c_str();
		//Set window title to fps
		SDL_SetWindowTitle(mWindow, theTitle); 
	}

You might want to consider a game that sleeps or waits for vsync instead of rendering as many times as possible. It's also a little better, I believe, to track performance in time, not FPS. Knowing your update is right at 60 UPS is nice - at least you know you have corrected your game loop now. Knowing it takes 12 ms on average, maybe with 18 ms spikes, is nicer. (I understand your update is nearly empty as of now, this is just an example.)

You won't get to be one of the guys here who talks about his awesome 4000 FPS game, but you might keep your CPU's fan from getting too loud.


But will the game logic run at the same speed even when the frames drop?

Nope, it will pretty much never update at the same speed. However, this is not a bad thing, and is what is supposed to happen. This distinction may just be me being pedantic, though. A fast computer will execute the game loop in very little time, whereas a slow computer may take longer. However, the faster computer will execute the game loop more times, accumulating time until it is time to update the world. The slower computer will accumulate much more time between iterations, and perform more updates per iteration. Technically, the logic is done at different rates for each machine. The important part is that they each update the world proportionately to the amount of time that has passed in fixed time steps.

In summary:

Fast computer iterates through loop many times, slow computer iterates through loop few times.

Both of them update the world the same amount, by being based on time in discrete time steps.

For a very silly analogy, Alice and Bob are asked to get six eggs from the market, and return in one hour with exactly six eggs. Alice can make it to the market and back in 20 minutes, whereas Bob takes a full hour to go to the market and return. Alice, wanting to show off and be flashy, runs back and forth from point to point, taking two eggs in her basket at a time, and bringing them back. Bob takes his time, and grabs all six eggs, putting handfuls of two in his basket at a time, in one trip in order to make the deadline. After one hour, both return, and each are holding their six eggs.


Second, i'm not sure if this is based on my UPS calculations, or the game logic itself, but my UPS runs between 59.99xxx and 60.00xxx, where the xxx are any numbers, usually the same each time it switches. Is this really a huge problem, or can I ignore that?

The most likely cause is floating point imprecision, and can likely be safely ignored, depending on what you will use your UPS calculation to accomplish. You can likely get away with rounding or truncating for display purposes.

You might want to consider a game that sleeps or waits for vsync instead of rendering as many times as possible. It's also a little better, I believe, to track performance in time, not FPS. Knowing your update is right at 60 UPS is nice - at least you know you have corrected your game loop now. Knowing it takes 12 ms on average, maybe with 18 ms spikes, is nicer. (I understand your update is nearly empty as of now, this is just an example.)

You won't get to be one of the guys here who talks about his awesome 4000 FPS game, but you might keep your CPU's fan from getting too loud.

Running at 4000+ FPS is definitely not a goal, just something it's doing. I'm not limiting it for now, just because I don't really have a need to aside from less cycles. Once I start adding things to the game, i'll see how they effect the FPS, and if it's capped, I won't know how it effects it unless it drops below my target. Then, when i'm confident that I won't be adding anything else that may significantly lower my FPS, I can work on capping it. My logic isn't currently based on FPS, but the amount of Update() passes per second, which I have set to 60 right now.

Also, I believe using any kind of Sleep() function when not explicitly programming multiple cores is bad. That's just what i've picked up over the years of learning, so until I get into that area if need be, I want to stay away from anything that puts the thread to sleep for now. I appreciate the suggestions though, definitely gives me things to think about!

Now, Ectara, I haven't really implemented any kind of time based logic, and I think that using a semi-fixed time step loop like this one, I read that it isn't necessary all the time. I could be way off, as I've only really spent the last few days deeply researching this. I came from XNA, which basically handled all this stuff for me. I did use ElapsedGameTime in XNA, which I think I can easily use here. That time function I used should keep track of the total time the game has been running, and if not, I can always use the less-precise SDL_GetTicks(). But say I was moving a sprite, and had something like this. This obviously wouldn't be all the logic, but I think it gets my point across:

SpritePositionX = 0

SpriteSpeed = 5

So, since I tried to get the updates to go the same amount of time (hence semi-fixed step, which is how http://gafferongames.com/game-physics/fix-your-timestep/ described it), each update if I were to move, I would do:

SpritePositionX = SpritePositionX + SpriteSpeed.

I know with game time based updating (which i'm not saying i WON'T use), since i'm trying to get Update() to run so many times per second, wouldn't it move 5 pixels each pass, which would be 60 x 5 pixels per second? I tried passing both dt and t to my Update method, and when playing around with variables increasing by 1 * some game time, I couldn't find a difference in increasing it by a static amount, like I used with SpriteSpeed, and increasing it by say, SpriteSpeed * gameTime. I understand the concept, but I can't really figure out if implementing it in my current design is necessary.

I could definitely be wrong on all of this, down to the entire game loop itself. Definitely not saying i'm right at all on anything, which is why i'm coming to you guys for help! Should I go by game time, which I think would look something like this:

Update(gameTime)

{

GetLastGameTime;

GetCurrentGameTime;

Find the difference;

Calculate logic based on difference;

}

I'm assuming based on your comments above that the game loop I have now will work well, (I hope!), so I think any changes I make come down to whether to update my logic based on time or Update() passes.


I know with game time based updating (which i'm not saying i WON'T use), since i'm trying to get Update() to run so many times per second, wouldn't it move 5 pixels each pass, which would be 60 x 5 pixels per second?

To do this you'd either move units_per_second / 1000 * ms_elapsed at every update, using coordinates with fractional parts, or constrain your updates per second to match your rate of movement's unit of time. The first would be used in most real-time games; even rounding or truncating to integer co-ordinates for calculations works in some applications. The second would be used in more controlled environments; revisiting the Pokemon analogy, the game simply ran as fast as it could, and the amount to move per update is largely decided based on how many times it could update per second, with the load placed on it.

Alright i'll have to try both options out and see how they work, preferably across multiple pieces of hardware. Thanks for all the help, especially Ectara! It's hard to believe some random guy across the interwebs appreciating much, but believe me, I do :)

Alright i'll have to try both options out and see how they work, preferably across multiple pieces of hardware. Thanks for all the help, especially Ectara! It's hard to believe some random guy across the interwebs appreciating much, but believe me, I do smile.png

It's what makes it all worthwhile. :)

Running at 4000+ FPS is definitely not a goal, just something it's doing. I'm not limiting it for now, just because I don't really have a need to aside from less cycles. Once I start adding things to the game, i'll see how they effect the FPS, and if it's capped, I won't know how it effects it unless it drops below my target. Then, when i'm confident that I won't be adding anything else that may significantly lower my FPS, I can work on capping it. My logic isn't currently based on FPS, but the amount of Update() passes per second, which I have set to 60 right now.

Also, I believe using any kind of Sleep() function when not explicitly programming multiple cores is bad. That's just what i've picked up over the years of learning, so until I get into that area if need be, I want to stay away from anything that puts the thread to sleep for now. I appreciate the suggestions though, definitely gives me things to think about!

Sleeping without multiple cores is bad? How'd you pick that up? Sleep is good! Sleep means other programs can run. If you only have one core, you need sleeping or waiting of some kind even more than the multicore system. People don't just run one executable at a time these days. There are all sorts of background tasks that need that CPU. Good programs don't hog the CPU.

This topic is closed to new replies.

Advertisement