Advertisement

60 FPS game loop using std::chrono

Started by July 17, 2017 03:11 AM
8 comments, last by Rutin 7 years, 4 months ago

Hi everyone, out of curiosity yesterday I was trying out chrono header with the goal of creating a 60fps loop, the code below is what I have so far and I wanted your opinion about it, if it is the proper way to do it, how it is usually done or how I could improve on it .

Thanks :)


#include <iostream>
#include <chrono>
#include <cstdint>
using namespace std;
using namespace chrono;

using frame = duration<int32_t, ratio<1, 60>>;
using ms = duration<float, milli>;

int main()
{
	time_point<steady_clock> fpsTimer(steady_clock::now());
	frame FPS{};

	while (true)
	{
		FPS = duration_cast<frame>(steady_clock::now() - fpsTimer);
		if (FPS.count() >= 1)
		{
			fpsTimer = steady_clock::now();
			cout << "LastFrame: " << duration_cast<ms>(FPS).count() << "ms  |  FPS: " << FPS.count() * 60 << endl;
		}
	}
	return 0;
}

output:

LastFrame: 16.6667ms  |  FPS: 60
LastFrame: 16.6667ms  |  FPS: 60
LastFrame: 16.6667ms  |  FPS: 60
LastFrame: 16.6667ms  |  FPS: 60
LastFrame: 16.6667ms  |  FPS: 60
LastFrame: 16.6667ms  |  FPS: 60
LastFrame: 16.6667ms  |  FPS: 60
LastFrame: 16.6667ms  |  FPS: 60
LastFrame: 16.6667ms  |  FPS: 60
LastFrame: 16.6667ms  |  FPS: 60
LastFrame: 16.6667ms  |  FPS: 60
LastFrame: 16.6667ms  |  FPS: 60

The 60 frames per second really only applies to game consoles where the TV monitor is running at 60 frames per second because that's what the old television standards used.

If you're on a monitor running at 75 frames per second, 60 is probably not your ideal target.

There are many good articles about the typical game loop, here are two often recommended articles:

Clicky - DeWiTTERS

Clicky - Gaffer

Advertisement

I'll go check them out, thanks frob :)

On 7/16/2017 at 10:52 PM, MarcusAseth said:

I'll go check them out, thanks frob :)

I would strongly recommend you do not lock render frames in your time step, but you render your input and logic at a certain amount of ticks per second, and render graphics as fast as possible with the remaining time until the next frame is due. You will have to use interpolation to smooth out movement however. This will make sure you're getting smooth graphics at any FPS.

I'm using a similar loop like DeWitters with interpolation. Works like a charm. Also, stay away from variable rates for logic and input, it can do some wonky things with collision among other things. I always prefer Fixed Time Steps with Variable Render Rates using Interpolation.

Programmer and 3D Artist

derived from, fix your timestep, I am using this:


mPlaying = true;

int lag = 0;
std::chrono::time_point<std::chrono::steady_clock> endTime = std::chrono::steady_clock::now();

long longestTime = 0;
long frameCount = 0;
long tickCount = 0;
long fpsElapsedTime = 0;

while (mPlaying) {

	std::chrono::time_point<std::chrono::steady_clock> startTime = std::chrono::steady_clock::now();

	std::chrono::milliseconds elapsedTime(std::chrono::duration_cast<std::chrono::milliseconds>(startTime - endTime));
	endTime = startTime;

	lag += static_cast<int>(elapsedTime.count());

	if (elapsedTime.count() == 0) { //the sim loop will not be run if lag was not increased prior ( previous frame normally )
		Sleep(5); 
	}

	const int fps = 60;
	const int lengthOfFrame = 1000 / fps;

	MSG msg;
	while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	//game-sim loop, is independent of rendering
	std::chrono::time_point<std::chrono::steady_clock> oldTime = std::chrono::steady_clock::now();
	while (lag >= lengthOfFrame) {
		//update the player and ship
		if (!getInput()) break;

		game.step();					

		//finish tick
		lag -= lengthOfFrame;
		tickCount++;	//keep track of how many ticks weve done this second
	}

	//render the frame
	Graphics::get().getRoot()->renderOneFrame();
	frameCount++;	//keep trrack of how many frames weve done this second

	//handle per-second stats
	fpsElapsedTime += static_cast<long>(elapsedTime.count());
	if (fpsElapsedTime >= 1000) {

		std::stringstream sstr;
		sstr << frameCount << ", "
					 << tickCount;

		mCefGui->writeJavascript("setStatus", sstr.str().c_str(), url);
		fpsElapsedTime = 0;
		frameCount = 0;
		tickCount = 0;
	}
}
	

 

Thanks for the feedback guys, I am putting this on hold because I'm swamped in math (trying to watch ALL khan academy videos in order, 600 videos in), but when the times come I'm definetly putting my full attention into this :P

Advertisement
10 hours ago, MarcusAseth said:

Thanks for the feedback guys, I am putting this on hold because I'm swamped in math (trying to watch ALL khan academy videos in order, 600 videos in), but when the times come I'm definetly putting my full attention into this :P

If you need help later on, just send me a PM.

@h8CplusplusGuru not sure if your loop is just an example to run and show, but you should never 'sleep' when waiting, it's not a good practice. You cannot guarantee when sleep will end, only that sleep will start and stay as such for the minimum time you set. Don't use it to regulate frames.

Programmer and 3D Artist

It really only matters when debugging the loop, such that elapsedTime may == 0. In actual app that is unlikely, and the sleep is not ran. It actually makes sense to have sleep(5) if you were to troll the code more effectively. 

1 minute ago, h8CplusplusGuru said:

It really only matters when debugging the loop, such that elapsedTime may == 0. In actual app that is unlikely, and the sleep is not ran. It actually makes sense to have sleep(5) if you were to troll the code more effectively. 

No problem. I just need to state this because new programmers are using sleep to regulate Frames, and it's a horrible practice.

Programmer and 3D Artist

This topic is closed to new replies.

Advertisement