Créons un jeu de plateformes de A à Z !
Tutoriel présenté par : Jérémie F. Bellanger
Dernière mise à jour : 08 novembre 2010
Difficulté :
6. Gérer le scrolling de la map
Vous avez toujours rêvé de scroller sans jamais savoir comment faire ? Eh bien, c'est votre jour de chance !
On va en effet rajouter un scrolling à notre map, que nous dirigerons au clavier (pour l'instant), et ça va être rapide et super facile ! Si, si, faites-moi confiance !
On va en effet rajouter un scrolling à notre map, que nous dirigerons au clavier (pour l'instant), et ça va être rapide et super facile ! Si, si, faites-moi confiance !
Résultat à la fin de ce chapitre : on scrolle la map au clavier !
Alors tout d'abord, vous allez télécharger une nouvelle map, à enregistrer dans le dossier du projet (Aron) dans le sous-dossier map à la place de l'ancienne. Celle-ci est déjà préconçue pour vous permettre de scroller (un peu) (N.B. : La map se trouve dans le projet correspondant au chapitre dans l'archive téléchargeable) :
Voilà une bonne chose de faite, nous pouvons donc maintenant modifier notre code !
Première chose à faire, nous allons rajouter la fonction update() dans la boucle de notre main pour traiter les inputs du clavier et permettre à la map de scroller.
Première chose à faire, nous allons rajouter la fonction update() dans la boucle de notre main pour traiter les inputs du clavier et permettre à la map de scroller.
Nom du fichier : main.c
#include "main.h" int main( int argc, char *argv[ ] ) { unsigned int frameLimit = SDL_GetTicks() + 16; int go; /* Initialisation de la SDL dans une fonction séparée (voir après) */ init("Aron"); /* Appelle la fonction cleanup à la fin du programme */ atexit(cleanup); go = 1; /* Boucle infinie, principale, du jeu */ while (go == 1) { /* On vérifie l'état des entrées (clavier puis plus tard joystick) */ getInput(); /* On met à jour le jeu */ update(); /* On affiche tout */ draw(); /* Gestion des 60 fps ( 60 images pas seconde : soit 1s ->1000ms/60 = 16.6 -> 16 On doit donc attendre 16 ms entre chaque image (frame) */ delay(frameLimit); frameLimit = SDL_GetTicks() + 16; } /* Exit */ exit(0); } |
Et nous rajouterons donc le prototype de cette fonction dans main.h (vous devriez commencer à avoir l'habitude maintenant ) :
Nom du fichier : main.h
extern void update(void); |
On va maintenant implémenter cette fonction dans le fichier input.c en-dessous de getInput() :
Nom du fichier : input.c
void update(void) { if (input.left == 1) { map.startX -= TILE_SIZE; if (map.startX < 0) { map.startX = 0; } } else if (input.right == 1) { map.startX += TILE_SIZE; if (map.startX + SCREEN_WIDTH >= map.maxX) { map.startX = map.maxX - SCREEN_WIDTH; } } if (input.up == 1) { map.startY -= TILE_SIZE; if (map.startY < 0) { map.startY = 0; } } else if (input.down == 1) { map.startY += TILE_SIZE; if (map.startY + SCREEN_HEIGHT >= map.maxY) { map.startY = map.maxY - SCREEN_HEIGHT; } } } |
Comme vous pouvez le voir, cette fonction est on ne peut plus simple.
On teste s'il y a un appui sur les touches fléchées haut, bas, gauche, droite du clavier (à l'aide des variables input mises à jour par la fonction getInput() ) et en fonction de ça on scrolle de la valeur de TILE_SIZE, c'est-à-dire de 32 pixels. Vous pouvez faire varier cette valeur pour un scrolling plus ou moins rapide .
Par sécurité, on vérifie aussi, si on ne sort pas de la map, en calant le scrolling à sa valeur max si les coordonnées de la carte (map.startX et map.startY) les atteignent ou les dépassent.
Et n'oublions pas d'ajouter dans input.h :
Il ne reste maintenant plus qu'à mettre à jour notre fonction drawMap() :
On teste s'il y a un appui sur les touches fléchées haut, bas, gauche, droite du clavier (à l'aide des variables input mises à jour par la fonction getInput() ) et en fonction de ça on scrolle de la valeur de TILE_SIZE, c'est-à-dire de 32 pixels. Vous pouvez faire varier cette valeur pour un scrolling plus ou moins rapide .
Par sécurité, on vérifie aussi, si on ne sort pas de la map, en calant le scrolling à sa valeur max si les coordonnées de la carte (map.startX et map.startY) les atteignent ou les dépassent.
Et n'oublions pas d'ajouter dans input.h :
extern Map map; |
Il ne reste maintenant plus qu'à mettre à jour notre fonction drawMap() :
Nom du fichier : map.c
void drawMap(void) { int x, y, mapX, x1, x2, mapY, y1, y2, xsource, ysource, a; /* On initialise mapX à la 1ère colonne qu'on doit blitter. Celle-ci correspond au x de la map (en pixels) divisés par la taille d'une tile (32) pour obtenir la bonne colonne de notre map Exemple : si x du début de la map = 1026, on fait 1026 / 32 et on sait qu'on doit commencer par afficher la 32eme colonne de tiles de notre map */ mapX = map.startX / TILE_SIZE; /* Coordonnées de départ pour l'affichage de la map : permet de déterminer à quels coordonnées blitter la 1ère colonne de tiles au pixel près (par exemple, si la 1ère colonne n'est visible qu'en partie, on devra commencer à blitter hors écran, donc avoir des coordonnées négatives - d'où le -1). */ x1 = (map.startX % TILE_SIZE) * -1; /* Calcul des coordonnées de la fin de la map : jusqu'où doit-on blitter ? Logiquement, on doit aller à x1 (départ) + SCREEN_WIDTH (la largeur de l'écran). Mais si on a commencé à blitter en dehors de l'écran la première colonne, il va falloir rajouter une autre colonne de tiles sinon on va avoir des pixels blancs. C'est ce que fait : x1 == 0 ? 0 : TILE_SIZE qu'on pourrait traduire par: if(x1 != 0) x2 = x1 + SCREEN_WIDTH + TILE_SIZE , mais forcément, c'est plus long ;)*/ x2 = x1 + SCREEN_WIDTH + (x1 == 0 ? 0 : TILE_SIZE); /* On fait exactement pareil pour calculer y */ mapY = map.startY / TILE_SIZE; y1 = (map.startY % TILE_SIZE) * -1; y2 = y1 + SCREEN_HEIGHT + (y1 == 0 ? 0 : TILE_SIZE); /* Dessine la carte en commençant par startX et startY */ /* On dessine ligne par ligne en commençant par y1 (0) jusqu'à y2 (480) A chaque fois, on rajoute TILE_SIZE (donc 32), car on descend d'une ligne de tile (qui fait 32 pixels de hauteur) */ for (y = y1; y < y2; y += TILE_SIZE) { /* A chaque début de ligne, on réinitialise mapX qui contient la colonne (0 au début puisqu'on ne scrolle pas) */ mapX = map.startX / TILE_SIZE; /* A chaque colonne de tile, on dessine la bonne tile en allant de x = 0 à x = 640 */ for (x = x1; x < x2; x += TILE_SIZE) { /* Suivant le numéro de notre tile, on découpe le tileset */ a = map.tile[mapY][mapX]; /* Calcul pour obtenir son y (pour un tileset de 10 tiles par ligne, d'où le 10 */ ysource = a / 10 * TILE_SIZE; /* Et son x */ xsource = a % 10 * TILE_SIZE; /* Fonction qui blitte la bonne tile au bon endroit */ drawTile(map.tileSet, x, y, xsource, ysource); mapX++; } mapY++; } } |
Ici, on efface les calculs simplifiés de l'affichage de la map et on les remplace par les calculs en commentaires.
Les explications sont fournies directement ligne par ligne ci-dessus.
Pour faire court, on calcule par quelles colonne et ligne de tiles on doit commencer l'affichage, selon les valeurs de map.startX et map.startY (qui augmentent/diminuent selon les inputs) et ensuite on calcule où commencer à blitter la première colonne et la première ligne (parfois hors-écran selon le scrolling). Enfin, on calcule où arrêter l'affichage de la map : si la 1ère ligne/colonne a été blittée hors écran, il va en effet falloir blitter une ligne/colonne de plus.
Vous pouvez vous amuser à changer ces calculs pour mieux comprendre comment cela fonctionne (ça peut paraître un peu difficile au début, c'est normal ). Supprimez, par exemple : (x1 == 0 ? 0 : TILE_SIZE) pour voir son importance !
Eh voilà notre scrolling est terminé (quoi ? déjà !!!), on est maintenant prêt à compiler... BUILD... RUN et tadaaaaam !Les explications sont fournies directement ligne par ligne ci-dessus.
Pour faire court, on calcule par quelles colonne et ligne de tiles on doit commencer l'affichage, selon les valeurs de map.startX et map.startY (qui augmentent/diminuent selon les inputs) et ensuite on calcule où commencer à blitter la première colonne et la première ligne (parfois hors-écran selon le scrolling). Enfin, on calcule où arrêter l'affichage de la map : si la 1ère ligne/colonne a été blittée hors écran, il va en effet falloir blitter une ligne/colonne de plus.
Vous pouvez vous amuser à changer ces calculs pour mieux comprendre comment cela fonctionne (ça peut paraître un peu difficile au début, c'est normal ). Supprimez, par exemple : (x1 == 0 ? 0 : TILE_SIZE) pour voir son importance !
Appuyez sur les touches fléchées du clavier, et ça scrolle !!!
Et maintenant, prochaine étape : un level editor !!