Big Tuto SDL 2 : Rabidja v. 3.0

Chapitre 5 : Avec un background, c'est mieux !

 

Tutoriel présenté par : Jérémie F. Bellanger (Jay81)
Dernière mise à jour : 29 juin 2015
Date de révision : 30 mai 2016

 

      Prologue

 

   Eh, voilà ! smiley Quelle magnificence ! cool Quelle fenêtre ! yes Mes aïeux ! heart

   Que... quoi ?! angel Vous trouvez que le monochrome noir, c'est moche ! laugh Mince alors ! Moi, qui espérais m'arrêter ici ! devil

   Non, je plaisante ! Bien sûr qu'on va la remplir notre bonne vieille fenêtre ! Et pour vous montrer ma détermination, on va tout de suite se lancer dans l'affichage du background ! cool

   Mais c'est quoi le background ? frown

   Non, ne me dites pas que vous êtes nul en anglais, à ce point là !?! angel Ah, ça me rassure ! Eh bien, ça va tout simplement être le décor de fond de notre map, qui va rester statique. 

   Pour cela, il va donc nous falloir apprendre à charger, puis décharger une image à partir d'un fichier, et à l'afficher ! Non, ça ne va pas être bien dur. wink

   Alors, on y va ! Banzaï ! laugh

   Quoi ? Stop ? sad
   Ah oui, j'allais oublier ! Comment peut-on afficher un background si on n'en a pas ? cheeky
  Deux solutions s'offrent à vous : soit vous dessinez le vôtre (format 800 x 480 pixels, la taille de notre fenêtre), soit vous prenez celui-là et vous le copiez dans le répertoire de votre projet dans le dossier graphics, sous le nom background.png (c'est important de respecter ce nom pour pouvoir le charger dans notre programme ensuite):

 

background.png (faites en clic droit pour sauvegarder l'image wink)

 

   Mais, il est plus grand que 800 x 480, ce background ?!? surprise

   Eh oui, mon cher Watson ! En effet, il est 2 fois plus long, car il peut aussi se mettre en boucle et défiler en parallaxe avec la map pour donner une impression de profondeur, fort sympathique ! smiley

   Mais, on verra tout ça plus loin. Pour l'instant, on va se contenter de l'afficher tel quel, et on n'en verra donc que la moitié wink (mais est-ce la meilleure moitié ? angel - Bon, faut arrêter de se poser des questions là ! cheeky).

 

 

 

      Le code

   Nous allons commencer par introduire une nouvelle structure, dans notre header (en-tête) structs.h :

 

Fichier : structs.h

// Structure pour gérer la map à afficher (à compléter plus tard)
typedef struct Map
{
 
SDL_Texture *background;
 
} Map;

 

   Il s'agit de la structure Map, que nous utiliserons, bien entendu dans le fichier du même nom (map.c) et qui contiendra les variables relatives à notre map.

 

   Mais c'est quoi une map ? angel

   Cela veut dire carte, en anglais, et cela représentera nos niveaux, car comme sur une carte, nous aurons différents éléments placés à diverses coordonnées bien précises. wink

   Ici, notre première variable sera une SDL_Texture qui contiendra l'image de notre background ci-dessus.

   Passons à notre main().

 

Fichier : main.c

//Rabidja 3 - nouvelle version intégralement en SDL 2.0
//Copyright / Droits d'auteur : www.meruvia.fr - Jérémie F. Bellanger
 
#include "prototypes.h"
 
/* Déclaration des variables / structures utilisées par le jeu */
Input input;
 
 
int main(int argc, char *argv[])
{
unsigned int frameLimit = SDL_GetTicks() + 16;
int go;
 
// Initialisation de la SDL
init("Rabidja 3 - SDL 2 - www.meruvia.fr");
 
// Chargement des ressources (graphismes, sons)
loadGame();
 
// Appelle la fonction cleanup à la fin du programme
atexit(cleanup);
 
go = 1;
 
// Boucle infinie, principale, du jeu
while (go == 1)
{
//Gestion des inputs clavier
gestionInputs(&input);
 
//On dessine tout
drawGame();
 
// Gestion des 60 fps (1000ms/60 = 16.6 -> 16
delay(frameLimit);
frameLimit = SDL_GetTicks() + 16;
}
 
// On quitte
exit(0);
 
}

 

   Vous pouvez supprimer le code précédent et le remplacer par celui-là. wink

   Vous noterez simplement 2 changements : on a 2 nouvelles fonctions :

- loadGame() qui aura pour tâche de charger toutes les ressources de notre jeu (pour l'instant, simplement le background cheeky).

- drawGame() qui affichera toutes les tiles et tous les sprites du jeu (soit tous les éléments graphiques).

 

Tile, sprite ? Kézako ? surprise
En général, un sprite est un élément mobile du jeu qui interagit avec d'autres sprites (par exemple : le héros, les monstres, les power-ups, etc.). Au contraire, une tile est un élément statique du décor représentant une petite partie du niveau (nos tiles feront par exemple 32x32 pixels, ce qui est une taille standard dans l'industrie du jeu vidéo wink). Pour plus d'infos sur le tilemapping, je vous invite à lire cet autre tuto.

 

   Créons désormais le fichier map.c qui se chargera de gérer notre map !

   Ajoutez donc un nouveau fichier vide et nommez-le map.cwink Copiez-y le code ci-dessous :

 

Fichier : map.c

#include "prototypes.h"
 
 
Map map;
 
 
void initMaps(void)
{
// Charge l'image du fond (background)
map.background = loadImage("graphics/background.png");
}
 
 
SDL_Texture *getBackground(void)
{
return map.background;
}
 
 
void cleanMaps(void)
{
// Libère la texture du background
if (map.background != NULL)
{
SDL_DestroyTexture(map.background);
map.background = NULL;
}
 
}

 

   Voilà, ce fichier n'est pas encore bien conséquent (ça va venir cheeky) mais il contient ce dont nous avons besoin.

   D'abord, vous voyez que nous déclarons notre structure map de type Map, tout en haut, que nous avons déjà définie dans defs.h.

 

   Ensuite, nous créons 3 nouvelles fonctions (dont nous devrons ensuite ajouter les prototypes dans le fichier prototypes.h) :

- initMaps() se chargera d'initialiser toutes les variables nécessaires au bon fonctionnement de notre map. Pour l'instant, elle se chargera simplement de charger l'image du background dans notre variable map.background à l'aide de la fonction loadImage() que nous allons écrire ci-après. wink

- getBackground() renvoie simplement la texture du background. Elle sera pratique pour pouvoir récupérer cette variable depuis un autre fichier, car autrement on n'aurait pas eu le droit d'y accéder vu qu'elle est "encapsulée" dans ce fichier map.c. C'est une pratique qui sera familière à ceux qui programment déjà de façon orientée objet (POO) avec des langages comme le C++, le C# ou le Java (entre autres).

- enfin, cleanMaps() fera le ménage à la fin du programme en supprimant de la mémoire notre fichier map.background. (C'est très important de le faire en C, car il n'y a pas de garbage collector comme dans d'autres langages plus sophistiqués wink).

 

   Passons maintenant au fichier init.csmiley

   Là, nous allons créer notre fonction loadGame() qui va charger notre jeu (en faisant ici simplement appel à initMaps() pour l'instant). wink

 

Fichier : init.c

void loadGame(void)
{
//On charge les données pour la map
initMaps();
}

 

   Nous allons également compléter notre fonction cleanup(), en rajoutant un appel à cleanMaps() tout en haut :

 

Fichier : init.c 

void cleanup()
{
//Nettoie les sprites de la map
cleanMaps();

 

   Bien, il ne nous reste plus maintenant qu'à gérer la partie graphique de notre programme dans le fichier draw.c. Supprimez la fonction drawGame() (seulement celle-là !) puis copiez/collez le code suivant :

 

Fichier : draw.c

void drawGame(void)
{
 
// Affiche le fond (background) aux coordonnées (0,0)
drawImage(getBackground(), 0, 0);
 
// Affiche l'écran
SDL_RenderPresent(getrenderer());
 
// Délai pour laisser respirer le proc
SDL_Delay(1);
 
}
 
 
SDL_Texture *loadImage(char *name)
{
 
/* Charge les images avec SDL Image dans une SDL_Surface */
 
SDL_Surface *loadedImage = NULL;
SDL_Texture *texture = NULL;
loadedImage = IMG_Load(name);
 
if (loadedImage != NULL)
{
// Conversion de l'image en texture
texture = SDL_CreateTextureFromSurface(getrenderer(), loadedImage);
 
// On se débarrasse du pointeur vers une surface
SDL_FreeSurface(loadedImage);
loadedImage = NULL;
}
else
printf("L'image n'a pas pu être chargée! SDL_Error : %s\n", SDL_GetError());
 
return texture;
 
}
 
 
void drawImage(SDL_Texture *image, int x, int y)
{
 
SDL_Rect dest;
 
/* Règle le rectangle à dessiner selon la taille de l'image source */
dest.x = x;
dest.y = y;
 
/* Dessine l'image entière sur l'écran aux coordonnées x et y */
SDL_QueryTexture(image, NULL, NULL, &dest.w, &dest.h);
SDL_RenderCopy(getrenderer(), image, NULL, &dest);
 
}

 

   Commençons par drawGame() : vous aurez remarqué qu'on ne remplit plus l'écran en noir (même si on aurait pu le laisser avant, sans que cela ne change grand chose wink), mais qu'à la place, on appelle drawImage() pour afficher le background, renvoyé par notre fonction précédente getBackground()wink

 

   loadImage() prend en argument l'emplacement du fichier à charger (si vous avez une erreur, ou un écran noir, commencez d'ailleurs par vérifier que votre image est au bon endroit et qu'elle a le bon nom wink), elle charge ensuite le fichier comme une SDL_Surface en 2D (comme pour la SDL 1.2) avant de la convertir en texture 3D à partir du format du renderer. L'image sera ainsi optimisée pour coller à votre renderer (qui peut par exemple recalculer la dimension des images pour faire du plein écran) et être gérée par votre grosse carte graphique (au boulot, ma vieille ! laugh). La SDL_Surface est ensuite supprimée de la mémoire (important !) et la fonction renvoie la texture. wink

 

   drawImage() enfin va nous servir à dessiner nos images. Cette fonction est très basique (nous en créerons une plus complexe plus tard pour gérer les découpages, les flips et les rotations de sprites wink) : elle prend une texture et ses coordonnées x et y en arguments et elle blitte (=colle) ensuite l'image toute entière auxdites coordonnées.

 

   Et voilà ! wink

   N'oublions pas maintenant de compléter notre fichier prototypes.h avec nos nouveaux prototypes. Je vous redonne le fichier complet ci-dessous :

 

Fichier : prototypes.h 

#ifndef PROTOTYPES
#define PROTOTYPES
 
#include "structs.h"
 
/* Catalogue des prototypes des fonctions utilisées.
On le complétera au fur et à mesure. */
 
extern void cleanMaps(void);
extern void cleanup(void);
extern void delay(unsigned int frameLimit);
extern void drawGame(void);
extern void drawImage(SDL_Texture *, int, int);
extern void drawMap(int);
extern void gestionInputs(Input *input);
extern SDL_Texture *getBackground(void);
extern void getInput(Input *input);
extern SDL_Renderer *getrenderer(void);
extern void init(char *);
extern void initMaps(void);
extern void loadGame(void);
extern SDL_Texture *loadImage(char *name);
 
 
#endif

 

   Compilez désormais le programme puis lancez-le et TADAAA ! yes

   Notre magnifique background s'affiche dans toute l'immensité de sa beauté ! (J'exagère à peine ! wink)

 

 

   @ bientôt pour le chapitre 6 ! smiley

                                               Jay

 

 

Connexion

CoalaWeb Traffic

Today131
Yesterday178
This week631
This month4305
Total1743512

25/04/24