Jump to content

Guide to Game Development/Theory/Game logic/Setting up ticks and frames

75% developed
From Wikibooks, open books for an open world

A piece of C++ set-up code for a game that separated the ticks from the frames, where ticks are constant and frames go as fast as the system allows it.

BUG:
Error with "ns.time_since_epoch" need to replace with a function that gets the system's nanosecond date since the start of the epoch.
#include <iostream>
#include <chrono>
#include <thread>
#include "cstdlib"
#include <sys/timeb.h>
#include <time.h>
using namespace std;

timeb tb;
bool running = false;
int tickCount = 0;

//Stating that the following functions exist below
void run();
void tick();
void render();

int main(){
	run();
	system("PAUSE");
	return 0;
}

void run(){
	auto ns = chrono::high_resolution_clock::now();

        //Get time in nano seconds
	long lastTime =  std::chrono::duration_cast<std::chrono::nanoseconds>
                        (std::chrono::system_clock::now().time_since_epoch()).count(); 
	double nsPerTick = 1000000000/60.0f;

	int ticks = 0;
	int frames = 0;

	long lastTimer = tb.millitm; //Get time in current milliseconds
	double delta = 0;

	while(running) {
		ns = chrono::high_resolution_clock::now();
                //Get time in nano seconds
		long now = std::chrono::duration_cast<std::chrono::nanoseconds>
                        (std::chrono::system_clock::now().time_since_epoch()).count(); 
		delta += (now-lastTime)/nsPerTick;
		lastTime = now;

		while (delta >= 1){
			ticks++;
			tick();
			delta--;
		}

		try{
			std::this_thread::sleep_for(std::chrono::milliseconds(2)); //Sleep for 2ms
		}catch(int e){
			cout << "Error #" << e << endl;
		}

		frames++;
		render();

		if (tb.millitm/*Get time in current milliseconds*/ - lastTimer >= 1000){
			lastTimer += 1000;
			cout << "Ticks: " << ticks << "   Frames: " << frames << endl; //Output of frames per second
			ticks = 0;
			frames = 0;
		}

	}
}

void tick(){
	tickCount++;
	
	//Do game updates here
}

void render(){
	//Handle graphics here
}

Another way that this can be done is:

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

struct vec2 { float x, y; } ballPos, ballVel{0.707f, 0.707f};

constexpr float GAME_LOGIC_SPEED = 1.f; //Frame time step
constexpr float SLICE = 1.f; //Size of a slice

void FixedUpdate() { //Time relevant stuff
	//Operator overloading (+, *, +=) could be added to vec2 to make this better
	ballPos.x += ballVel.x * GAME_LOGIC_SPEED;
	ballPos.y += ballVel.y * GAME_LOGIC_SPEED;
}

void Update() { //Graphics and non-time relevant stuff
	//Ball.Render();
}

void HandleWindowEvents() { }
void HandleInput() { }

void main() {
	float currentSlice = 0.f;
	int lastFrameTime = 0;
	auto lastTimePoint = chrono::high_resolution_clock::now();
	auto deltaTime = chrono::high_resolution_clock::now() - lastTimePoint;
	float ftSeconds, fps;
	while (true) { //Game Loop
		HandleWindowEvents();
		HandleInput();

		currentSlice += lastFrameTime;

		for (;currentSlice >= SLICE; currentSlice -= SLICE)
			FixedUpdate();

		Update();
		deltaTime = chrono::high_resolution_clock::now() - lastTimePoint;
		lastTimePoint = chrono::high_resolution_clock::now();

		int ft = chrono::duration_cast<chrono::duration<float, milli>>(deltaTime).count();
		lastFrameTime = ft;
		ftSeconds = ft / 1000.f;
		fps = 1.f / ftSeconds;
		cout << "\rFT: " << ft << "\t\t\tFPS: " << fps << "\t\t\t";
	}

	system("pause");
}

/*
     *-----Frame---*--Frame-*--------Frame--------*
|.......|.......|.......|.......|.......|.......|.......|.......
|       |       |       |       |       |       |       |       
\ Slice \ Slice \ Slice \ Slice \ Slice \ Slice \ Slice \ Slice 

*/

References

[edit | edit source]