Vally8

Utilisation du timer

Pour le quatrième tutorial, nous allons voir comment utiliser le timer pour effectuer une action à vitesse constante.
Nous en profiterons pour voir une possibilité un peu plus évoluée du blitting.

A quoi peut bien sevir le timer ?? Et bien c'est simple, quand vous faites un jeu, vous aimez bien qu'il y ai des choses qui bougent dans le jeu (ben oui, sinon il est pas très interressant ce jeu !)
Or voila, avec la diversité des PC, selon la machine sur lequel le jeu tournera, il risque d'être bien trop lent, ou au contraire, bien trop rapide pour pouvoir être joué !!
Ce qu'on fait donc, c'est qu'on synchronise le jeu à l'aide d'un timer, et la majorité du temps se fera à attendre qu'il soit temps de rendre une nouvelle image.

Voyons maintenant comment SDL gère le timer, et bien ce n'est pas bien plus compliqué que le reste.
Pour commencer, il faut initialiser le timer avec la maintenant célèbre fonction SDL_Init (ou SDL_InitSubsystem), puis il ne reste plus qu'à demander l'"heure" grace à la fonction SDL_GetTicks.

L'algo est donc très simple :

  • On décide d'un fréquence de mise à jour de l'image, dans notre exemple c'est 50 images par secondes, soit 20 mili-secondes entre chaque affichage.

  • On demande et on sauvegarde le temps courant.

  • On fait notre rendu.

  • On redemande le temps courant et on boucle tant qu'il n'est pas passé asser de temps pour le prochain rendu.

  • On reprend au premier point.

Noter que cet algorithme est celui utilisé dans la majorité des jeux en 2D (jeux NES, Game Boy, ...), mais qu'il a quelques défauts qui le rendent inadaptés aux jeux en 3D, pour tous les jeux en 3D, c'est un autre algorithme qui est utilisé.

Pour ce qui est du blitting, nous verrons comment blitter une surface dans une partie seulement de notre zone d'affichage.
c'est en fait très simple, vous vous souvenez de la fonction SDL_BlitSurface ? Je vous avez dit qu'elle avait des tas de possibilités ! Et bien en voila une :
Et bien son dernier paramètre, s'il n'est pas nul, donne le rectangle à l'écran ou copier la surface.
Mais attention, seul les valeurs x et y de ce rectangle sont valides, la largeur et la hauteur restent celle de la surface.

Il reste encore un problème à résoudre, vous vous souvenez de la fonction SDL_WaitEvent du tutorial precédant ? Elle attendait qu'un evenement arrive pour nous rendre la main.
Cela risque d'être très gênant car si aucun évenement n'arrive, nous ne quittons pas la fonction SDL_WaitEvent nous ne pouvons pas effectuer de rendus, ni même vérifier s'il est temps d'en effectuer un.
D'un autre coté, on ne peut pas se passer de la fonction de récupération des messages !
Pour résoudre ce problème, SDL met à notre disposition la fonction SDL_PollEvent, qui, s'il n'y a pas de messages en attente, rend tout de suite la main.
Nous l'utiliserons donc à partir de maintenant à la place de la fonction SDL_WaitEvent.

Voila le source complet de ce quatrième tutorial :


#include <stdio.h>
#include <stdlib.h>
#include "SDL.h"


SDL_Surface *loadBmp(const char *fichier)
{
	SDL_Surface *image;

	/* Chargement du bitmap "fichier" en memoire dans la surface image */
	image = SDL_LoadBMP(fichier);
	if ( image == NULL )
	{
		fprintf(stderr, "Echec de chargement du fichier %s : %s.\n", fichier, SDL_GetError());
		return NULL;
	}
	
	/* Verification du format de l'image */
	if ( (image-&gt;w != 160) || (image-&gt;h != 160) )
	if ( image == NULL )
	{
		fprintf(stderr, "L'image du fichier %s doit être de taille 160*160 pixels.\n", fichier);
		SDL_FreeSurface(image);
		return NULL;
	}
	
	return image;
}

int main()
{

	SDL_Surface *ecran, *images[2];
	SDL_Event event;
	int bFin = 0;
	Uint32 temps_courant, prochain_rendu = 0;
	
	/* rectangle de destination du blit */
	SDL_Rect dest;
	
	/* position de l'objet numéro un*/
	int pos;
	
	/* tableau des positions possibles des objets */
	Sint16 x_pos[160];
	Sint16 y_pos[160];
	
	/* initialisation des tableaux */
	for (pos = 0; pos &lt; 40; pos++)
	{
		x_pos[pos]     = pos*4;
		x_pos[pos+40]  = 160;
		x_pos[119-pos] = pos*4;
		x_pos[120+pos] = 0;
		
		y_pos[pos]     = 0;
		y_pos[pos+40]  = pos*4;
		y_pos[pos+80]  = 160;
		y_pos[159-pos] = pos*4;
	}
	
	atexit(SDL_Quit);
	
	/* initialisation de SDL_Video */
	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) &lt; 0)
	{
		fprintf(stderr, "Echec d'initialisation de SDL.\n");
		return 1;
	}
	printf("Bonjour le monde, SDL est initialisé avec succès.\n");
	
	
	/* creation d'une fenetre avec une zone d'affichage de 320*320 pixels, une profondeur de bits 
  egale à celle du serveur X ou de l'explorateur */
	ecran = SDL_SetVideoMode(320, 320, 0, SDL_ANYFORMAT);
	if ( ecran == NULL )
	{
		fprintf(stderr, "Echec de creation de la fenetre de 160*160 : %s.\n", SDL_GetError());
		return 1;
	}
	
	if ( !(images[0] = loadBmp("image1.bmp")) )
	{
		/* on libère SDL et on quitte */
		SDL_Quit();
		return 1;
	}
	
	if ( !(images[1] = loadBmp("image2.bmp")) )
	{
		/* on libère l'image déjà chargée */
		SDL_FreeSurface(images[0]);
		
		/* on libère SDL et on quitte */
		SDL_Quit();
		return 1;
	}

	
	pos = 0;
	dest.w = dest.h = 160;
	
	while (!bFin)
	{
		while (SDL_PollEvent(&event))
		{
			switch (event.type)
			{
			case SDL_KEYDOWN:
				switch (event.key.keysym.sym)
				{
				case SDLK_ESCAPE:
					bFin = 1;
					break;
				
				default:
					printf("Une touche à été pressée.\n");
				}
				break;
			
			case SDL_QUIT:
				bFin = 1;
				break;
			
			default:
				;
			}
		}
		
		/* On recupère le temps écoulé en mili-secondes */
		temps_courant = SDL_GetTicks();
		
		/* On regarde s'il est temps d'effectuer le prochain rendu */
		if (temps_courant > prochain_rendu)
		{
			/* un rendu toutes les 20 milli-secondes = 50 images par secondes */
			prochain_rendu = temps_courant + 20;
		
			/* On efface l'écran */
			SDL_FillRect(ecran, NULL, 
				ecran->format->Rmask | ecran->format->Gmask | ecran->format->Bmask );
			
			dest.x = x_pos[pos];
			dest.y = y_pos[pos];
			SDL_BlitSurface(images[0], NULL, ecran, &dest);
			
			dest.x = x_pos[(pos+80)%160];
			dest.y = y_pos[(pos+80)%160];
			SDL_BlitSurface(images[1], NULL, ecran, &dest);

			/* Mise à jour de la zone d'affichage de la fenetre */
			SDL_UpdateRect(ecran, 0, 0, 0, 0);

			pos++;
			if (pos >= 160)
				pos = 0;
		}
		
	}

	SDL_FreeSurface(images[0]);
	SDL_FreeSurface(images[1]);
	
	SDL_Quit();
	return 0;
}



Voilà le lien avec le fichier source, le makefile et les images : timer.tar.bz2

un problème ??? On peut en parler sur le forum

Retour à l'accueil
compteur

Valid HTML 4.01! Valid CSS Valid RSS feed.