
Cette fois ci, ça va être plus simple, une manière de "souffler" un peu.
Nous allons voir comment on va gérer le temps. En fait, c'est tout simple, nous allons changer légèrement la classe de base et la fonction principale pour donner aux objets mis à jour des informations sur l'écoulement du temps, comme le moment de leur dernière mise à jour, etc etc.
Ça peut être très utile, on ne va donc pas s'en priver. Au passage, nous offrirons la possibilité de limiter les FPS de notre jeu.
C'est parti !!! :D
Oui, nous allons considérer le temps comme un évènement, ces notions sont liées.
Nous allons donc tout naturellement déléguer la gestion du temps au gestionnaire d'évènements.
Nous n'avons besoin que d'une méthode à rajouter : celle qui indique depuis combien de millisecondes le programme est lancé.
A partir de là les autres objets du jeu pourront sans problèmes faire leur gestion.
Bon, il suffit de déléguer le travail à la SDL (elle est là pour faire le sale boulot, nous on encapsule). Il suffit donc de retourner la valeur de SDL_GetTicks.
C'est tellement simple que vous n'avez probablement pas besoin de code, bien que vous pouvez consulter la branche pédagogique du KGG pour voir comment nous gérons cela.
Vous pouvez si vous le désirez, implémenter une sorte de timer : une méthode qui prend en paramètre un pointeur de fonction et un nombre de millisecondes, de manière à ce que la fonction soit lancée N millisecondes plus tard.
Pour cela, vous aurez besoin d'une liste chaînée (je vous renvoie au cours les traitant) de type Tâche, une Tâche représentant la fonction à lancer et le moment où la lancer.
Techniquement, vous devriez donc faire une structure Task avec un unsigned long qui désigne la milliseconde où lancer la fonction, et un pointeur de fonction, et rajouter une liste de Tasks dans votre gestionnaire d'évènements.
A partir de là, c'est facile : la méthode addTask crée une nouvelle Task, y place le pointeur de fonction, et déduit le moment d'exécution en ajoutant le temps actuel avec la durée d'attente avant la fonction.
Ensuite il vous faut modifier la méthode Update de manière à parcourir la liste, à tester les Tasks pour voir s'il faut les exécuter.
Si une task doit être exécutée, on appelle la fonction correspondante, puis on supprime la tâche de la liste (c'est important, ça permet d'accélérer l'exécution du tout).
Vous pouvez même créer un système de Timers à partir du système de Tasks.
La seule différence est que lorsque la fonction du Timer est exécutée, on ne supprime pas celui-ci de la liste, mais on modifie le moment d'exécution pour le repousser encore.
Gérer les FPS est très facile, tout se passe dans la boucle principale.
La première chose à faire est de vérifier s'il reste des évènements à traiter : tant qu'il en reste on les traite.
Nous vous conseillons de faire une fonction à part dans laquelle vous gérerez les évènements.
Ensuite, vous testez à quand remonte le dernier tour de boucle principale. S'il date d'il y a moins de, disons 20 ms, alors ==> on sleep(), puis on reprend la boucle du début. Sinon, on ajourne le moment du dernier tour de boucle, et enfin on redessine l'écran.
Ça vous paraît un peu abstrait ? Voici un schéma :
Tant que le jeu n'est pas fini :
|----Tant que il reste des évènements à traiter :
|----|----traiter un évènement
|----Fin tant que
|----Si (temps actuel - derniere_frame) > 20
|----|----dormir 20 - temps actuel - derniere frame
|----Sinon
|----|----derniere_frame = temps actuel
|----|----Recommencer la boucle
|----Fin si
Fin tant que
C'est ce schéma que nous allons employer.
Il existe une alternative qui consiste en placer la première boucle (pour les évènements) en un thread séparé, mais c'est légèrement plus compliqué.
Ce schéma permet en fait de traiter les évènement dès qu'ils surviennent (ou presque), mais de ne redessiner la fenêtre qu'a certains intervalles, ce qui est le but.
La gestion du temps étant en grande partie déléguée à du code déjà existant, tel que la SDL ou notre gestionnaire d'évènements, il n'y a pas grand chose à ajouter.
De plus, ce n'est pas un concept très compliqué. Une fois qu'il est bien intégré à notre moteur, il faudra l'utiliser judicieusement, ce qui peut se révéler plus difficile.
Nous verrons cela lorsque nous feront du code plus haut niveau, nous détachant un peu de la SDL.