Les timers avec Allegro

Un article de Mangue.org, l'encyclopéde libre.


Lorsque l'on programme un jeu, il est toujours agréable de pouvoir contrôler le temps, pour par exemple limiter l'utilisation d'un bonus à 10 secondes, ou bien encore afficher le temps de jeu. C'est à cela que servent les timers d'allegro, et le lot de fonctions qui servent à les utiliser. Nous allons donc apprendre à nous servir de cette importante possibilité d'Allegro dans ce cours. Il est à noter que ce cours ne propose pas d'exemples limités à une fonctionnalités, car ils ne seraient pas représentatifs d'une bonne utilisation des timers, mais par contre propose en derniére partie un exemple complet d'implémentation d'un timer.


Les Timers

La toute premiére chose à faire dans un programme pour utiliser les timers, est de les initialiser. Pour cela, il faut appeller la fonction install_timer() en début de programme. A ce stade de la procédure, pratiquement la moitié du travail est réalisée.

D'un point de vue théorique, un timer permet d'appeller une fonction à fréquence fixe. La plupart du temps, cette fonction permet d'incrémenter une variable, variable qui constitue donc l'image du timer (par exemple, si un timer appelle à 1 seconde d'intervalle une fonction qui incrémente une variable, cette variable contiendra le temps en secondes depuis l'installation du timer). Maintenant tréve de bavardages, passons à la pratique.

L'installation d'un timer se réalise par la fonction install_int_ex(void (*proc)(), int speed). Celle-ci prend 2 arguments :

  • Le premier est un pointeur vers la fonction qui sera appellée à intervalles fixes
  • Le second est la fréquence à appliquer à cet appel. Celui-ci est paramétré grâce aux macros suivantes :
SECS_TO_TIMER(secs) //donne le nombre de secondes entre deux appels de la fonction
MSEC_TO_TIMER(msec) //donne le nombre de millisecondes entre deux appels de la fonction
BPS_TO_TIMER(bps) //donne le nombre d'appels de la fonction chaque seconde
BPM_TO_TIMER(bpm) //donne le nombre d'appels de la fonction par minute
 
/*
 Exemples :
 */
//Appelle la fonction ma_fonction() toutes les 2 secondes
install_int_ex(ma_fonction,SECS_TO_TIMER(2));
//Appelle la fonction ma_fonction() toutes les 10 millisecondes
install_int_ex(ma_fonction,MSEC_TO_TIMER(10));
//Appelle la fonction ma_fonction() 2 fois par seconde
install_int_ex(ma_fonction,BPS_TO_TIMER(2));
//Appelle la fonction ma_fonction() 4 fois par minute
install_int_ex(ma_fonction,BPM_TO_TIMER(4));


Limites des timers

Bien souvent, le corollaire de la puissance est la limitation. Les timers d'allegro ne dérogent pas à cette régle, du fait qu'ils utilisent pour fonctionner les interruptions dos. C'est ainsi qu'il y a un certain nombre de régles à respecter dans l'utilisation des timers. Ces régles sont données ci-dessous.

En premier lieu, les données accédées par le timer doivent faire l'objet d'une attention toute particuliére. Ainsi :

  • La déclaration de la fonction appellée par le timer doit être suivie par la macro END_OF_FUNCTION(nom_de_la_fonction)
  • Cette fonction doit être "lockée" par l'appel à la macro LOCK_FONCTION(nom_de_la_fonction)
  • Les variables modifiées par la fonction doivent être "lockées" par la macro LOCK_VARIABLE(nom_de_la_variable), et doivent être définies comme volatiles

Ensuite, la fonction appellée par le timer posséde un certain nombre de restrictions dans son code même, qu'il est inutile de détailler ici. En conséquence, il est impératif d'utiliser cette fonction uniquement pour modifier des drapeaux (flag en anglais), ou incrémenter/décrémenter une variable de type entiére.

Troisiémement, allegro limite le nombre de timers créés à 16. De plus, un certain nombre de fonctions d'allegro (par exemple les fonction de la GUI, l'affichage du pointeur de souris, etc.) utilisent des timers. Si un timer ne peut être installé, la fonction install_int_ex() renvoie un nombre inférieur à 0. Sinon, elle renvoie 0. Il est toujours possible de désinstaller un timer par un appel à la fontion remove_int().

Enfin, lorsque nous utilisons des timers dans un programme, il est impératif de ne pas utiliser les fonctions relatives au temps de l'ANSI C. La fonction delay() sera donc remplacée par la fonction rest(), qui prend comme unique paramétre le nombre de millisecondes à attendre.


Maintenir une vitesse de jeu constante

Lorsque l'on réalise un jeu, il peut être embétant que ce jeu s'éxecute plus rapidement sur des ordinateurs plus puissant que l'ordinateur de développement. C'est pourquoi il peut être intéressant de fixer la vitesse du jeu à une constante.

Pour cela, il nous faut séparer, dans notre jeu, la partie affichage (affichage des sprites, des explosions, etc.) de la partie logique (gestion des collisions, gestion des contrôles, etc.), en les contenant respectivement dans les fonctions affichage() et logique(). Ensuite, pour avoir une vitesse de jeu constante, il faut que notre fonction logique soit exécutée un certain nombre de fois par seconde (par exemple 60), et qu'ensuite la fonction d'affichage soit exécutée quand nous le pouvons. Cela donne le code suivant :

Réponse
La variable dessiner ne sert qu'à empêcher d'éxécuter 2 fois de suite la fonction affichage, ce qui serait inutile puisque seul un appel à la fonction logique() permet de modifier ce qui est affiché à l'éran.
volatile int timer=0;
 
void affichage()
{
    //Affichages...
}
 
void logique()
{
	//Collisions, etc.
}
 
void incrementer_timer()
{
	timer++;
}
END_OF_FUNCTION(incrementer_timer);
 
void jeu()
{
	int temps_de_jeu=0, dessiner=1;
 
	LOCK_VARIABLE(timer);
	LOCK_FUNCTION(incrementer_timer);
 
	install_int_ex(incrementer_timer,BPS_TO_TIMER(60));
	while(!fin_du_jeu)
	{
		//logique() est exécutée jusqu'à ce que temps_de_jeu ai "rattrapé" le timer
		while(temps_de_jeu < timer)
		{
			logique();
			temps_de_jeu++;
			dessiner=1;
		}
		//Ensuite nous pouvons afficher
		if(dessiner)
		{
			affichage();
			dessiner=0;
		}
	}
	remove_int(incrementer_timer);
}
Outils personels