En complément de la réponse de Journeyman Geek (parce que mon édition a été rejetée) pour les personnes intéressées par le point de vue du codage / développeur:
Du point de vue des programmeurs, pour ceux qui sont intéressés, les temps de DOS étaient des moments où chaque tick du processeur était important, les programmeurs gardaient donc le code le plus rapidement possible.
Un scénario typique dans lequel n'importe quel programme s'exécutera à la vitesse maximale de la CPU est simple (pseudo C):
int main()
{
while(true)
{
}
}
cela fonctionnera pour toujours, transformons cet extrait de code en un pseudo-jeu-DOS:
int main()
{
bool GameRunning = true;
while(GameRunning)
{
ProcessUserMouseAndKeyboardInput();
ProcessGamePhysics();
DrawGameOnScreen();
//close game
if(Pressed(KEY_ESCAPE))
{
GameRunning = false;
}
}
}
À moins que les DrawGameOnScreen
fonctions n'utilisent la double mise en tampon / synchronisation V (ce qui était assez cher à l'époque des jeux DOS), le jeu tournera à la vitesse maximale du processeur. Sur un mobile i7 moderne, cette vitesse serait environ 1 000 000 à 5 000 000 fois par seconde (en fonction de la configuration de l'ordinateur portable et de l'utilisation actuelle du processeur).
Cela signifierait que si je pouvais faire jouer n'importe quel jeu DOS sur mon processeur moderne dans mes fenêtres 64 bits, je pourrais avoir plus de mille (1 000!) FPS, ce qui est trop rapide pour qu'un humain puisse jouer si le traitement physique "suppose" qu'il s'exécute. entre 50-60 fps.
Ce que les développeurs du jour actuel (peuvent) font est:
- Activer V-Sync dans le jeu (* non disponible pour les applications fenêtrées ** [alias uniquement disponible dans les applications plein écran])
- Mesurer la différence de temps entre la dernière mise à jour et mettre à jour la physique en fonction de la différence de temps qui permet effectivement au jeu / programme de fonctionner à la même vitesse quel que soit le taux de FPS
- Limiter le framerate par programmation
*** En fonction de la configuration de la carte graphique / du pilote / du système d'exploitation, cela peut être possible.
Pour le point 1, je ne montrerai aucun exemple car ce n’est pas vraiment une "programmation". C'est juste en utilisant les fonctionnalités graphiques.
En ce qui concerne les points 2 et 3, je montrerai les extraits de code et les explications correspondants:
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;
}
}
}
Ici, vous pouvez voir que les entrées utilisateur et la physique tiennent compte de la différence de temps, mais vous pouvez toujours obtenir plus de 1000 FPS à l'écran car la boucle est aussi rapide que possible. Parce que le moteur physique sait combien de temps a passé, il ne doit pas dépendre "d'aucune hypothèse" ou "d'un certain nombre d'images par seconde", de sorte que le jeu fonctionnera à la même vitesse sur n'importe quel processeur.
3:
Ce que les développeurs peuvent faire pour limiter le nombre d’images par exemple à 30 images par seconde n’est en réalité rien de plus difficile, jetez-y un coup d’œil:
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;
}
}
}
Ce qui se passe ici, c’est que le programme compte le nombre de millisecondes écoulées. Si une certaine quantité est atteinte (33 ms), il redessine l’écran de jeu en appliquant effectivement une cadence proche de ~ 30.
En outre, en fonction du développeur, il / elle peut choisir de limiter le traitement TOUT à 30 ips avec le code ci-dessus légèrement modifié:
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;
}
}
}
}
Il existe quelques autres méthodes, et certaines que je déteste vraiment.
Par exemple, en utilisant sleep(<amount of milliseconds>)
.
Je sais que cette méthode permet de limiter le nombre d'images par seconde, mais que se passe-t-il lorsque le traitement de votre jeu prend 3 millisecondes ou plus? Et puis vous exécutez le sommeil ...
Cela se traduira par une fréquence d'images inférieure à celle qui sleep()
devrait être la seule cause.
Prenons par exemple un temps de sommeil de 16 ms. cela ferait fonctionner le programme à 60 hz. maintenant, le traitement des données, la saisie, le dessin et tout le reste prend 5 millisecondes. nous sommes à 21 millisecondes pour une boucle, ce qui donne un peu moins de 50 hz, alors que vous pourriez facilement être à 60 hz, mais à cause du sommeil, c’est impossible.
Une solution serait de créer un sommeil adaptatif en mesurant le temps de traitement et en déduisant le temps de traitement du sommeil souhaité, ce qui résoudrait notre "bogue":
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);
}
}
}