Advice With Game Loop

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

Hey everyone, how goes it?

So i've been working on programming a game in C++/SDL2.0 ever since SDL 2.0 release candidate came out. I have spent about 2 days straight of doing nothing except trying to find and implement a game loop I thought would be best for me. Now, I don't mean best ever, I just mean deciding between fixed step and variable step, etc. Basically what I have now is a game loop that calls the Update() function about 60 times per second, while not capping the framerate. I have been thinking about also capping the frame rate to 60, but i'm not sure if that's necessary yet, as all I draw on the screen for now is a blue color. The Update() function will update all of the game logic when it starts to be implemented, while Render() draws everything. Ever since I switched back to C++ from XNA, i've been trying to basically emulate a XNA framework into C++, as that's where I found it to be the easiest.

If it matters at all, i'm planning on making a 2D tile based game. So do you guys mind taking a look at my main game loop, and telling me if I should change it over to a different setup? Here is the code to just the main game loop:


 //Event structure
	SDL_Event e;

	const int UPDATES_PER_SECOND = 60;
	const int MS_PER_UPDATE = 1000 / UPDATES_PER_SECOND;
	const int MAX_FRAMESKIP = 5;
	
	Uint32 elapsedMS = 0;
	Uint32 startTime = SDL_GetTicks();
	Uint32 nextGameTick = SDL_GetTicks();
	int loops;

	int numUpdates = 0;
	int numFrames = 0;


	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	
		loops = 0;
		while(SDL_GetTicks() > nextGameTick && loops < MAX_FRAMESKIP)
		{
			game->Update();
			nextGameTick += MS_PER_UPDATE;
			loops++;
			++numUpdates;
		}

		//Render the current frame
		game->Render();
		++numFrames;

		//Used to display FPS/UPS
		elapsedMS = SDL_GetTicks() - startTime;
		if(elapsedMS % 1000 == 0) {  //Only update once per second
			game->FPS = numFrames / (elapsedMS / 1000.0);
			game->UPS = numUpdates / (elapsedMS / 1000.0);
		}

	} // End main loop

So basically, here are my questions:

1) Is this an efficient game loop? By that I mean, will it update at about the same rate on all computers? It's designed to update the logic at 60 times per second no matter what the computer (usually), but the frame rate will change.

2) Is 60 updates per second too much? Too little?

3) How much should I look into capping the frame rate?

Let me know if you guys see anything you think I should change. I've been wracking my brain over this for 48 hours now, i'm ready to move on! :P Thanks guys!

Advertisement

Quickly skimming over the code sample posted, I can point out one thing: If you want to set two variables to the same time value, don't call the function that returns the time twice in a row. I'm not sure if that was what you were going for, but it is possible for the two to wind up with different values. Best to copy from one to the other.

Additionally, I find it hard to picture how you would get your logic to update precisely 60 times per second on any machine without capping the framerate on machines that run it faster. Machines that can't run it fast enough, however, is tougher. Some people wisely choose to update the game state proportionately to how much time has passed, but not all types of games lend themselves to this kind of intricacy.

All in all, we may need more information.

I planned on doing it based off time, I just haven't got far enough into development to get that far. My main goal now was just setting up the skeleton of the game, which started with getting the frame rate and update rate running at the right rate. What information might you need? There isn't a whole lot more to my game right now aside from initializing SDL, and some empty functions that haven't been put to use yet. Right now, Update() just set's the title of the window to display the FPS/UPS, as I haven't gotten far enough to implement my own bitmap font.

My main goal right now is just to be able to get a steady game loop going. What would you recommend I do with it? I was using this:

http://www.koonsolo.com/news/dewitters-gameloop/comment-page-2/#comments

The third example, modified a few variable names and added code to keep track of FPS/UPS. Let me know what I can do to help you help me! :)


//Used to display FPS/UPS

elapsedMS = SDL_GetTicks() - startTime;

if(elapsedMS % 1000 == 0) {

//Only update once per second

game->FPS = numFrames / (elapsedMS / 1000.0);

game->UPS = numUpdates / (elapsedMS / 1000.0);

}

I'm curious if this part does what I think it does. Do you really mean to check to see if the time taken to update before drawing takes an even multiple of 1000ms, or am I missing something?

The most important thing is what kind of game/application will this be? This might work well for a little side-scroller, but for a bitmap editor, it is a horrible way to do things. For the most part, you absorbed the lesson, but truly, the loop's purpose determines its structure.

IMHO the loop suffers from the following quirks:

1. It makes no sense to render although the state isn't updated, because the rendering produces exactly the same image as the rendering before. (However, it is okay if you expect game->Render() to last longer than the 1/60 seconds in the real game.)

2. The conditional (elapsedMS % 1000 == 0) becomes true in exactly 1 millisecond per second, assuming that SDL_GetTicks has a real resolution of at least 1 millisecond. It is very unlikely that this condition is hit in the way as wanted. (See also Ectara's comment above.)

3. The way you compute FPS//UPS (letting issue 2 aside) gives the average FPS//UPS since the very beginning. This means that over time the shown values become more or less stable. You probably want to show the average of the last n seconds instead (i.e. a floating time window).


//Used to display FPS/UPS

elapsedMS = SDL_GetTicks() - startTime;

if(elapsedMS % 1000 == 0) {

//Only update once per second

game->FPS = numFrames / (elapsedMS / 1000.0);

game->UPS = numUpdates / (elapsedMS / 1000.0);

}

I'm curious if this part does what I think it does. Do you really mean to check to see if the time taken to update before drawing takes an even multiple of 1000ms, or am I missing something?

The most important thing is what kind of game/application will this be? This might work well for a little side-scroller, but for a bitmap editor, it is a horrible way to do things. For the most part, you absorbed the lesson, but truly, the loop's purpose determines its structure.

All that part does is update the FPS and UPS every second, instead of 60 times per second. No need to see the numbers change at a speed when you can't even read them! I think the only thing I need to worry about is this program running the update cycle pretty much at the same speed on all computers. I believe the point of the tutorial I followed was to do just that, and if the user's computer is slow, to shave off some frame rate instead of losing game logic. That was my ultimate goal with my game loop, so I was hoping to have someone tell me whether I actually implemented it right or not.

But yeah, the code you questioned just keeps track of the FPS/UPS and updates it once per second instead of 60, since the game updates 60 times per second. The game is going to be a tile based 2D game like I said, but if you need a visual on exactly how the gameplay might look, think of like Hotline Miami, or even Pokemon games, the way it's a big map where you can move along both x and y planes. I want to use the term top down, but loosely.


All that part does is update the FPS and UPS every second, instead of 60 times per second.


But yeah, the code you questioned just keeps track of the FPS/UPS and updates it once per second instead of 60, since the game updates 60 times per second.

Allow me to rephrase: you are checking to see if the time taken to update before drawing is an even multiple of 1000ms. What that test does is check that the time that elapsed was 1000, 2000, 3000, etc., disallowing 999, 1001, or any other much more likely number. What I'm saying is, if that's truly the code, it's pure luck that it is updating once per second, and the frame rate is likely less stable than it appears, because the frame rate calculation only runs when the tide comes in.


I think the only thing I need to worry about is this program running the update cycle pretty much at the same speed on all computers.

I think this idea has good intentions, but it seems ultimately flawed in the direction it is being considered. One shouldn't strive to have the logic run at the same speed, no matter what the machine's configuration, and magically have the game's rendering not depend on the completion of the game's logic. The focus really should be on accepting that no machine will complete each frame near the target frame rate, and instead keep some sort of accumulator of time passed; when the time has gone over the interval between logic updates by however many frames, draw that many frames, and leave the remainder of time for the next iteration.

In layman's terms, instead of trying to scale the time spent, scale the work you do based on time elapsed. You want the world to move the same rate, even if the game runs at 15fps. Pokemon is a bad example for this; there was only one set of hardware on which it ran, so it was simply designed to run essentially as fast as possible, with very minimal timing code to keep everything synchronized as they should be, rather than trying to make it look the same on all machines. For this reason, if you play it on an emulator, by speeding up the clock rate, the game becomes unplayably fast, whereas if I start up Quake III and raise the clock rate on my processor, the game renders faster, but the world moves at the same rate.

As an anecdote, when I was testing out the physics engine I was writing at the same time that I was testing my software renderer, I was swapping back and forth between my OpenGL plugin and my software renderer plugin between tests, and was surprised to find that the world moved much faster with the software renderer, which rendered at 20fps or so, than it did using the OpenGL renderer, at 200fps. It was then that I learned how important it is to accumulate the time that passes, and update the world in fixed increments, rather than just taking however much time passed, and updating the world by that much. Not only did it cause a disparity between speeds of iterating through the game loop, but it led to problems like objects "tunneling" through each other, because at one point, the actor is on one side of the wall, but after the next update, it has gone straight through the wall, because there were no updates between the two points in time, where the actor was close to the wall and would have been stopped by the collision detection.

If anyone else has better solutions than what I've learned, I'd be interested in knowing, as well.

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

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

Like I said, that has nothing to do with the actual FPS/UPS calculations. The FPS/UPS variable it updates to is STRICTLY for display purposes. I added that code in after I had the rates updating at the same time as it had after I added that code. It has NOTHING to do with the actual fps/ups calculations, just the display of information that tells me how many times per second the game is updating. If I run the game, which is just a blue screen still, it updates the title of the window with that information, telling me that i'm getting about 4000-4500 FPS, and about 60-62 UPS right now. But instead of that number in the title bar changing 60 times per second, which is how many times Update() get's called per second (UPS), it checks to see if a second in time has passed, and if so, updates the title bar. Nothing more, nothing less. I can't stress that enough. It has absolutely NOTHING to do with the actual game loop. tongue.png

I used Pokemon as more of an example of the display I was going for, with a kind of top down approach with a full 2D world, and not a 2D side scroller. I have no intentions of emulating Pokemon gameplay at all, but at the time, that was the first game that popped into my head to use as an example of the type of display I was looking for. I should have said, not a 2D side scroller, but a 2D tile based world.

GafferonGames' link is the only other link I used that I actually tried to implement the gameplay, but it almost seemed a little too complicated for a smaller indie title. Not only that, but I was having trouble trying to decipher the actual game loop in terms of FPS/UPS. Going for a time based Update() is my goal here, I guess i'm just implementing it wrong. I'll have to look over it again, for a third day in a row, and see if I can grasp it any better than I previously had. One question about this article though, is the integrate() function basically the Update() function i'm trying to use? Keep in mind, my game logic hasn't really been born yet, which is why I don't even pass a gameTime variable into Update(). It will eventually, but like I said, I need to get the main game loop running in a way I'm happy with and know it SHOULD run about the same on MOST systems first.

So I reworked my loop, and this is what I have, but with an unexpected result:


double t = 0.0;
	const double dt = 0.01;

	double currentTime = SDL_GetTicks();
	double accumulator = 0.0;

	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	

		double newTime = SDL_GetTicks();
		double frameTime = newTime - currentTime;
		if(frameTime > 0.25)
			frameTime = 0.25;
		currentTime = newTime;

		accumulator += frameTime;

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

		game->Render();

	} // End main loop

Now, I know that SDL_GetTicks() only has a resolution of 1ms, so I may find a better one soon. Here is my code for calculating frames per second and updates per second:


	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;
	}

timeBase, frameCount and updateCount are all initialized to zero in the constructor, and frameCount++ is located in the render function, but I didn't find it necessary to include that entire function for one line of code, which is basically in Update() as well.

Now, here is where I'm confused. the FPS is about 270, while the UPS is about 7000. This seems a little off, but I could be wrong. I haven't incorporated the time stuff into the Update() function yet, since there's nothing on the screen to worry about updating, but I will when I get this loop going correct. How does it look to you guys? Would you do anything different? Thanks!

This topic is closed to new replies.

Advertisement