Controlling game loop speed

Started by
9 comments, last by SimonForsman 10 years, 11 months ago

My pacman clone is progressing, and I am now at the point where I need to slow down the game loop.

However, there are points where I switch textures which causes a subtle slow-down (I think - it's hard to tell), so my thoughts turn to regulating the game loop speed - i.e. I don't want to see any slowdown whatsoever.

Rather than putting a wait command in the loop, what other techniques are there? I'm sure there are loads out there, so please feel free to point in the way of your favourite article. I'm basically doing research at the moment before going ahead with anything.

Essentially I want to choose a game speed which means there will always be room for a little extra processing overhead, and the user will not notice a thing. It's running too fast at the moment! On average about 350 FPS.

Thanks in advance! cool.png

Advertisement
Yep, neither a busy wait nor a Thread.Sleep would be I good idea. The former wastes cycles and the latter has too big a granularity so you might drop frames.

Do a VSync, in D3D9 this is done with PresentInterval.One (PresentParameters of the device or SwapChain), in D3D11 with SwapChain.Present(1,PresentFlags.None);

The below code is from a C++ user's perspective, but the concept works across languages.

Every frame, get the amount of time passed since the last frame, like this:

//Get the amount of time passed since the program began.
currentTime = GetTimeInMillaseconds();
 
//Calculate amount of time past since the previous frame's time.
deltaTime = (currentTime - previousTime);
 
//Make this frame's time be the previous frame's time (for next loop's calculation).
previousTime = currentTime;

Then convert from milliseconds to floating point seconds (not actually required, but makes calculations alot easier).

//This makes 1 second equal to 1.0f, and half a second 0.5f, and so on.
deltaTimeAsFloat = (float(deltaTime) / 1000.0f);

Sometimes APIs provide a convenience function that directly gives you the delta time.

Now you can pass 'deltaTimeAsFloat' to all your subsystems where movements, animations, and everything else takes place.

So, you can do things like this:

updateCharacter(float deltaTime)
{
      const float MovementSpeedPerSecond = 200.0f; //200 pixels a second.
 
      //If half a second (deltaTime = 0.5f) goes by, then (200.0f * 0.5f == 100.0f).
      float distanceMovedThisFrame = (MovementSpeedPerSecond * deltaTime);
      
      playerPosition += distanceMovedThisFrame;
}

So you end up updating movement based on the amount of time passed, not the number of frames that ran.

There are other methods as well, like fixed time-steps.

unbird and Servant - thanks for the advice!

So I just tried this as a first foray into FPS regulation. Results are ... not great!

So before my game loop I call this code:


			int FPS_TARGET = 150;
			int SKIP_TICKS = 10000000 / FPS_TARGET;			
			int TICKS_BEFORE_GAME_LOOP = getTickCount();	
			int TICKS_PASSED = 0;
			int TICK_TARGET = 0;
			int TICKS_NOW = 0;
			int TICKS_TO_WAIT = 0;
			TimeSpan timeSpan;

Then after the game operations, at the end of the loop I call:


					//		Tick target = Ticks BEFORE game loop + how many ticks to equate to desired FPS
					TICK_TARGET = TICKS_BEFORE_GAME_LOOP + SKIP_TICKS;
					// Get the tick count right now
					TICKS_NOW = getTickCount();
					// Find out how many ticks have passed
					TICKS_PASSED = getTickCount() - TICKS_BEFORE_GAME_LOOP;
					// Find out how many ticks we should wait
					TICKS_TO_WAIT = TICK_TARGET - TICKS_NOW;
					// Create a time span object so we can esily derive the milliseconds to wait
					timeSpan = new TimeSpan(TICKS_TO_WAIT);

					// Now sleep for the desired number of milliseconds	
					if(timeSpan.Milliseconds > 0)
					{
						// We're going to fast so slow down.
						Thread.Sleep(timeSpan.Milliseconds);
					}
					else
					{
						// We're running slow
					}

Initially this looks like it is working, but then later on the game starts going to fast again. I've got some timers in my game already, and confess to not really understanding threads that much (all the work has been on the game engine and logic so far...), so I am going to look into why my method is failing terribly!

As I said, new to this FPS regulation stuff. Trying a few things out is teaching me about various pitfalls etc (I seem to like learning that way!) cool.png

Well I said you shouldn't use Thread.Sleep wink.png. Get accurate timing first. You can e.g. use the System.Diagnostics.Stopwatch: Start it at program start and use the Elapsed property.

Edit: As Servant pointed out. You use the delta to "feed your simulation", not the other way round.

Now: Even the Stopwatch normally has a granularity which might pose a problem (15 ms if memory serves). Check if Stopwatch.IsHighresolution is enabled, it usually is. You will have to do the conversion yourself with (double)Stopwatch.ElapsedTicks / Stopwatch.Frequency.

Well I've tried to implement FPS control using a timer, but it's not working for some reason I cannot fathom.

I am succesfully calculating the "MS to wait after every frame until proceeding to the next iteration of the game loop". Now then - having already tried Thread.Sleep() with dodgy results, I thought using a time would help.

I've got a setting to enable me to switch between timing modes. The code for using a timer is as follows:


	
        nextFrame = false;

        if(FPS_METHOD == "TIMER")
	{		
		// -----------------------------------------------------------------
		// TIMER METHOD (awful at present
		// -----------------------------------------------------------------
		if(GLOBAL_MS_TO_WAIT > 0)
		{		
			// START TIMER
			StartFPSTimer();	// Sets MS passed to 0
			
			// Wait until the nextFrame flag has been set. This will be when we reach the desired number of MS.
			// This is detected in the event MSecondHasPassed().
			do
			{
				// nothing
			} while (!nextFrame); // nextFrame will always be true after exiting this loop.
		}
		else
		{
			// We're running SLOW.
		}
	} // TIMER METHOD

Now in the game initialisation code I call the following to set up the FPSTimer:


		public void CreateFPSTimer()
		{
			// This timer needs to be tied to an event which fires every time a MS passes, not a second.
			FPStimer = new System.Threading.Timer(new System.Threading.TimerCallback(MSecondHasPassed), null, 0, 100);
		}

I declare the event as follows:


		private void MSecondHasPassed(object obj)
		{			
			// Increase the seconds passed counter.
			MsecondsPassed++;
			
			// Set nextFrame flag if MS passed is at the threshold
			if(MsecondsPassed == GLOBAL_MS_TO_WAIT)
			{
				nextFrame = true;
			}
		}

So what I'm doing is just waiting for the flag to be set via the event per MS. It looked easy to implement.

I *think* my timer is not declared correctly because the game slows to an unbearable pace. I thought it was declared correctly but it can't be. Is there something painfully obvious that I've got wrong?

Why are you insisting on regulating your framerate? That's kind of a backwards approach to the issue, which is that you don't use real wall-clock time to control your simulation speed.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

Well basically I'm trying to slow the game down. The design is quite (read very) primitive really. I'm just starting out and whilst I have a fairly solid game engine, I need to control the speed of the game loop.

Whilst really I should go with an overhaul to bring the design up to speed with best practice (updating movement based on time passed) I thought a quick fix for now would be to regulate the FPS.

I strongly recommend biting the bullet and writing the code correctly. As far as I'm aware you're under no specific deadline, so there's no reason to do something you explicitly know is inferior :-)

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

My first game iteration plan includes quick-win options, as the quality of the first game is not supposed to be A1 but server as a learning exercise. I would imagine that the 2nd pass, or phase 2 would include using deltas and doing things correctly. Although I am under no specific deadline, I do want to draw Phase 1 to a close.


The next task after controlling game speed is to introduce sound, which I'm placing a higher priority on than a full rework to control game speed correctly. I guess it depends what's on your plan is for the implementation - and they are always pretty individual. I'd favour a patch-up for now. Just a preference though, and I'm sure others will disagree. If I can patch it up for minimal effort and it runs OK from the users point of view then that's fine for now.cool.png

This topic is closed to new replies.

Advertisement