18 mar. 2008

Questions sur XNA : implémenter un système de pause

De temps en temps, vous m'adressez directement des questions sur différents sujets. Aujourd'hui, j'inaugure une série d'articles qui répondent à des questions concernant XNA. Et je commence par une question de Ludovic.

Si vous avez des questions portant sur XNA, n'hésitez pas à m'envoyer un mail à question@emmanueldeloget.com.

Question de Ludovic:

Salut, j'ai vu que sur ton site tu exposais plusieurs articles sur XNA et j'aurais une petite question. Je souhaite faire une pause après appuie sur mon bouton dans mon jeu XNA que je suis en train de développer mais je ne vois pas comment mettre en pause mon jeu, vu que les méthodes update() sont appelés en boucle ! Peux tu m'aider ?

Ca devrait pouvoir se faire.

La notion de pause dans un jeu (qu'il soit XNA ou pas) n'implique pas que la boucle principale soit stoppée, mais que les traitement qui sont effectués dans cette boucle ne modifie pas l'état courant du jeu (le modèle). Pourquoi ? PArce que arrêter la boucle d'update, c'est comme arrêter le temps : on ne sait jamais combien de temps ça dure (ou, plus être plus explicite : comment faire pour stopper la pause sans effectuer au moins certaines des tâches liées à l'Update(), telle que la récupération des entrées utilisateur ?). Ce n'est donc pas la méthode à utiliser.

Dans l'idéal, il faut réunir les deux conditions suivantes:

  1. avoir un booléen quelque part qui nous dit si le jeu est en pause ou non; ce booléen doit pouvoir être mis à jour en fonction de certaines conditions.
  2. faire en sorte que le temps ne défile pas pendant que le jeu est en pause.

(1) est facile à obtenir (private bool paused;). (2) n'est pas du tout prévu dans le framework XNA : il faut donc, en se basant sur le système offert par XNA (le GameTime passé en paramètre à Update()), gérer le temps du jeu par nous même. Ce n'est pas forcément complexe.

Considérons pour faire simple que le GameTime donné pas XNA est un temps système (le laps de temps depuis lequel l'exécutable du jeu a été lancé). Nous pouvons construire un temps de simulation à partir de ce temps système, en gardant quelque part la durée pendant laquel le jeu a été en pause.

public class SimulationTime
{
  // imitation de GameTime
  private TimeSpan totalRealTime;
  private TimeSpan totalGameTime;
  private TimeSpan elapsedRealTime;
  private TimeSpan elapsedGameTime;
  private bool isRunningSlowly;
// gestion de la pause private TimeSpan gameTimePause = TimeSpan.Zero; private TimeSpan realTimePause = TimeSpan.Zero; private bool paused = true; // au début: jeu en pause
// propriétés public bool Paused { get { return paused; } set { paused = value; } } public TimeSpan TotalRealTime { get { return realTime - realTimePause; } } public TimeSpan TotalGameTime { get { return gameTime - gameTimePause; } } public TimeSpan ElapsedGameTime { get { return elapsedGameTime; } } public TimeSpan ElapsedRealTime { get { return elapsedRealTime; } } public bool IsRunningSlowly { get { return isRunningSlowly; } }
// à appeler dans Game1.Update() public void Update(GameTime systemTime) { // première initialisation - rien d'aurte à faire totalRealTime = systemTime.TotalRealTime; totalGameTime = systemTime.TotalGameTime; elapsedRealTime = systemTime.ElapsedRealTime; elapsedGameTime = systemTime.ElapsedGameTime; isRunningSlowly = systemTime.IsRunningSlowly;
if (paused) { gameTimePause = gameTimePause + elapsedGameTime; realTimePause = realTimePause + elapsedRealTime; } } }

La classe SimulationTime est instanciée une fois (soit dans l'instance de Game, soit dans l'instance de l'état qui gère le jeu (par exemple, l'état "jeu en cours" d'une machine à état). Ensuite, à chaque Update(), on appelle simulationTime.Update(gameTime); Pour toutes les opérations du jeu qui dépendent d'un temps (mise à jour des animations ou des timers spécifiques du modèle du jeu), on utilise simulationTime à la place de gameTime.

Si on veut mettre le jeu en pause ou repasser en mdoe actif: simulationTime.Paused = true; ou simulationTime.Paused = false;

Si on veut tester la pause: if (simulationTime.Paused) { ...

On en revient à l'idée de base : lorsque le jeu est en pause, son modèle ne doit pas être mis à jour. Cela ne signifie pas que la boucle principale est stoppée, puisque la musique doit continue à jouer, les entrées utilisateurs doivent être lues (ne serait-ce que pour mettre fin à la pause), et il est nécessaire de continuer à mettre l'écran à jour (bien que Draw() soit indépendant d'Update(), certaines quantités calculées dans cette méthode peuvent être utiles à l'affichage).

Commentaires

1. Le mardi, mars 18 2008, 18:48 par hugobosscool26

Merci de la réponse encore une fois :)

Ajouter un commentaire

Les commentaires peuvent être formatés en utilisant une syntaxe wiki simplifiée.

Fil des commentaires de ce billet