Séparation de la logique / mise à jour du code de rendu / dessin dans un seul thread à l'aide de la mise en veille

9

J'ai lu que la vitesse des objets de jeu ne devrait pas être entravée par FPS mais devrait plutôt être basée sur le temps. Comment puis-je séparer le code de mise à jour / dessin pour maximiser les performances sans limiter le taux de dessin et fournir un taux de mise à jour logique constant en fonction du temps?

Mon pseudo-code actuel est le suivant

loop
{
    draw();
    if (ticksElapsed() > 100)
    {
        update();
        ticks+= ticksElapsed();
    }        
}

Le problème est que le code de dessin entrave les performances du taux de mise à jour (). Et il consomme 100% de processeur car si le sommeil est interrompu, il supprime les deux fonctions de dessin / logique.

J'utilise également SDL et il ne semble pas avoir d'option vsync. J'ai également entendu parler des termes temps fixe et variable, mais je ne sais pas comment cela peut être fait avec sleep ()

Oskenso Kashi
la source
1
Vous n'avez pas besoin de gaspiller 100% de puissance CPU uniquement pour attendre, mettez un sommeil (0) à la fin des boucles while si ticksElapsed () <100. Le système d'exploitation reviendra au thread immédiatement s'il n'y a pas d'autre thread qui veut courir. Mais ne gaspillez plus 100% de puissance CPU.
Maik Semder
Cependant, la meilleure solution pour une telle configuration à 1 thread est d'utiliser vsync, si vous ne pouvez pas vsync, puis appelez sleep (0) en boucle jusqu'à ce que vous atteigniez la fréquence d'images cible, puis mettez à jour et dessinez
Maik Semder

Réponses:

3

Dans votre extrait de code, il semble que vous essayez d'exécuter votre jeu en mode pas à pas fixe en attendant que votre dessin et votre mise à jour prennent moins de 15 ms (60 ips). C'est possible et vous avez bien deviné que cela ne peut pas être fait en utilisant un appel de sommeil parce que vous ne savez pas exactement combien de temps vous allez dormir. La boucle d'attente occupée est la bonne solution.

Cependant, considérez le cas où votre mise à jour et votre dessin dépassent 15 ms, vous devez maintenant ralentir le dessin et la mise à jour du jeu. Vous pouvez maintenant faire deux choses: détecter cet état et supprimer les images (sauter le dessin et passer directement à la mise à jour jusqu'à ce que vous soyez à nouveau synchronisé), mais si l'ordinateur est juste à ralentir, il ne rattrapera jamais.

Une autre solution consiste à rendre votre logique de mise à jour indépendante du temps fixe. Vous n'avez pas besoin d'un thread séparé pour cela, il vous suffit de spécifier à quelle vitesse les choses devraient évoluer. Au lieu de 5 pixels par tick, vous devez utiliser 50 pixels par seconde. Vous auriez besoin d'un minuteur de haute précision pour y parvenir, et toute votre logique de mise à jour devrait pouvoir accéder au minuteur pour voir combien de temps s'est écoulé depuis la dernière mise à jour.

Fondamentalement, vous passez de:

void UpdatePlayer()
 player.x += 10;

À

void UpdatePlayer(float elapsedSeconds) //the total seconds elapsed since last update
 player.x += walkspeed * elapsedSeconds;
Roy T.
la source
Donc mon moteur consommera toujours à 100% et je ne peux rien y faire?
Oskenso Kashi
1
Il consommera autant de cycles que le programmateur le permet, mais ce n'est pas vraiment un problème car vous ne pouvez pas vraiment faire beaucoup d'autres choses pendant que vous jouez à un jeu :).
Roy T.
@Oskenso Cependant, c'est un problème si vous utilisez plus d'un thread, alors le thread principal ne laissera pas les autres s'exécuter autant qu'ils le pourraient, gaspillant beaucoup de puissance de calcul dans la boucle while, vous devriez vraiment envisager un sommeil
Maik Semder
@Maik Semder: avez-vous une solution pour que sleep (x) ne soit pas précis? Une fois l'intervalle de sommeil écoulé, le thread est prêt à s'exécuter. Mais un thread prêt n'est pas garanti pour s'exécuter immédiatement. Cela dépend du planificateur. Lorsque vous utilisez deux threads, il existe d'autres solutions, pour cela, consultez cet excellent article: altdevblogaday.com/2011/07/03/threading-and-your-game-loop
Roy T.
1
@Roy sleep (0) est la solution. Il revient immédiatement s'il n'y a aucun autre thread qui veut s'exécuter ( Sleep WinAPI ) et donne aux autres threads la chance de s'exécuter. Si l'autre thread ne donne pas la chance au thread principal de s'exécuter en retour, alors vous avez un problème de thread, mais bloquer tout le reste en n'appelant pas sleep en premier lieu le rend encore pire et n'est guère une solution. La clé est d'appeler sleep (0) et de tester le temps écoulé jusqu'à ce que vous atteigniez votre taux de trame fixe cible, afin de ne pas gaspiller 100% CPU juste pour attendre.
Maik Semder