Come aggiunta alla risposta di Journeyman Geek (perché la mia modifica è stata respinta) per le persone che sono interessate alla parte di programmazione / prospettiva degli sviluppatori:
Dal punto di vista dei programmatori, per coloro che sono interessati, i tempi di DOS erano tempi in cui ogni tick della CPU era importante, quindi i programmatori mantenevano il codice il più velocemente possibile.
Uno scenario tipico in cui qualsiasi programma verrà eseguito alla massima velocità della CPU è questo semplice (pseudo C):
int main()
{
while(true)
{
}
}
questo funzionerà per sempre, ora, trasformiamo questo frammento di codice in un gioco pseudo-DOS:
int main()
{
bool GameRunning = true;
while(GameRunning)
{
ProcessUserMouseAndKeyboardInput();
ProcessGamePhysics();
DrawGameOnScreen();
//close game
if(Pressed(KEY_ESCAPE))
{
GameRunning = false;
}
}
}
a meno che le DrawGameOnScreen
funzioni non utilizzino il doppio buffering / V-sync (che era un po 'costoso nei giorni in cui venivano creati i giochi DOS), il gioco funzionava alla massima velocità della CPU. Su un moderno i7 mobile questo dovrebbe funzionare da circa 1.000.000 a 5.000.000 di volte al secondo (a seconda della configurazione del laptop e dell'utilizzo attuale della CPU).
Ciò significherebbe che se potessi far funzionare qualsiasi gioco DOS sulla mia CPU moderna nelle mie finestre a 64 bit, potrei ottenere più di mille (1000!) FPS che è troppo veloce per essere giocato da qualsiasi essere umano se l'elaborazione della fisica "presume" che funzioni tra 50-60 fps.
Ciò che gli sviluppatori (possono) oggi possono fare è:
- Abilita V-Sync nel gioco (* non disponibile per le applicazioni con finestre ** [aka disponibile solo nelle app a schermo intero])
- Misura la differenza di tempo tra l'ultimo aggiornamento e aggiorna la fisica in base alla differenza di tempo che fa effettivamente funzionare il gioco / programma alla stessa velocità, indipendentemente dalla frequenza FPS
- Limitare il framerate a livello di codice
*** a seconda della configurazione della scheda grafica / driver / sistema operativo potrebbe essere possibile.
Per il punto 1 non c'è alcun esempio che mostrerò perché non è proprio una "programmazione". Sta solo usando le funzionalità grafiche.
Per quanto riguarda i punti 2 e 3, mostrerò i frammenti di codice e le spiegazioni corrispondenti:
2:
int main()
{
bool GameRunning = true;
long long LastTick = GetCurrentTime();
long long TimeDifference;
while(GameRunning)
{
TimeDifference = GetCurrentTime()-LastTick;
LastTick = GetCurrentTime();
//process movement based on how many time passed and which keys are pressed
ProcessUserMouseAndKeyboardInput(TimeDifference);
//pass the time difference to the physics engine so it can calculate anything time-based
ProcessGamePhysics(TimeDifference);
DrawGameOnScreen();
//close game if escape is pressed
if(Pressed(KEY_ESCAPE))
{
GameRunning = false;
}
}
}
Qui puoi vedere l'input dell'utente e la fisica prende in considerazione la differenza di tempo, ma potresti comunque ottenere sullo schermo 1000+ FPS perché il loop sta funzionando il più velocemente possibile. Poiché il motore fisico sa quanto tempo è passato, non deve dipendere da "nessuna ipotesi" o "un certo framerate", quindi il gioco funzionerà alla stessa velocità su qualsiasi CPU.
3:
Ciò che gli sviluppatori possono fare per limitare il framerate, ad esempio, a 30 FPS non è in realtà nulla di più difficile, basta dare un'occhiata:
int main()
{
bool GameRunning = true;
long long LastTick = GetCurrentTime();
long long TimeDifference;
double FPS_WE_WANT = 30;
//how many milliseconds need to pass before we need to draw again so we get the framerate we want?
double TimeToPassBeforeNextDraw = 1000.0/FPS_WE_WANT;
//For the geek programmers: note, this is pseudo code so I don't care for variable types and return types..
double LastDraw = GetCurrentTime();
while(GameRunning)
{
TimeDifference = GetCurrentTime()-LastTick;
LastTick = GetCurrentTime();
//process movement based on how many time passed and which keys are pressed
ProcessUserMouseAndKeyboardInput(TimeDifference);
//pass the time difference to the physics engine so it can calculate anything time-based
ProcessGamePhysics(TimeDifference);
//if certain amount of milliseconds pass...
if(LastTick-LastDraw >= TimeToPassBeforeNextDraw)
{
//draw our game
DrawGameOnScreen();
//and save when we last drawn the game
LastDraw = LastTick;
}
//close game if escape is pressed
if(Pressed(KEY_ESCAPE))
{
GameRunning = false;
}
}
}
Quello che succede qui è che il programma conta quanti millisecondi sono passati, se viene raggiunto un determinato importo (33 ms), quindi ridisegna la schermata di gioco, applicando effettivamente una frequenza dei fotogrammi vicino a ~ 30.
Inoltre, a seconda dello sviluppatore, può scegliere di limitare TUTTA l'elaborazione a 30 fps con il codice sopra leggermente modificato in questo modo:
int main()
{
bool GameRunning = true;
long long LastTick = GetCurrentTime();
long long TimeDifference;
double FPS_WE_WANT = 30;
//how many miliseconds need to pass before we need to draw again so we get the framerate we want?
double TimeToPassBeforeNextDraw = 1000.0/FPS_WE_WANT;
//For the geek programmers: note, this is pseudo code so I don't care for variable types and return types..
double LastDraw = GetCurrentTime();
while(GameRunning)
{
LastTick = GetCurrentTime();
TimeDifference = LastTick-LastDraw;
//if certain amount of miliseconds pass...
if(TimeDifference >= TimeToPassBeforeNextDraw)
{
//process movement based on how many time passed and which keys are pressed
ProcessUserMouseAndKeyboardInput(TimeDifference);
//pass the time difference to the physics engine so it can calculate anything time-based
ProcessGamePhysics(TimeDifference);
//draw our game
DrawGameOnScreen();
//and save when we last drawn the game
LastDraw = LastTick;
//close game if escape is pressed
if(Pressed(KEY_ESCAPE))
{
GameRunning = false;
}
}
}
}
Ci sono alcuni altri metodi e alcuni di loro odio davvero.
Ad esempio, usando sleep(<amount of milliseconds>)
.
So che questo è un metodo per limitare il framerate, ma cosa succede quando l'elaborazione del tuo gioco richiede 3 millisecondi o più? E poi esegui il sonno ...
questo comporterà un framerate inferiore rispetto a quello che solo sleep()
dovrebbe causare.
Prendiamo ad esempio un tempo di sospensione di 16 ms. questo farebbe funzionare il programma a 60 hz. ora l'elaborazione dei dati, input, disegno e tutto il resto richiede 5 millisecondi. siamo a 21 millisecondi per un loop ora che risulta in poco meno di 50 hz, mentre potresti facilmente essere ancora a 60 hz ma a causa del sonno è impossibile.
Una soluzione sarebbe quella di rendere un sonno adattivo sotto forma di misurazione del tempo di elaborazione e deduzione del tempo di elaborazione dal sonno desiderato con conseguente correzione del nostro "bug":
int main()
{
bool GameRunning = true;
long long LastTick = GetCurrentTime();
long long TimeDifference;
long long NeededSleep;
while(GameRunning)
{
TimeDifference = GetCurrentTime()-LastTick;
LastTick = GetCurrentTime();
//process movement based on how many time passed and which keys are pressed
ProcessUserMouseAndKeyboardInput(TimeDifference);
//pass the time difference to the physics engine so it can calculate anything time-based
ProcessGamePhysics(TimeDifference);
//draw our game
DrawGameOnScreen();
//close game if escape is pressed
if(Pressed(KEY_ESCAPE))
{
GameRunning = false;
}
NeededSleep = 33 - (GetCurrentTime()-LastTick);
if(NeededSleep > 0)
{
Sleep(NeededSleep);
}
}
}