Jeux Libres
       
           

» Les Tutoriels » Création d'un jeu vidéo » Création de la carte

Création de la carte


Nous allons maintenant créer la carte. Là encore, nous allons chercher la simplicité. Cette fois ci, le résultat sera visible et motivant. Vous gagnez en autonomie, je vais simplement vous guider en vous laissant créer votre jeu de vos propres mains.





Chapitre précédent     Sommaire     Chapitre suivant


Le principe


Le plus simple, à mon avis, c'est de créer un fichier carte.bmp. Chaque pixel sombre représente un obstacle.


A partir de cette image, on va créer un tableau de booléens.


Ensuite, il s'agira du placer les murs entre les 0 et les 1.

Voici l'image du mur que nous utiliserons :


Le principe est simple, allons y !


La classe "Carte"


La carte se comporte un peu comme un Objet3dStatique.

Voici pour l'instant la déclaration de ma classe Carte :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Carte
{
   private:
       ConteneurTextures conteneurTextures;
 
       bool8* carte;
       uint32 largeurCarte;
       uint32 hauteurCarte;
       GLuint listeAffichage;
 
       void creerListeAffichage();
 
   public:
       Carte(const char* nomFichier);
       ~Carte();
       void dessiner();
};

Rien de compliqué ici. Un constructeur prenant la carte en paramètre. A partir de la carte, on construira le tableau de booléens puis on créera la liste d'affichage des murs, et du sol éventuellement. Une méthode dessiner() pour rappeler la liste d'affichage.


Les méthodes


Vous devez commencer à être à l'aise, je vais donc essayer de ne pas m'étendre trop dans les explications. Après les sources, nous verrons les choses à bien comprendre. Mais avant de plonger dans le code, voici quelques informations à propos des images BMP 24 bits.

BMP 24 bits



Lorsqu'on charge un BMP 24 bits dans une surface SDL, les pixels sont les uns à la suites des autres, de gauche à droite, ligne par ligne, de haut en bas, comme lorsqu'on écrit. Les pixels font 3 octets : un octet pour la composante bleu, un autre pour la verte, puis un dernier pour la rouge. Le format est BGR (bleu - vert - rouge).

Le pixel le plus clair est le pixel blanc. Les 8 bits sont à 1 pour chacune de ses composantes. La somme est donc de 3 x 255, soit 765. On considèrera donc que le pixel est clair lorsque la somme des composantes est supérieure à 765 / 2, soit 382. Au dessous de cette valeur, on considèrera qu'il s'agit d'un mur.

Avec paint, faite attention à l'enregistrement :


Chargement de la carte



Voici le code du constructeur. Vous devriez avoir quelque chose de semblable à ça :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
Carte::Carte(const char* nomFichier)
{
   // Initialisation des attributs
   this->carte = NULL;
   this->largeurCarte = 0u;
   this->hauteurCarte = 0u;
   this->listeAffichage = 0u;
 
   // Tentative de chargement
   SDL_Surface* imageCarte = SDL_LoadBMP(nomFichier);
 
   // Si l'image est chargee en 24 bits/px
   if ( (NULL != imageCarte) && (24 == imageCarte->format->BitsPerPixel) )
   {
       // Dimension de la carte
       this->largeurCarte = imageCarte->w;
       this->hauteurCarte = imageCarte->h;
 
       // Calcule le nombre de cases
       uint32 nbCases = this->largeurCarte * this->hauteurCarte;
 
       // Instantiation des cases de la carte
       this->carte = new bool8[nbCases];
 
       
       /* ANCIEN CODE
       // Structuration des pixels BGR pour une lecture simplifiee
       typedef struct {uint8 bleu; uint8 vert; uint8 rouge;} bgr;
       bgr* pixelsCarte = (bgr*)imageCarte->pixels;
 
       // Parcours de la carte
       for(uint32 i = 0; i < nbCases; i++)
       {
           // Si la moyenne des 3 couleurs est superieur 50% de la luminosit maximale
           if( (pixelsCarte[ i ].rouge + pixelsCarte[ i ].vert + pixelsCarte[ i ].bleu) > 382 )
           {
               // Pas de mur
               this->carte[ i ] = 0;
           }
           else
           {
               this->carte[ i ] = 1;
           }
       }*/

 
 
       // Parcours de chaques lignes
       for(uint32 ligne = 0; ligne < this->hauteurCarte; ligne++)
       {
           // Structuration des pixels BGR pour une lecture simplifiee
           typedef struct {uint8 bleu; uint8 vert; uint8 rouge;} bgr;
           bgr* pixelsCarte = (bgr*) ((uint8*)imageCarte->pixels + (ligne * imageCarte->pitch));
 
           // Parcours des pixels de la ligne
           for(uint32 pixel = 0; pixel < this->largeurCarte; pixel++)
           {
               // Si la moyenne des 3 couleurs est superieur 50% de la luminosit maximale
               if( (pixelsCarte[pixel].rouge + pixelsCarte[pixel].vert + pixelsCarte[pixel].bleu) > 382 )
               {
                   // Pas de mur
                   this->carte[(this->largeurCarte * ligne) + pixel] = 0;
               }
               else
               {
                   this->carte[(this->largeurCarte * ligne) + pixel] = 1;
               }
           }
       }
 
       // Creation de la liste d'affichage de la carte
       this->creerListeAffichage();
 
 
       SDL_FreeSurface(imageCarte);
   }
 
   // Erreur de chargement
   else
   {
       printf("Erreur de chargement de la carte\n");
   }
}

Explications du chargement



On commence par charger l'image en surface SDL, puis on vérifie que l'image soit bien en 24 bits.

Ensuite, on calcule le nombre de cases (nombre de pixels) en multipliant la largeur et la hauteur de l'image BMP. Ainsi, on peut instancier un tableau de booléens comportant le bon nombre de cases. Personnellement, je ne suis pas fan des tableaux à 2 dimensions, j'ai donc choisi un tableau à une seule dimension.

Ensuite, j'ai créé une structure de manière à récupérer directement les composantes des pixels.

Chez moi, j'ai testé, ça fonctionne très bien. Mais j'ai quand même un doute sur la façon dont les données sont organisées dans la mémoire. J'ai bien peur qu'en optimisant le compilateur pour la vitesse, la structure s'aligne sur 4 octets et que la lecture des pixels (3 octets) soit décalée. Si vous rencontrez un problème ou si vous avez une meilleur solution, merci de me le faire savoir.


Pour les geeks : Dans une première version, j'avais fait une simple boucle comme si les pixels étaient bien rangés les uns derrières les autres. En réalité, certains octets sont parfois ajoutés entre chaque lignes de pixels pour aligner en mémoire les débuts de ligne sur une adresse multiple de 4. Vous devez donc récupérer la taille de la ligne en octets grâce au champs pitch pour calculer l'adresse du pointeur pour chaque débuts de ligne. J'ai laissé volontairement l'ancienne version en commentaire. Notez que l'ancien code fonctionne parfaitement pour les cartes dont la largeur est un multiple de 4.


Nous nous servons ensuite de cette structure pour parcourir les pixels de l'image et déterminer s'il s'agit d'un mur ou non.

Enfin, nous créons la liste d'affichage. Pour l'instant on se contente de faire un appel de fonction et on vérifie que ça compile.

Création de la liste d'affichage.



Je ne vais pas tout vous donner pour cette partie. Voici à quoi ressemble mon code pour la première ligne de pixel. La carte sera dessiné entre les axes des X positifs et les Y positifs.

Les X du BMP sont représenté par l'axe des Y dans la scène 3D. De même, les Y du BMP sont sur l'axe des X en 3D.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
    // Chargement de la texture du mur
   this->conteneurTextures.ajouter("mur.bmp");
 
   // Creation de la liste d'affichage
   this->listeAffichage = glGenLists(1);
   glNewList(this->listeAffichage, GL_COMPILE);
 
   // Activation des textures
   glEnable(GL_TEXTURE_2D);
 
   // Selection de la texture
   glBindTexture(GL_TEXTURE_2D, this->conteneurTextures.texture("mur.bmp").texture);
 
   bool8 caseGauche;
   bool8 caseDroite;
 
   #define HAUTEUR_MURS 1
 
   // Parcours d'un ligne
   for (uint32 i = 0u; i < this->largeurCarte - 1; i++)
   {
       caseGauche = this->carte[i];
       caseDroite = this->carte[i+1];
 
       // Si le mur est visible de l'ouest vers l'est
       if ((0 == caseGauche) && (1 == caseDroite))
       {
           // Dessin du mur
           glBegin(GL_QUADS);
           glTexCoord2i(0, 0); glVertex3i(0, i+1, HAUTEUR_MURS);
           glTexCoord2i(0, 1); glVertex3i(0, i+1, 0);
           glTexCoord2i(1, 1); glVertex3i(1, i+1, 0);
           glTexCoord2i(1, 0); glVertex3i(1, i+1, HAUTEUR_MURS);
           glEnd();
       }
   }
 
   // Fin de la liste d'affichage
   glEndList();

Là il est important de bien comprendre ce que l'on fait. Pour la liste d'affichage, je suppose maintenant que c'est compris : on crée la liste d'affichage puis on fait comme si on dessinait. Pour les murs de la ligne de pixel du haut, je parcours la ligne de bloc de 2 pixels en bloc de 2 pixels. Lorsque le pixel gauche est un trou et que le pixel droite est un mur, alors je dessine le mur dans le sens trigonométrique du point de vue de la face visible.

Vous devrez ajouter une boucle pour parcourir les lignes et gérer l'affichage des 2 types de mur (nord et sud) puis faire quelque chose de semblable pour les murs ouest et est.

Pour aller jusqu'au bout des choses, il serait bien d'ajouter de l'herbe de la taille de la carte à Z=0.

La méthode dessiner



La méthode dessiner est très très simple, elle fait un simple appel à la liste d'affichage, on l'a déjà vu dans Objet3DStatique.

Le destructeur



N'oubliez pas de libérer vos ressources !


Intégration de la carte à la scène


L'intégration n'est pas très difficile, elle se fait à peu près de la même manière que la skybox. Si ce n'est pas déjà fait, pensez à arrêter de faire tourner le monde autour de votre caméras, si vous voyez ce que je veux dire, puis placez la caméras de telle sorte que la carte soit dans la zone visible de la scène.

Si rien ne s'affiche, deux possibilités : soit la caméra n'est pas bien placée, soit l'affichage de votre carte ne fonctionne pas. Je vous laisse débugger s'il y a besoin.

Voici le résultat :


Avec le masquage des faces cachées désactivé, on se rend mieux compte :





Comme vous avez pu le voir, dans ce chapitre, je vous ai laissé créer votre jeu vous même. De plus en plus ce sera comme ça. Vous devrez de plus en plus chercher par vous même et savoir où vous procurer les informations recherchées.

Vous avez également le forum qui est là pour ça. Sur lequel j'essaie d'être actif autant que possible.

Pour ce qui concerne OpenGL, je vous recommande vivement le Red Book, qui est en accès libre sur Internet. Personnellement, j'ai choisi d'investir dans un livre que j'utilise beaucoup :


Il s'agit d'une "copie" française du Red Book, et il coute 52?. Oui, c'est chère, et la traduction n'est pas toujours parfaite. Malgré tout, je n'ai aucun regret car il m'est très utile pour créer ce jeu.

Si vous connaissez des livres ou des sites intéressants, merci de me le faire savoir. J'essaierai de créer un sujet de discutions sur le forum pour permettre de fournir des liens utiles au développement de jeu vidéo.


Dans le chapitre suivant, nous ajouterons un personnage à notre jeu puis nous nous mettrons dans la peau de se personnage. Notre jeu va commencer à devenir sérieusement intéressant.



Chapitre précédent     Sommaire     Chapitre suivant



Rédigé par David
Consulté 17523 fois



Hébergeur du site : David
Version PHP : 5.4.45-0+deb7u2
Uptime : 126 jours 4 heures 17 minutes
Espace libre : 2117 Mo
Dernière sauvegarde : inconnue
Taille de la sauvegarde : 1101 Mo


4149568 pages ont été consultées sur le site !
Dont 645 pages pendant les 24 dernières heures.

Page générée en 0.406 secondes


Nos sites préférés
- Création d'un jeu de plateforme de A à Z avec SDL
- Zelda ROTH : Jeux amateurs sur le thème de Zelda
- Zeste de Savoir : la connaissance pour tous et sans pépins
- YunoHost : s'héberger soi-même en toute simplicité
- Site de Fvirtman : recueil de projets et de codes en C et C++


  © 2005-2017 linor.fr - Toute reproduction totale ou partielle du contenu de ce site est strictement interdite.