Guide to Game Development/Theory/Game logic/Setting up ticks and frames
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.
- BUG:
#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
*/