Utilisation du temps d'inactivité dans les jeux au tour par tour (RPG) pour la mise à jour

9

Si vous prenez n'importe quel jeu RPG au tour par tour, il y aura de longues périodes où rien ne se passera parce que le jeu est en boucle sur 'wait_for_player_input'. Naturellement, il semble judicieux d'utiliser ce temps pour mettre à jour les choses.

Cependant, cela semble immédiatement suggérer qu'il devrait être enfilé. Ce type de conception est-il possible dans un seul fil?

loop:  
if not check_something_pressed:  
    update_a_very_small_amount  
else  
  keep going

Mais si nous disons que 'a_very_small_amount' ne met à jour qu'un seul objet à chaque boucle, cela va être très lent à mettre à jour.

Comment procéderiez-vous à ce sujet, de préférence dans un seul fil?

EDIT: J'ai étiqueté ce langage indépendant de la langue car cela semble la chose raisonnable, bien que quelque chose de plus spécifique à Python serait génial. ;-)

Deuxième montage: ce jeu ne prévoit pas de composants animés; c'est-à-dire que je l'exécute actuellement en tant qu'entrée en attente de lecteur, puis tout mettre à jour et dessiner. Donc, plutôt que X FPS, cela dépend de la vitesse de l'utilisateur.

Le canard communiste
la source

Réponses:

7

Oui, c'est possible de le faire en un seul fil. D'une manière générale cependant, vous voudrez mettre à jour les objets à chaque image et pas seulement lorsqu'il y a des cycles de rechange. Vos animations et mouvements seront déconnectés de la fréquence d'images et auront l'air plutôt saccadés si vous ne le faites pas. Si vous parlez davantage de mises à jour de l'IA ou de quelque chose d'autre qui n'a pas besoin d'être en temps réel, je mettrais une minuterie dessus. Vous devez savoir quelle est votre fréquence d'images cible et le temps d'inactivité sera ce qui reste après que tout le reste ait été terminé.

Disons que vous visez 60 FPS pour votre jeu. Cela vous laisse 16,667 ms pour effectuer tout le travail dont vous avez besoin pour faire chaque trame. Au début du jeu, obtenez l'heure actuelle en utilisant le minuteur de résolution le plus élevé disponible, ajoutez-y 16,667 ms et stockez-le quelque part. Je pense que la fonction en python est time () même si cela fait un moment que je n'ai pas travaillé dans le langage. Une fois votre traitement terminé, entrez une boucle qui compare l'heure actuelle à l'heure que vous avez enregistrée. Si l'heure actuelle est inférieure à l'heure de fin de la trame, update_a_very_small_amount. Je ne m'inquiéterais pas beaucoup du traitement dépassant la fin du cadre, car votre petite mise à jour devrait être rapide à traiter. Ce ne serait qu'un léger retard au début de l'image suivante et vous semblez avoir suffisamment de temps d'inactivité pour le gérer.

Une fois le traitement de la trame terminé, ajoutez 16,667 ms au temps qui a été stocké pour la fin de la dernière trame pour savoir où la fin de la trame suivante doit être. Si vous utilisez l'heure actuelle + 16,667 ms et que le traitement se termine, la fin de la trame suivante sera repoussée par autant de temps que la dernière trame s'est écoulée.

Re: Second Edit

Pour clarifier, j'utilise ici le terme fréquence d'images pour indiquer une itération à travers la boucle principale. Si c'est basé sur la vitesse d'entrée de l'utilisateur, j'imagine que votre objectif est simplement de rendre le jeu réactif. Sinon, vous pouvez simplement vérifier l'entrée et tout mettre à jour à chaque fois dans la boucle, même si cela prend 10 secondes pour le faire. Pour le rendre réactif, vous voudrez probablement vérifier l'entrée environ 20 fois par seconde, ce qui donne une fréquence d'images effective de 20 FPS, même si vous ne dessinez pas réellement ces images. Cela vous donnerait 50 ms pour mettre à jour les choses avant de devoir vérifier à nouveau la saisie.

Joel Baker
la source
2

Pour Python en particulier, vous pouvez essayer d'utiliser Coroutines pour effectuer des calculs sur plusieurs appels de mise à jour.

... les coroutines sont des composants de programme qui généralisent les sous-programmes pour permettre plusieurs points d'entrée pour suspendre et reprendre l'exécution à certains emplacements ...

PEP-0342 détaille l'implémentation de la coroutine en Python.

Vous pouvez créer votre propre planificateur et tâches qui peuvent simuler le multithreading sur un seul thread. C'est de la même manière que les frameworks Javascript permettent un traitement multithread comme même si Javascript est exécuté sur un seul processus.

David Young
la source
1

Oui c'est possible. Votre jeu aura une sorte de boucle de jeu principale. Quelque chose comme ça:

while(gameRunning)
{
  checkUserInput()
  updateGame()
  renderGame()
}

Dans updateGame, vous pouvez parcourir vos objets de jeu et les mettre à jour "un peu". Si vous effectuez un calcul lourd dans cette méthode, le jeu se bloquera simplement. Vous avez donc besoin d'un moyen de fractionner ces calculs pour exécuter plusieurs itérations de la boucle de jeu.

La façon de les diviser dépend du type de jeu que vous construisez. Supposons que vous ayez une routine de recherche de chemin qui utilise A * pour calculer un chemin à travers un labyrinthe. Vous devrez arrêter l'algorithme après un certain temps ou après un nombre fixe d'itérations, conserver le chemin calculé jusqu'à présent et retourner le contrôle à la boucle de jeu. La recherche de chemin se poursuivra la prochaine fois que updateGame sera appelé. Vous pouvez même déplacer le personnage le long du chemin partiel pendant qu'il est encore en cours de calcul.

La plupart du temps, vous n'avez pas à vous soucier que l'appel à updateGame prenne trop de temps.

"L'optimisation prématurée est la racine de tout Mal!"
Si vous rencontrez un goulot d'étranglement des performances dans votre routine de mise à jour, c'est le moment d'enquêter et d'optimiser. Comme vous ne pouvez pas savoir à l'avance quelles parties de votre jeu prendront le plus de temps à calculer et pourraient perdre du temps à optimiser les mauvaises choses.

Stephen
la source
1

D'après ce que je comprends de votre question, vous posez essentiellement des questions sur le multitâche coopératif.

L'infrastructure de base serait une liste de pointeurs de fonctions, tous appelés de manière circulaire (à moins qu'une priorisation plus sophistiquée ne soit requise), et toutes ces fonctions feraient "un peu de travail", suffisamment petites pour ne pas provoquer le jeu. devenir lent. Je l'ai fait sous DOS avant que les threads ne soient chauds, et également sur les appareils mobiles qui ne prenaient pas en charge le threading.

Une meilleure question, à mon avis, est de savoir quoi faire en attendant que le joueur bouge. Quels types de choses seraient si gourmandes en ressources processeur qu'elles ne pourraient de toute façon pas être exécutées à la volée, tout en étant si optionnelles que si le joueur martelait juste une touche de déplacement assez rapidement, le jeu n'avait pas le temps de les traiter. Ainsi, des trucs comme calculer A * pour quelques milliers d'IA sont sortis.

La meilleure solution, à mon avis, consiste simplement à céder / dormir et à laisser la gestion de l'alimentation de l'ordinateur faire son travail. Les utilisateurs d'ordinateurs portables vous apprécieront mieux pour cela.

Jari Komppa
la source