Chapitre 35 (final)

Faisons tout exploser ! 

 

Tutoriel présenté par : Jérémie F. Bellanger (Jay)
Dernière mise à jour : 23 août 2014

 

   Eh voilà, nous y sommes : le dernier chapitre de ce Big Tuto, décidément très "Big" ! cool Bon, je sais, j'avais déjà dit que ce serait la fin au chapitre 23, mais cette fois, c'est vrai. Je termine enfin ce Big tuto qui m'aura pris 3 ans, beaucoup d'énergie, et qui passe de la SDL 1.2 à la SDL 2, non sans heurts, parfois. Je tiens aussi à remercier Gondulzak, qui m'a beaucoup aidé dans ce travail de conversion, et grâce auquel ce tuto termine véritablement en beauté ! wink

   Par la suite, je vais m'atteler au reboot en SDL 2, avec bien entendu, un code encore peaufiné et de nombreuses améliorations. wink

   Bon, mais ce n'est pas le tout ! Il nous reste encore un dernier chapitre à écrire avant de tourner cette grande et belle page ! Et pour ne rien gâcher, je vous propose de finir en faisant TOUT EXPLOSER !!! devil

   Ahah ! Non, n'ayez pas peur, notre but va être simplement de rajouter une animation d'explosion quand un monstre meurt. cheeky Ce sera moins abrupt ainsi, et cela va rajouter du charme à notre jeu ! Eh oui, ce sont tous ces petits détails qui font un grand jeu ! wink

   Allez, on est parti ! smiley

   

 

 

      Des zombies qui explosent !

   Et pourquoi, ça n'existerait pas, d'abord ? sad Vous croyez vraiment que les zombies, ça existe, vous ? Na ! laugh

   Bon, commençons par le plus facile, et copions le sprite de notre animation dans le répertoire graphics, sous le nom explosion.png :

 

   C'est fait ? wink

   Bon, réfléchissons à ce que nous voulons faire. En fait, c'est simple, ce que l'on veut c'est afficher l'animation de l'explosion à la place d'un monstre venant de défunter ! cheeky

  Il nous faudra donc créer une nouvelle animation (en utilisant un tableau de sprites / GameObjects comme pour les monstres) à chaque fois qu'un monstre va mourir. De plus, comme plusieurs monstres peuvent décéder de façon (plus ou moins) violentes à la suite, il nous faudra prendre en charge la possibilité d'afficher plusieurs explosions en même temps. cool

   Ensuite, contrairement à nos autres animations, celle de l'explosion ne devra pas se mettre en boucle, mais disparaître une fois terminée.

   Voilà pour l'essentiel ! wink Si vous vous sentez d'attaque pour un ultime défi, vous pouvez essayer tout seul, puis regarder l'une des solutions possibles (car, oui, il existe bien plusieurs façons d'aboutir au même résultat wink).

   Vous êtes déjà revenu ? OK, bon on y va ensemble ! wink

 

   Commençons par créer un nouveau tableau de structures GameObject pour nos explosions :

 

Fichier : main.h

//Pour générer les explosions
GameObject explosions[10];
    

 

   On met ensuite à jour notre structure Gestion :

Fichier : structs.h : dans la structure Gestion : Ajoutez :

//Explosions
int nombreExplosions;
SDL_Texture *explosionTex;
    

 

    On crée une variable pour comptabiliser le nombre d'explosions à dessiner, comme pour les shurikens/boules de feu ou les monstres, ou encore les plateformes volantes wink.

   On crée aussi une SDL_Texture pour héberger notre sprite d'explosion. On aurait très bien pu le mettre dans notre GameObject, mais on l'aurait alors dupliquer à chaque fois, sans compter les chargements / déchargements successifs. En ne chargeant la texture qu'une fois au début du jeu et en la déchargeant à la fin, on économise des accès disque, plus lent, et de la RAM. smiley

 

Fichier : loadGame()- Rajouter sous les shurikens :

  

//On charge l'anim' des explosions
jeu.explosionTex = loadImage("graphics/explosion.png");

 

   Là, on charge notre spritesheet.

   Puis, on le décharge dans le Cleanup(). Notez que j'ai un peu modifié la libération des monstres pour qu'on ne libère que les monstres chargés (inutile pour le reste wink).

 

Fichier : init.c : cleanup()- Changer et rajouter :

  

 /* Libère le sprite des monstres */
	for(i = 0 ; i < jeu.nombreMonstres ; i++)
    {
        if (monster[i].sprite != NULL)
	    {
		   SDL_DestroyTexture(monster[i].sprite);
		   monster[i].sprite = NULL;
		}
	}

		/* Libère le sprite des explosions */
	if (jeu.explosionTex != NULL)
	{
		SDL_DestroyTexture(jeu.explosionTex);
	}

 

   On arrive à la plus grosse des nouveautés : on va créer 2 nouveaux fichiers pour nos explosions (vous devez avoir l'habitude maintenant wink).

   Ces fichiers ajoutent 2 nouvelles fonctions :

- createExplosion() va ajouter une nouvelle explosion à notre tableau de sprites, un peu comme pour les monstres. Elle prendra en argument les coordonnées x et y du monstre qui viendra de défunter. cheeky

- drawExplosions() est une variante de drawAnimatedEntity() qui passe en revue toutes les explosions actives et les affiche. Sa particularité est d'être spécifique aux explosions en exploitant leur texture dédiée, et en updatant automatiquement la liste des explosions quand celles-ci sont terminées. Si vous avez essayé de créer les explosions par vous-même, vous aurez peut-être réutilisé drawAnimatedEntity() en l'adaptant un peu. C'est une autre possibilité. wink

 

Créer un nouveau fichier : explosions.c

  

//Rabidja - SDL 2.0 - Copyrights www.meruvia.fr

#include "explosions.h"


void createExplosion(int x, int y)
{
	/* Si on peut créer une explosion, on la crée */
	if (jeu.nombreExplosions < 10)
	{

		//On réinitialise la frame et le timer
		explosions[jeu.nombreExplosions].frameNumber = 0;
		explosions[jeu.nombreExplosions].frameTimer = TIME_BETWEEN_2_FRAMES_PLAYER;

		/* Ses coordonnées de démarrage seront envoyées en arguments */
		explosions[jeu.nombreExplosions].x = x;
		explosions[jeu.nombreExplosions].y = y;

		/* Hauteur et largeur de notre explosion (ici 64 x 64 pixels) ) */
		explosions[jeu.nombreExplosions].w = 64;
		explosions[jeu.nombreExplosions].h = 64;

		//Variable nécessaire pour savoir si on doit supprimer ou non l'explosion (après un tour)
		explosions[jeu.nombreExplosions].timerMort = 0;

		jeu.nombreExplosions++;
	}

}


void drawExplosions(void)
{
	int i;

	for (i = 0; i < jeu.nombreExplosions; i++)
	{

		/* Gestion du timer */
		// Si notre timer (un compte à rebours en fait) arrive à zéro
		if (explosions[i].frameTimer < = 0)
		{
			//On le réinitialise
			explosions[i].frameTimer = TIME_BETWEEN_2_FRAMES_PLAYER;

			//Et on incrémente notre variable qui compte les frames de 1 pour passer à la suivante
			explosions[i].frameNumber++;

			//Mais si on dépasse la frame max, l'explosion est finie, il faut la détruire
			int w;
			SDL_QueryTexture(jeu.explosionTex, NULL, NULL, &w, NULL);
			if (explosions[i].frameNumber > = w / explosions[i].w)
			{
				/* Libère le sprite */
				if (explosions[i].sprite != NULL)
				{
					SDL_DestroyTexture(explosions[i].sprite);
				}
				explosions[i] = explosions[jeu.nombreExplosions - 1];
				jeu.nombreExplosions--;
				return;
			}
		}
		//Sinon, on décrémente notre timer
		else
			explosions[i].frameTimer--;


		//Ensuite, on peut passer la main à notre fonction

		/* Rectangle de destination à dessiner */
		SDL_Rect dest;

		// On soustrait des coordonnées de notre héros, ceux du début de la map, pour qu'il colle
		//au scrolling :
		dest.x = explosions[i].x - map.startX;
		dest.y = explosions[i].y - map.startY;
		dest.w = explosions[i].w;
		dest.h = explosions[i].h;

		/* Rectangle source */
		SDL_Rect src;

		//Pour connaître le X de la bonne frame à dessiner, il suffit de multiplier
		//la largeur du sprite par le numéro de la frame à afficher -> 0 = 0; 1 = 64; 2 = 128...
		src.x = explosions[i].frameNumber * explosions[i].w;
		src.y = 0;
		src.w = explosions[i].w;
		src.h = explosions[i].h;

		/* Dessine notre héros sur l'écran aux coordonnées x et y */
		SDL_RenderCopy(jeu.renderer, jeu.explosionTex, &src, &dest);

	}

}

 

   Et le fichier d'en-tête :

Créer un nouveau fichier : explosions.h

  
//Rabidja - SDL 2.0 - Copyrights www.meruvia.fr

#ifndef DEF_EXPLOSIONS
#define DEF_EXPLOSIONS

#include "structs.h"

/* Structures globales */
extern Gestion jeu;
extern GameObject explosions[];
extern Map map;


#endif
  

 

   On passe ensuite dans le fichier player.c, pour gérer la réinitialisation des explosions au chargement du jeu / des niveaux :

 

Fichier : player.c : initializePlayer() - Rajouter au tout début :

  int i;  

 

   Puis,

Fichier : player.c :  initializePlayer() - Changer :

 

//Réinitialise les monstres 
jeu.nombreMonstres = 0;  

Par :

  
    //Réinitialise les monstres
	/* Libère le sprite des monstres */
	for (i = 0; i < jeu.nombreMonstres; i++)
	{
		if (monster[i].sprite != NULL)
		{
			SDL_DestroyTexture(monster[i].sprite);
			monster[i].sprite = NULL;
		}
	}
	jeu.nombreMonstres = 0;

	//Réinitialise les explosions
	jeu.nombreExplosions = 0;

 

   Vous remarquerez que j'ai aussi mis à jour la réinitialisation des monstres, pour prendre en compte les sprites déjà chargés. C'était un oubli de ma part, qui devait causer une infinitésimale fuite de mémoire au passage de chaque niveau, mais tellement petite (quelques ko à chaque fois), que personne ne s'en était aperçue ! laugh

   Et du coup, on met à jour l'en-tête pour qu'il puisse aller chercher ces sprites récalcitrants :

 

Fichier : player.h : Rajouter :

  extern GameObject monster[];  

   

   Voilà, maintenant, on passe au fichier monster.c et on va rajouter l'appel à createExplosion() dans la partie qui gère la mort des monstres (avant de le détruire, si on veut récupérer ses coordonnées cheeky).

 

Fichier : monster.c : updateMonsters() - On crée une explosion quand un monstre meurt

        //Si le monstre meurt, on active une tempo
        if (monster[i].timerMort > 0)
        {
            monster[i].timerMort--;

            /* Et on le remplace simplement par le dernier du tableau puis on
            rétrécit le tableau d'une case (on ne peut pas laisser de case vide) */
            if (monster[i].timerMort == 0)
            {
		//On crée une explosion
		createExplosion(monster[i].x, monster[i].y);

                /* Libère le sprite */
                if (monster[i].sprite != NULL)
                {
                    SDL_DestroyTexture(monster[i].sprite);
                }
                monster[i] = monster[jeu.nombreMonstres-1];
                jeu.nombreMonstres--;

			}
        }

 

 

   Eh hop ! Le prototype va dans l'en-tête !

 

Fichier : monster.h - Rajouter:

extern createExplosion(int x, int y);

 

   Maintenant, on raccorde la fonction drawExplosions() à notre fonction draw() (sinon, les explosions n'apparaîtront pas wink) :

 

Fichier : draw.c : draw()- Rajouter sous les monstres :

//Dessine les explosions
drawExplosions();

 

   Et enfin, on met à jour l'en-tête !

 

Fichier : draw.h - Rajouter :

extern void drawExplosions(void);
    

 

   Et voilà ! Nos explosions sont maintenant opérationnelles, et on va pouvoir éclater du zombie à tout-va !!! cool

   Ahah, c'est trop cool !! smiley

 

 

   Bon, eh bien voilà qui conclut enfin ce giganteque tuto ! J'espère qu'il vous aura plu et que vous aurez appris plein de choses. wink

   Bien entendu, il y a d'autres façons de faire, mais cela vous donne une idée de comment on bâtit un jeu. Maintenant, c'est à vous de continuer pour créer le jeu de vos rêves ! heart

   @ bientôt sur Meruvia !

 

 

 

Connexion

CoalaWeb Traffic

Today94
Yesterday238
This week1017
This month495
Total1745394

3/05/24