Chapitre 32

Reprenons notre projet en SDL 2 ! 

 

Tutoriel présenté par : Jérémie F. Bellanger (Jay)
Dernière mise à jour : 19 juillet 2014

 

      Un renouveau du projet en SDL 2

 

   Avant de repartir dans un nouveau tuto en SDL 2 (lequel va sensiblement modifier l'architecture de jeu, et s'avérer donc beaucoup plus complexe wink), je tenais à finir ce Big Tuto en mettant une sorte de point final à notre jeu. En effet, bien que ce soit sans doute le tuto de jeu le plus détaillé de tout le web, il lui manque encore quelques détails pour le rendre vraiment pro. Et c'est ce que je vais m'attacher à faire dans ces quelques tutoriels finaux. wink

   En cinq ans, j'ai programmé pas moins de 6 jeux complets, aussi bien sur PC que sur Xbox 360, et j'ai énormément appris, ce faisant. D'une part, grâce à ma propre Recherche & Développement, pour créer le Meruvia Engine (dont ce tuto en est l'une des ossatures les plus basiques - car, oui, le Meruvia Engine en v. 4 est assez énorme ! wink), et d'autre part, en discutant et en échangeant avec d'autres amis programmeurs ou d'autres studios indés ou pros. Et c'est une partie de ces connaissances que je vais maintenant partager avec vous !

   Alors, forcément, ça va devenir un petit peu plus compliqué à partir de maintenant, et comme vous avez déjà accumulé suffisamment de bases, j'irai plus vite sur certains points, comme les réglages du moteur, par exemple (que vous retrouverez en relisant les chapitres antérieures wink).

 

   En un mot comme en cent, on avait un petit moteur de jeu sympa en SDL 1.2, qui pouvait impressionner les copains de la cour de récré, mais maintenant, on va passer au cran supérieur en créant une killer app' en SDL 2, qui va déchirer grave !! laugh

 

Le nouvel écran-titre est quand même beaucoup plus classe (et ça n'a demandé que
quelques lignes de code en plus wink) !

 

      De nouveaux graphismes pour les gouverner tous !   

   Eh oui, point de killer app' sans des graphismes qui tuent ! Et comment dire, les graphismes du jeu original étaient sympas avec leur côté rétro, mais là il nous faut des dessins qui envoient du lourd ! wink

   J'ai donc décidé de changer tous les graphismes, ainsi que les sons du projet original pour des tilesets et des effets sonores plus modernes et plus pros.

 

Ces assets sont issus des jeux du studio et restent donc sous Copyright exclusif de Meruvia. Vous ne pourrez donc pas vous en servir pour votre propre jeu (il faudra les changer), mais dans un but pédagogique d'apprentissage, ils vont vous montrer de quoi est réellement capable ce moteur 2D sur lequel vous travaillez depuis si longtemps maintenant ! cool

 

   Par souci de place, je ne les ai pas tous affichés ici : ils sont tous téléchargeables avec le projet entier, déjà préconfiguré pour la SDL 2 (car oui, ça peut prendre une journée à configurer... cheeky). Voici d'ailleurs le lien pour télécharger le projet :

 

 

    Je vous conseille de le télécharger maintenant et de l'ouvrir car vous en aurez besoin pour suivre les changements opérés au sein du code. wink

 

C'est quand même plus beau et ça fait plus ambiance "Ninja !" ! wink

 

     Des menus plus beaux !

   Forcément, tant qu'à refaire les graphismes du jeu, il fallait changer cet hideux écran-titre ! cheeky

   Mais comment faire ? angel

   En fait, c'est très simple, j'ai désactivé la plupart des écritures sauf les options : Start et Quit (on pourra en envisager d'autres plus tard wink), et j'ai créé une nouvelle texture dans la structure map : titlescreen. Celle-ci va contenir le joli fond d'écran que j'ai dessiné pour l'occasion. Il ne reste plus maintenant qu'à l'afficher à la place du background, sans oublier de la libérer dans la fonction Cleanup() en quittant le programme ! wink

 

      De nouveaux réglages

   Comme j'ai changé les tilesets, les sprites et la musique, j'ai dû opéré de nombreux changements au niveau des réglages du jeu. Je ne les passerai pas en revue ici car ce serait inutile et fastidieux (et vous pourrez les retrouver ou dans le code du projet, ou dans les chapitres dédiés wink).

 

   Mais pour faire court, voici les principales modifications opérées :

- changement des dimensions des sprites (dans les defs.h).

- changement des tilesets : plus grands - mise à jour des numéros des tiles (traversables, bloquantes, etc...).

- changement du nom de la musique et des sounds Fx (juste un changement au niveau des fichiers).

- nouvelles dimensions pour notre fenêtre de jeu : 800 x 480 (ce changement a été opéré par Gondulzak et je l'ai conservé, car il me semblait sympa wink. Qui plus est, en Full Screen, l'écran devrait être moins déformé sur des écrans 16/9 ou 16/10.).

- nouveaux réglages au niveau de la physique du héros : il saute moins haut par exemple, pour apporter plus de challenge au jeu.

- nouveaux timers : un pour la map (plus lent) et un pour les sprites (plus rapide) : le jeu a ainsi l'air plus dynamique.

 

      Trois couches de tiles pour mieux dominer !

    Dans un chapitre précédent, je vous avais montré comment passer d'une couche de tiles à 2. Eh bien, j'ai fait pareil pour en rajouter une troisième ! cheeky

   Bon, je n'ai touché qu'au code du jeu, donc c'était facile wink. Pour le level editor, je vais vous en proposer un nouveau : celui que j'utilise depuis Aron 2 : il permet beaucoup plus de choses et est bien plus simple et intuitif.

   Nous ne nous servirons pas de tout bien sûr (j'ai enlevé certaines options qui ne vous serviront pas), mais je vous le laisser avec le code source en C# / XNA pour que vous puissiez le modifier / l'améliorer le cas échéant. wink Mais on verra tout cela dans une autre partie de ce chapitre.

 

Les 3 couches de tiles permettent plus de détails et les shurikens tournent maintenant ! cool

 

     Des fonctions améliorées

   Afin de permettre des améliorations notables du jeu (notamment graphiques), j'ai modifié plusieurs fonctions :

 

              drawString() dans le fichier font.c

   J'ai tout d'abord modifié la fonction drawString() pour nous permettre de passer la couleur (RGB) en argument ainsi que la transparence (a). Ainsi, nous pourrons maintenant écrire de toutes les couleurs, et de façon plus ou moins opaque. Qui plus est, en écrivant le texte une première fois en noir, puis en blanc par dessus (avec un décalage de 1 ou 2 pixels), on obtient un magnifique effet ombré, qui rend aussi le texte plus lisible quelque que soit le fond (clair ou foncé) : je m'en suis servi pour l'affichage des vies et des étoiles. wink  

      Corrigeons une grosse fuite de mémoire !

    Je ne l'avais pas remarquée tout de suite, mais effectivement, il restait une grosse fuite de mémoire dans la version précédente, convertie en SDL 2 ! surprise Je m'en suis rendu compte quand mon programme s'est fermé subitement après 15 minutes de jeu : la mémoire enfle d'environ 10Mo / sec !

   J'ai passé une bonne heure avant de trouver d'où cela pouvait venir : en fait, il manquait la destruction de la texture dans la fonction drawString() : SDL_DestroyTexture(texture);, ce qui fait qu'une nouvelle texture était stockée en mémoire toutes les 16ms !

   Je vous donne donc ci-dessous la bonne fonction, complète, avec les mises à jour : wink

 

void drawString(char *text, int x, int y, TTF_Font *font, int r, int g, int b, int a)
{
	SDL_Rect dest;
	SDL_Surface *surface; //Pour écrire le texte
	SDL_Texture *texture; //Pour convertir la surface en texture
	SDL_Color foregroundColor;

	/* On choisit d'écrire le texte en noir (RGBA) */
	foregroundColor.r = r;
	foregroundColor.g = g;
	foregroundColor.b = b;
	foregroundColor.a = a;


	/* On utilise SDL_TTF pour générer une SDL_Surface à partir d'une chaîne 
        de caractères (string) */
	   surface = TTF_RenderUTF8_Blended(font, text, foregroundColor);

	if (surface != NULL)
	{
		/* NOUS MODIFIONS QUELQUE PEU NOTRE CODE POUR PROFITER DE LA MEMOIRE 
                GRAPHIQUE QUI GERE LES TEXTURES  */
		// Conversion de l'image en texture
		texture = SDL_CreateTextureFromSurface(jeu.renderer, surface);

		// On se débarrasse du pointeur vers une surface
		/* On libère la SDL_Surface temporaire (pour éviter les fuites de 
                 mémoire - cf. chapitre dédié) */
		SDL_FreeSurface(surface);
		surface = NULL;

	    // On dessine cette texture à l'écran
	    dest.x = x;
	    dest.y = y;

		SDL_QueryTexture(texture, NULL, NULL, &dest.w, &dest.h);
		SDL_RenderCopy(jeu.renderer, texture, NULL, &dest);

		//On supprime la texture
		SDL_DestroyTexture(texture);

	}
	else
	{
		printf("La chaine n'a pu être écrite %s: %s\n", text, TTF_GetError());

		exit(0);
	}
}
 

               drawImagePlus() dans le fichier draw.c

   Bien plus intéressante, cette nouvelle fonction, dont je ne suis pas peu fier, permet, grâce à la nouvelle puissante de la SDL de faire à peu près la même chose qu'avec XNA en une seule ligne de code : à savoir blitter une image ou une partie de l'image (comme avant), mais en y ajoutant (ou non) un effet de flip^(retournement horizontal ou vertical) ou de rotation ! cool

   Concrètement, cela signifie qu'on va pouvoir se débarrasser de la moitié de nos spritesheets, puisqu'on va pouvoir les retourner directement on-the-go ! Et qu'on va aussi pouvoir rajouter de jolis effets de rotation. Cela tombe bien, car les shurikens de rabidja vont maintenant pouvoir tournoyer à fond la caisse ! wink

 

void drawImagePlus(SDL_Texture *image, int x, int y, double rotation, 
                                        SDL_Point center, SDL_RendererFlip flip)
{
	//Nouvelle fonction en SDL2 qui gère le flip et les rotations

	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_RenderCopyEx(jeu.renderer, image, NULL, &dest, rotation, ¢er, flip);
}

 

   Il suffit maintenant d'appeler la fonction comme suit (tout en n'ayant pas oublié de rajouter les variables rotation et center à notre struct GameObject wink) :

 

void drawFireballs(void)
{
 
    int i;
 
    //On affiche toutes les boules de feu
    for ( i = 0; i < jeu.nombreFireballs; i++ )
    {
	   drawImagePlus(jeu.Shuriken_image, shuriken[i].x - map.startX, 
	       shuriken[i].y - map.startY, shuriken[i].rotation, shuriken[i].center, SDL_FLIP_NONE);
    }
 
}

   Voilà donc la fonction issue de fireballs.c, pour tirer les shurikens.

   Vous verrez dans le code du projet que rotation est gérée dans les updates : on l'augmente de 20° par frame, jusqu'à 360°, puis on revient à 0°, quant au centre, c'est le centre du sprite (pour qu'il tourne sur lui-même - comme il fait 20 x 20 pixels, son centre est donc (10, 10)). Quant au flip, on ne s'en sert pas ici, on envoie donc SDL_FLIP_NONE, les autres options étant : SDL_FLIP_HORIZONTAL et SDL_FLIP_VERTICAL.

   Je pense qu'on va pouvoir s'amuser maintenant ! cool

 

      Un nouveau format de map   

   J'en ai aussi profité pour changer un peu le format de la map, en plus de la troisième couche de tiles.

   Vous n'en aviez pas marre de voir votre héros tomber du ciel, au début du niveau ? cheeky Eh bien, maintenant, vous pourrez choisir son point de départ dans le level editor, et celui-ci sera stocké dans la map.

   Pareil, la config des niveaux était limitée à niveau 1 = tileset 1, niveau 2 = tileset 2, etc... Mais si vous voulez faire 5 niveaux de suite avec le même tileset ? surprise

   Maintenant, le tileset utilisé par la map sera lui aussi stocké dans le fichier de la map. wink

   Pour résumer, notre fichier de map, va donc commencer avec 3 nouvelles variables, à rajouter dans la struct map (fichier structs.h) :

//Numéro du tileset à utiliser
 int tilesetAffiche;
// Coordonnées de départ du héros, lorsqu'il commence le niveau
 int beginx, beginy;

 

   La fonction loadMap() va donc être très légèrement modifiée (je vous laisse regarder, c'est pas grand chose). De même, la fonction drawMap() affiche maintenant 3 couches : la couche 1 étant la couche d'action, celle avec laquelle le joueur interagit, la couche 2 devient celle du fond, et la couche 3 passe en forground ! cool

   La fonction changeLevel() prend maintenant en compte le tilesetAffiche, et la fonction initializePlayer() initialise le héros aux coordonnées map.beginx et map.beginy de la map en cours (à moins qu'il n'ait pris un checkpoint).

Attention : les anciennes maps utilisées précédemment ne fonctionneront donc plus et risqueraient de faire planter le jeu !

 

Des zombies attaquent le village ! Vite à la rescousse !

      Des tiles pour les traverser toutes, et des pics !

   J'ai aussi rajouté un nouveau format de tiles : les tiles plateformes qu'on peut traverser par le bas, mais sur lesquelles on marche.

   Pour cela, c'est en fait très simple, il suffit de placer ces tiles juste avant les tiles bloquantes dans votre tileset, et quand on teste s'il y a du sol sous les pieds du sprite (dans la direction du BAS donc), on utilise le numéro de la première tile plateforme au lieu de BLANK_TILE (qui correspond à la dernière tile traversable, rappelons-le wink). Ainsi, en allant dans toutes les autres directions, le sprite passe au-travers de cette tile, sauf s'il tombe dessus ! Si, c'est pas cool, ça ! cool

   Vous pourrez voir cela en action dans les fonctions mapCollision() et monsterCollisionToMap() du fichier map.c.

 

   Vous noterez que j'ai aussi rajouté une nouvelle tile spéciale, pour le fun : les pics ! wink Vous trouverez les quelques lignes les concernant dans la même fonction.
   En fait, quand on tombe dessus, le perso rebondit et perd un coeur. Il devient alors invincible, comme quand il touche un monstre. wink

 

        Un nouveau level editor en SDL2 

   Enfin, j'ai très nettement amélioré le level editor (maintenant en SDL 2 ! wink), de façon à ce que vous puissiez éditer confortablement vos maps, en proposant toutes les options de base. Vous le trouverez ci-dessous avec son code source :

 

   Je ne détaillerai pas toutes les mises à jour effectuées, mais ça reste très basique et aussi très répétitif. Bien entendu, on pourrait faire encore mieux et s'il y a des volontaires pour créer le level editor idéal et universel, configurable à volonté, n'hésitez pas à m'en parler ! wink

   Les nouveautés :

- Tout le tileset est désormais affiché à droite et défile avec un ascenseur ! Pour de gros tilesets, c'est mille fois plus pratique ! cool
- Un clic sur une tile et elle est sélectionnée. On peut toutjours copier/coller les tiles sur la carte avec les clics gauche et droit, et la molette fait aussi défiler les tiles, comme avant. wink
- Les touches fléchées scrollent toujours la map.
- Les touches S, L, R, F1, F2 et F3 fonctionnent toujours mais il y a aussi maintenant des boutons (parfois plus pratiques).
- On peut changer les couches/layers facilement pour les éditer simplement.
- On peut sélectionner le tileset (s'il y en a plusieurs).
- On peut aussi choisir l'emplacement de départ du niveau en cliquant sur +1 à côté de Choose start point, on clique ensuite où l'on veut sur la map, et on reclique sur le bouton +1 pour arrêter le choix du "startPoint". wink
- Des messages s'affichent quand on sauvegarde / charge / réinitialise ou édite le point de départ (pour être sûr que ça marche bien...) cheeky
- Les coordonnées de la souris sont affichées (mais ça, ça servait plus à créer l'éditeur).

 

Je travaille actuellement sur une nouvelle version de ce level editor, qui deviendra le level editor standard du studio pour tous les projets (tutos, jeux commerciaux, etc.) et que vous pourrez utiliser pour vos propres projets, customiser, et même proposer une version améliorée à la commnunauté ! Il bénéficiera d'un coup de polish supplémentaire, de plus de sécurités et du support tactile (pour éditer vos maps sur tablettes / PC tactiles Windows 8 ;) !

 

Et voilà pour ce chapitre de remise à niveau de notre jeu en SDL 2 !
Quand on voit, le résultat, je pense que cela valait le coup ! wink

 

 

@ bientôt pour le chapitre 33 et l'ajout des gamepads / joysticks (enfin !) ! smiley

  

 

 

 

Connexion

CoalaWeb Traffic

Today73
Yesterday238
This week996
This month474
Total1745373

3/05/24