Big Tuto SDL 2 : Rabidja v. 3.0
Chapitre 20 : Faisons tout exploser !
Tutoriel présenté par : Jérémie F. Bellanger (Jay81)
Ecriture : 16 novembre 2014
Dernière mise à jour : 11 novembre 2015
Prologue
Eh voilà, nous y sommes : le dernier chapitre de la partie tout public de ce Big Tuto ! ![]()
J'envisage, en effet, de rajouter par la suite quelques chapitres exclusifs pour les membres Premium, couvrant des sujets particulièrement difficiles pour des débutants, comme la gestion de boss, la création d'éclairages dynamiques, l'ajout de monstres qui sautent, tirent, etc...
Quand ces nouveaux chapitres seront disponibles, j'en indiquerai la liste et le contenu dans ce chapitre. ![]()
Bon, mais ce n'est pas le tout ! Il nous reste encore un dernier chapitre à écrire avant de finir cette première partie du tuto ! Et pour ne rien gâcher, je vous propose de finir en faisant TOUT EXPLOSER !!! ![]()
Ahah !
Non, n'ayez pas peur, notre but va être simplement de rajouter une animation d'explosion quand un monstre meurt.
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 ! ![]()
Allez, on est parti ! ![]()
Des zombies qui explosent !
Et pourquoi, ça n'existerait pas, d'abord ?
Vous croyez vraiment que les zombies, ça existe, vous ? Na ! ![]()
Bon, commençons par le plus facile, et copions le sprite de notre animation dans le répertoire graphics, sous le nom explosion.png :

explosion.png
C'est fait ? ![]()
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 ! ![]()
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. ![]()
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 !
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
).
Vous êtes déjà revenu ? OK, bon on y va ensemble ! ![]()
Le code
Fichier : Créer un nouveau fichier explosions.c et y copier :
|
#include "prototypes.h" GameObject explosions[10];
SDL_Texture *explosionTex;
int nombreExplosions;
void loadExplosionImage(void)
{
explosionTex = loadImage("graphics/explosion.png");
}
void cleanExplosionImage(void)
{
if (explosionTex != NULL)
{
SDL_DestroyTexture(explosionTex);
}
}
int getExplosionNumber(void)
{
return nombreExplosions;
}
void resetExplosions(void)
{
nombreExplosions = 0;
}
void createExplosion(int x, int y)
{
/* Si on peut créer une explosion, on la crée */
if (nombreExplosions < 10)
{
//On réinitialise la frame et le timer
explosions[nombreExplosions].frameNumber = 0;
explosions[nombreExplosions].frameTimer = TIME_BETWEEN_2_FRAMES_PLAYER;
/* Ses coordonnées de démarrage seront envoyées en arguments */
explosions[nombreExplosions].x = x;
explosions[nombreExplosions].y = y;
/* Hauteur et largeur de notre explosion (ici 32 x 32 pixels) ) */
explosions[nombreExplosions].w = 64;
explosions[nombreExplosions].h = 64;
//Variable nécessaire pour savoir si on doit supprimer ou non l'explosion (après un tour)
explosions[nombreExplosions].timerMort = 0;
nombreExplosions++;
}
}
void drawExplosions()
{
int i;
for (i = 0; i < 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(explosionTex, NULL, NULL, &w, NULL);
if (explosions[i].frameNumber >= w / explosions[i].w)
{
explosions[i] = explosions[nombreExplosions - 1];
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 - getStartX();
dest.y = explosions[i].y - getStartY();
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 explosion sur l'écran aux coordonnées x et y */
SDL_RenderCopy(getrenderer(), explosionTex, &src, &dest);
}
}
|
Comme vous pouvez le voir, on reste très près de ce qu'on a déjà pu écrire pour nos shurikens. ![]()
Et on passe donc logiquement à notre fichier init.c pour rajouter les appels à loadExplosionImage() et cleanExplosionImage() dans les fonctions loadGame() et cleanup() : on charge ainsi notre sprite au début du jeu et on les décharge en quittant. ![]()
Fichier : init.c : Remplacer les fonctions loadGame() et cleanup() par :
|
void loadGame(void)
{
//On charge les données pour la map
initMaps();
//On initialise les menus
initMenus();
//On charge la feuille de sprites du monstre
initMonsterSprites();
//On charge la feuille de sprites (spritesheet) de notre héros
initPlayerSprites();
//On commence au premier niveau
SetValeurDuNiveau(1);
changeLevel();
/* On initialise les variables du jeu */
setNombreDeVies(3);
setNombreDetoiles(0);
/* On charge le HUD */
initHUD();
//On charge le shuriken
loadShurikenImage();
//On charge l'anim' des explosions loadExplosionImage();
//On charge notre plateforme
loadPlateforme();
//On charge la musique
loadSong("music/Caviator.mp3");
/* On charge les sounds Fx */
loadSound();
//On commence par le menu start
setOnMenu(1, START);
}
void cleanup()
{
//Nettoie les sprites de la map et des menus
cleanMaps();
cleanMenus();
/* Libère le sprite du héros */
cleanPlayer();
/* Libère le sprite des monstres */
cleanMonsters();
/* Libère le sprite des explosions */ cleanExplosionImage();
//Libère le HUD
cleanHUD();
//Libère le shuriken
cleanShurikenImage();
//Libère la plateforme
cleanPlateforme();
/* Ferme la prise en charge du joystick */
closeJoystick();
/* On libère la musique */
cleanUpMusic();
//On libère les sons
freeSound();
//On quitte SDL_Mixer 2 et on décharge la mémoire
Mix_CloseAudio();
Mix_Quit();
//On fait le ménage et on remet les pointeurs à NULL
SDL_DestroyRenderer(renderer);
renderer = NULL;
SDL_DestroyWindow(screen);
screen = NULL;
//On quitte SDL_TTF 2
TTF_Quit();
//On quitte la SDL
SDL_Quit();
}
|
Ensuite, dans drawGame(), on ajoute un appel à drawExplosions(), pour pouvoir les afficher :
Fichier : draw.c : Remplacer drawGame() par :
|
void drawGame(int pauseMenu)
{
int i;
// Affiche le fond (background) aux coordonnées (0,0)
drawImage(getBackground(), 0, 0);
/* Affiche la map de tiles : layer 2 (couche du fond) */
drawMap(2);
/* Affiche la map de tiles : layer 1 (couche active : sol, etc.)*/
drawMap(1);
/* Affiche le joueur */
drawPlayer();
/* Affiche les monstres */
for (i = 0; i < getMonsterNumber(); i++)
{
drawMonster(getMonster(i));
}
//Dessine les explosions drawExplosions();
//Affiche les plateformes flottantes
drawPlateforme();
//Affiche les shuriken
drawShuriken();
/* Affiche la map de tiles : layer 3 (couche en foreground / devant) */
drawMap(3);
//On affiche le HUD par-dessus tout le reste
drawHud();
//On affiche le menu Pause, si on est en Pause
if (pauseMenu)
drawPauseMenu();
// Affiche l'écran
SDL_RenderPresent(getrenderer());
// Délai pour laisser respirer le proc
SDL_Delay(1);
}
|
Dans la fonction initializePlayer(), on appelle simplement resetExplosions(), pour remettre le nombre d'explosions affichées à zéro, quand on ressuscite ou qu'on passe à un nouveau niveau ! ![]()
Fichier : player.c : Remplacer la fonction initializePlayer() par :
|
void initializePlayer(int newLevel)
{
//PV à 3
player.life = 3;
//Timer d'invincibilité à 0
player.invincibleTimer = 0;
//Nombre de shurikens à 0
resetShuriken();
//Nombre de plateformes flottantes à 0
resetPlateformes();
//Indique l'état et la direction de notre héros
player.direction = RIGHT;
player.etat = IDLE;
//Indique le numéro de la frame où commencer
player.frameNumber = 0;
//...la valeur de son chrono ou timer
player.frameTimer = TIME_BETWEEN_2_FRAMES_PLAYER;
//... et son nombre de frames max (8 pour l'anim' IDLE
// = ne fait rien)
player.frameMax = 8;
/* Coordonnées de démarrage/respawn de notre héros */
if (player.checkpointActif == 1)
{
player.x = player.respawnX;
player.y = player.respawnY;
}
else
{
player.x = getBeginX();
player.y = getBeginY();
}
//On réinitiliase les coordonnées de la caméra
//si on change de niveau
if (newLevel == 1)
{
setStartX(getBeginX());
setStartY(getBeginY());
}
/* Hauteur et largeur de notre héros */
player.w = PLAYER_WIDTH;
player.h = PLAYER_HEIGTH;
//Variables nécessaires au fonctionnement de la gestion des collisions
player.timerMort = 0;
player.onGround = 0;
//Réinitialise les monstres
/* Libère le sprite des monstres */
resetMonsters();
//Réinitialise les explosions resetExplosions(); }
|
Et enfin, dans la fonction updateMonsters() du fichier monster.c, on appelle la fonction createExplosion() quand un monstre meurt en lui envoyant les coordonnées de ce monstre. ![]()
Fichier : monster.c : Modifier dans updateMonsters() tout à la fin de la fonction :
|
//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);
monster[i] = monster[nombreMonstres - 1];
nombreMonstres--;
}
}
}
}
|
Plus qu'à mettre à jour notre catalogue de prototypes :
Fichier : prototypes.h : Remplacez par :
|
#ifndef PROTOTYPES
#define PROTOTYPES
#include "structs.h"
/* Catalogue des prototypes des fonctions utilisées.
On le complétera au fur et à mesure. */
extern void centerScrollingOnPlayer(void);
extern void changeLevel(void);
extern void checkCollisionsWithPlateforms(GameObject *entity);
extern int checkFall(GameObject monster);
extern void cleanExplosionImage(void);
extern void cleanHUD(void);
extern void cleanMaps(void);
extern void cleanMenus(void);
extern void cleanMonsters(void);
extern void cleanPlateforme(void);
extern void cleanPlayer(void);
extern void cleanShurikenImage(void);
extern void cleanup(void);
extern void cleanUpMusic(void);
extern void closeFont(void);
extern void closeJoystick(void);
extern int collide(GameObject *player, GameObject *monster);
extern void createExplosion(int x, int y);
extern void createShuriken(void);
extern void delay(unsigned int frameLimit);
extern void doPlateforme(void);
extern void doShuriken(void);
extern void drawExplosions();
extern void drawGame(int pauseMenu);
extern void drawHud(void);
extern void drawImage(SDL_Texture *, int, int);
extern void drawImagePlus(SDL_Texture *image, int x, int y, double rotation, SDL_Point center, SDL_RendererFlip flip);
extern void drawMap(int);
extern void drawMonster(GameObject *entity);
extern void drawPauseMenu(void);
extern void drawPlateforme(void);
extern void drawPlayer(void);
extern void drawShuriken(void);
extern void drawStartMenu(void);
extern void drawString(char *text, int x, int y, int r, int g, int b, int a);
extern void drawTile(SDL_Texture *image, int destx, int desty, int srcx, int srcy);
extern void freeSound(void);
extern void gestionInputs(Input *input);
extern SDL_Texture *getBackground(void);
extern int getBeginX(void);
extern int getBeginY(void);
extern int getExplosionNumber(void);
extern void getInput(Input *input);
extern void getItem(int itemNumber);
extern void getJoystick(Input *input);
extern int getLevel(void);
extern int getLife(void);
extern int getMaxX(void);
extern int getMaxY(void);
extern int getMenuType(void);
extern GameObject *getMonster(int nombre);
extern int getMonsterNumber(void);
extern int getNombreDetoiles(void);
extern int getNombreDeVies(void);
extern int getOnMenu(void);
extern int getPlateformeNumber(void);
extern GameObject *getPlayer(void);
extern int getPlayerDirection(void);
extern int getPlayerx(void);
extern int getPlayery(void);
extern SDL_Renderer *getrenderer(void);
extern int getShurikenNumber(void);
extern int getStartX(void);
extern int getStartY(void);
extern SDL_Texture *getTileSetA(void);
extern SDL_Texture *getTileSetB(void);
extern int getTileValue(int y, int x);
extern void init(char *);
extern void initHUD(void);
extern void initMaps(void);
extern void initMenus(void);
extern void initializeNewMonster(int x, int y);
extern void initMonsterSprites(void);
extern void initPlateforme(int x, int y, int type);
extern void initPlayerSprites(void);
extern void initializePlayer(int newLevel);
extern void killPlayer(void);
extern void loadExplosionImage(void);
extern void loadFont(char *, int);
extern void loadGame(void);
extern SDL_Texture *loadImage(char *name);
extern void loadMap(char *name);
extern void loadPlateforme(void);
extern void loadShurikenImage(void);
extern void loadSong(char filename[200]);
extern void loadSound(void);
extern void mapCollision(GameObject *entity);
extern void monsterCollisionToMap(GameObject *entity);
extern void openJoystick(void);
extern void playerHurts(GameObject *monster);
extern void playSoundFx(int type);
extern void resetCheckpoint(void);
extern void resetExplosions(void);
extern void resetMonsters(void);
extern void resetPlateformes(void);
extern void resetShuriken(void);
extern void setNombreDeVies(int valeur);
extern void setNombreDetoiles(int valeur);
extern void setOnMenu(int valeur, int type);
extern void setPlayerx(int valeur);
extern void setPlayery(int valeur);
extern void setStartX(int valeur);
extern void setStartY(int valeur);
extern void SetValeurDuNiveau(int valeur);
extern int shurikenCollide(GameObject *monster);
extern void updateMonsters();
extern void updatePauseMenu(Input *input);
extern void updatePlayer(Input *input);
extern void updateStartMenu(Input *input);
#endif
|
Et voilà ! On compile, et ça marche !!! ![]()
Nos explosions sont maintenant opérationnelles, et on va pouvoir éclater du zombie à tout-va !!! ![]()
Ahah, c'est trop cool !! ![]()
Bon, eh bien voilà qui conclut la partie tout public de ce reboot du Big Tuto en SDL 2 ! J'espère qu'il vous aura plu et que vous aurez appris plein de choses. ![]()
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
, sachant que si vous décidez de soutenir le site en devenant premium, vous pourrez profiter bientôt de petits coups de pouce supplémentaires ! ![]()

@ bientôt pour de nouveaux chapitres premium ! ![]()
Jay

English
Français 
