Y a-t-il un inconvénient à ce que la boucle de jeu principale ne soit pas contrôlée?

19

Je me demandais s'il y avait un danger possible lorsque ma boucle de jeu tourne aussi vite que le système le permet?

J'ai actuellement une boucle qui, en mesurant le temps passé en nanosecondes, exécute la logique du jeu et rend la logique à des vitesses prédéfinies sans problème. En fait, toute logique que je fais dans la boucle est cadencée à un certain nombre d'appels chaque seconde.

La boucle elle-même fonctionne à peu près aussi vite qu'elle le souhaite, ce qui représente environ 11,7 millions de boucles par seconde sur ma machine.

Boucle (pseudocode simple):

while(!isGameOver){

 if(canPollInputs){
    pollInputs()
 }

 while(canStepLogic){
    stepLogic()
 }

 if(canRender){
    render()
 }
}

Ma question est essentiellement si cette simple boucle, si elle ne fonctionne pas à une vitesse contrôlée, peut nuire à un système?

Edit: cela signifie que ma logique fonctionne 30 fois par seconde (30 tps), mon moteur de rendu fonctionne à 60 ips, j'interroge les entrées 100 fois par seconde et il y a aussi une certaine logique pour faire face à la logique ou le rendu prend plus de temps que prévu . Mais la boucle elle-même n'est pas étranglée.

Edit: Utiliser Thread.sleep()par exemple pour réduire la boucle principale à 250 boucles par seconde entraîne une réduction mais les boucles fonctionnent à environ 570 boucles par seconde au lieu des 250 souhaitées (ajoutera du code lorsque je serai sur ma machine de bureau ..)

Edit: C'est parti, une gameloop java fonctionnelle pour clarifier les choses. N'hésitez pas non plus à l'utiliser mais ne le revendiquez pas à vous;)

private void gameLoop() {

    // Time that must elapse before a new run
    double timePerPoll = 1000000000l / targetPPS;
    double timePerTick = 1000000000l / targetTPS;
    double timePerFrame = 1000000000l / targetFPS;
    int maxFrameSkip = (int) ( (1000000000l / MINIMUM_FPS) / timePerTick);

    int achievedPPS = 0;
    int achievedFPS = 0;
    int achievedTPS = 0;

    long timer = TimeUtils.getMillis();

    int loops = 0;

    int achievedLoops = 0;

    long currTime = 0l;
    long loopTime = 0l;

    long accumulatorPPS = 0l;
    long accumulatorTPS = 0l;
    long accumulatorFPS = 0l;

    long lastTime = TimeUtils.getNano();

    while(!isRequestedToStop) {
        currTime = TimeUtils.getNano();
        loopTime = currTime - lastTime;
        lastTime = currTime;

        loops = 0;

        accumulatorPPS += loopTime;
        accumulatorTPS += loopTime;
        accumulatorFPS += loopTime;

        if(accumulatorPPS >= timePerPoll) {
            pollInputs();
            playerLogic();
            achievedPPS++;
            accumulatorPPS -= timePerPoll;
        }

        while(accumulatorTPS >= timePerTick && loops < maxFrameSkip) {
            tick();
            achievedTPS++;
            accumulatorTPS -= timePerTick;
            loops++;
        }

        // Max 1 render per loop so player movement stays fluent
        if(accumulatorFPS >= timePerFrame) {
            render();
            achievedFPS++;
            accumulatorFPS -= timePerFrame;
        }

        if(TimeUtils.getDeltaMillis(timer) > 1000) {
            timer += 1000;
            logger.debug(achievedTPS + " TPS, " + achievedFPS + " FPS, "
                    + achievedPPS + " Polls, " + achievedLoops + " Loops");
            achievedTPS = 0;
            achievedFPS = 0;
            achievedLoops = 0;
        }

        achievedLoops++;
    }
}

Comme vous pouvez le voir, il n'y a presque pas de code exécuté sur chaque boucle mais toujours une certaine sélection en fonction du temps réel écoulé. La question se réfère à cette «boucle des travailleurs» et comment elle influence le système.

dot_Sp0T
la source
2
lien obligatoire 'fix your timestep': gafferongames.com/game-physics/fix-your-timestep veuillez le lire. Notez également que Thread.Sleep () ne peut pas être utilisé pour limiter votre pas de temps de manière fiable car le système d'exploitation ne garantit pas de retourner le contrôle à votre application après la durée demandée. (Cela peut varier).
Roy T.
Oui, c'est le problème avec le pseudocode. J'ai fait la plupart de ce que Gaffer écrit (travaille actuellement sur une implémentation delta qui complète ma structure de boucle). Si je ne peux pas utiliser Thread.sleep (), alors quoi d'autre est disponible dans Java standard?
dot_Sp0T
1
Vous utilisez généralement une boucle occupée avec Thread.Sleep (0) et espérez le meilleur. Gaffer en parle beaucoup dans quelques articles.
Roy T.
Eh bien, c'est essentiellement ce que j'ai fait, juste sans le thread.sleep (0), ET ce qui a provoqué à l'origine cette question. Y a-t-il un mal dans une boucle occupée (resp pas occupée car même cette petite logique répétitive est limitée) pour le système?
dot_Sp0T
Il y a quelque temps, un bug a poussé Starcraft II à le faire dans certaines conditions. Il a fini par fondre littéralement quelques GPU. Alors oui, il est très possible qu'une boucle de jeu non contrôlée cause du tort.
Mason Wheeler

Réponses:

35

Cela entraînera un cœur de processeur à toujours fonctionner à 100%. Cela ne cause généralement aucun dommage au système. Les processeurs sont conçus pour fonctionner à 100% pendant des heures. Mais sur un appareil mobile, cela déchargera rapidement la batterie et chauffera l'appareil, ce qui vous coûtera probablement environ une étoile dans les notes de votre magasin. Sur un ordinateur de bureau, cela pose moins de problème, mais il consommera davantage d'électricité des utilisateurs, provoquera une rotation plus rapide du ventilateur du processeur, ce qui pourrait provoquer du bruit et gaspiller des cycles de processeur qui pourraient autrement être utilisés par d'autres processus. Bien que ce ne soient pas des défauts critiques, ils sont toujours de mauvais style, vous devez donc les éviter lorsque cela est possible.

Vous n'avez rien dit sur le fonctionnement interne de votre boucle de jeu, mais lorsque vous utilisez l'approche delta-time (chaque calcul que vous faites prend le temps depuis le dernier appel), vous avez affaire à un très petit delta- valeurs de temps. Cela signifie que vous pouvez rencontrer des problèmes d'imprécision en virgule flottante qui peuvent provoquer toutes sortes de comportements étranges. De plus, la résolution de la minuterie système est souvent limitée, donc lorsque votre boucle logique est trop rapide, vous pouvez obtenir une valeur delta-t de zéro, ce qui pourrait alors provoquer une division par zéro entraînant un crash.

Pour atténuer ce problème, vous devez limiter votre fréquence d'images (graphique et logique) au maximum de ce que l'œil humain peut percevoir. La quantité contestée dépend du type d'animation et du type d'affichage affiché, mais les estimations varient de 40 FPS à 120 FPS. Cela signifie que vous devez définir un temps minimum d'exécution de chaque boucle entre 20 ms et 8 ms. Si une itération de boucle se termine plus rapidement, laissez le thread cpu sleeppour la durée restante.

Philipp
la source
Merci pour l'explication Philipp. En fait, j'ai fait référence au fait que je cours à un certain fps et tps ainsi que le sondage seulement un certain nombre de fois par seconde. Je vais essayer de clarifier les choses.
dot_Sp0T
@ dot_Sp0T Après la modification de votre question, seuls le premier paragraphe et la dernière phrase sont pertinents pour votre cas. Cependant, je voudrais conserver les deuxième et troisième paragraphes de ma réponse, car ils pourraient être utiles aux autres.
Philipp
J'accepterai sans doute aussi bien votre réponse que le premier paragraphe donne parfaitement une réponse. Pourriez-vous, d'une manière ou d'une autre, le distinguer visuellement du reste de la réponse?
dot_Sp0T
Jouez à une civilisation 2 non corrigée sur un bureau et vous constaterez que cela pose problème. Du moins, oui. haussement d'épaules
corsiKa
1
En plus des considérations relatives à la batterie et au ventilateur, une utilisation accrue du processeur peut entraîner une chaleur excessive qui peut être inconfortable (par exemple un ordinateur portable, en particulier mon rMBP) et même entraîner l'arrêt du PC (en particulier les ordinateurs plus anciens dotés de refroidisseurs poussiéreux). Ces facteurs sont exaspérés si vous exécutez tous les cœurs à 100%.
NPSF3000
2

Vous perdez des cycles CPU. Cela signifie moins de temps de batterie sur les ordinateurs portables, tablettes et téléphones, des factures d'électricité plus élevées, plus de chaleur générée par la machine, des ventilateurs plus bruyants. De plus, vous pouvez manger des cycles provenant d'autres processus système importants (par exemple, le serveur de fenêtres peut devenir saccadé), ce qui pourrait alors affecter le gameplay. Certains programmateurs système sur les systèmes multitâches préventifs d'aujourd'hui pénalisent également les applications qui utilisent trop de cycles, vous pouvez donc être rapide en premier, puis voir soudainement d'étranges secousses lorsque vous êtes étranglé par le système.

De nombreux joueurs inconditionnels construisent également à partir de zéro des PC personnalisés qui sont à la limite des spécifications de leurs fans, auquel cas une journée chaude et la chaleur générée par votre jeu peuvent déclencher les sécurités dans la machine (au mieux) ou même faire surchauffer les pièces et mourir (au pire). Donc, si c'est votre public cible, vous voudrez peut-être vous assurer de toujours laisser un peu d'espace "en haut".

Enfin, il y a des problèmes de gameplay où vous pourriez être en train d'avantager les joueurs avec des machines plus rapides que ceux avec des machines plus lentes. La limitation de la boucle de jeu à une certaine fréquence et l'utilisation de cycles supplémentaires uniquement pour les aspects non liés au gameplay (comme le rendu de graphismes plus fidèles, les effets sonores surround ou autre) rendront votre jeu plus équitable.

uliwitness
la source
Je dirais à l'inverse que les gens qui construisent passionnément leurs machines utilisent de bons ventilateurs, même le watercooling. Seuls les boîtiers HTPC ou mini ITX peuvent avoir des limitations sur cette partie. Sinon, les pauvres mais les passionnés utiliseront la boîte ventirad. ce qui est suffisant même à 100%, chaud mais ok.
v.oddou
Je répète simplement par expérience. Si vous regardez Star Trek Online, ils ont en fait un paramètre séparé dans leur interface graphique qui insérera sleep () s dans leur boucle principale pour empêcher certaines machines de surchauffer, donc cela ne peut pas être une chose rare si le fabricant de Neverwinter et La STO a vu la nécessité de l'ajouter. Gardez à l'esprit cette passion! = Compétence. Il y a beaucoup de bricoleurs qualifiés et passionnés, mais il y a aussi des débutants et d'autres qui apprennent encore, et avec un taux d'abandon décent, les statistiques disent qu'ils sont la majorité.
uliwitness
Grâce à ces commentaires je suis venu relire votre réponse. Vous énumérez des points valides mais, malheureusement, ne répondez pas vraiment à la question avec la controverse entre les machines plus rapides et les machines plus lentes . La boucle logique (appelée gameloop dans votre troisième paragraphe) est plafonnée. La question était de limiter la boucle de la dorsale qui réévalue simplement chaque exécution si une entrée doit être tirée, une logique calculée ou une trame doit être rendue - donc pas de logique plus rapide à moins que votre machine ne soit terriblement boiteuse
dot_Sp0T
1

Vous ne devriez pas avoir de mouvement / gameplay nerveux

Il existe deux façons d'implémenter la logique du jeu - liée à un temps réel, ou liée au nombre de "tours" / étapes de traitement. Si quelque chose dans votre jeu se déplace vers la gauche, est-ce que le mouvement sera différent si votre stepLogic () a été appelé 100 au lieu de 50 fois?

Si votre code traite explicitement le temps écoulé partout et le fait correctement, alors cela pourrait être correct; mais s'il y a quelque chose dans votre code qui dépend du nombre de «pas», vous obtiendrez des effets secondaires indésirables.

Tout d'abord, la vitesse des choses qui devraient bouger constamment variera de manière imprévisible (selon l'utilisation du processeur), ce qui rend les contrôles très ennuyeux - vous ne pouvez pas effectuer un coup de poing précis ou un saut si soudainement le jeu accélère ou ralentit. Même pour les jeux sans «secousses», cela semble ennuyeux et nerveux si les choses ne se déroulent pas correctement.

Deuxièmement, vous pourriez avoir des problèmes avec les capacités des personnages en fonction de la vitesse de l'ordinateur - un exemple classique est l'ancien problème Quake où la plage de saut maximale dépendait involontairement de vos fps.

Ces problèmes peuvent apparaître même si vous essayez d'avoir un code indépendant des fps, l'accumulation d'erreurs d'arrondi peut souvent provoquer de tels bogues.

Peter est
la source
1
Autant j'aime que votre réponse ne soit pas pertinente à la question, car je ne pose pas de question sur le mouvement / gameplay nerveux mais sur le mal qu'une boucle non contrôlée (qui ne contrôle aucune logique ou rendu ou autre) peut faire au système
dot_Sp0T
1

Le seul danger potentiel est votre consommation d'énergie, alors ne le faites pas sur les appareils mobiles. À l'inverse, bon nombre de systèmes intégrés passent leur vie entière dans une boucle en attendant que les choses se produisent.

Auparavant, il était tout à fait normal de simplement rendre les images de jeu aussi rapidement que possible, parfois même sans minuterie pour compenser le gameplay pour différentes vitesses de processeur. Le jeu de course de Bullfrog, Hi-Octane, a été particulièrement victime de cela, et je soupçonne que c'est à cela que fait référence l'affiche mentionnant Civ2.

Je vous conseille au moins d'interroger les entrées à chaque passage si vous êtes sur un système Windows ou similaire, pour assurer une réponse d'entrée rapide.

pjc50
la source
Ce n'est pas une question de minuterie, c'est une question de chronomètre, je dirais plutôt, ou un compteur de performance. Il faut obtenir le temps réel à un moment donné, et une boucle libre fonctionne parfaitement. Cependant, une minuterie (dans le sens d'une interruption régulière) tente de réguler la vitesse de boucle en utilisation générale. Je ne considère pas cela comme bon. Il est préférable d'utiliser la fonction swap/ present/ flippour attendre le signal vsync, pour réguler la boucle de jeu.
v.oddou