
Big Tuto SFML 2 / Action-RPG : Legends of Meruvia
Chapitre 18 : Ajoutons des explosions !
Tutoriel présenté par : Jérémie F. Bellanger (Jay81)
Date d'écriture : 8 juillet 2016
Date de révision : -
Prologue
Dans ce dernier chapitre, nous allons rajouter des explosions, pour faire exploser nos monstres quand ils meurent ! 
Mais bon, là encore, le procédé va être le même que pour Rabidja.
Prêt pour un petit TP ?
Essayez donc de le faire tout seul, et éventuellement d'ajouter votre petite touche personnelle ! ![]()
Des monstres qui explosent !
Commençons par vérifier que le sprite de notre animation se situe bien 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 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) violente, à 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 (sinon, ça va faire bizarre !
).
Voilà pour l'essentiel ! ![]()
Le code
Commençons par créer une nouvelle classe Explosions. ![]()
Fichier : Créer un nouveau fichier explosions.h et y copier :
|
//Legends of Meruvia - C++ / SFML 2
//Copyright / Droits d'auteur : www.meruvia.fr - Jérémie F. Bellanger
#ifndef EXPLOSIONS_H
#define EXPLOSIONS_H
#include <SFML/Graphics.hpp>
#include <iostream>
class Player;
class Map;
class Monster;
class Explosions
{
public:
//Constructeur
Explosions();
//Accesseurs
int getX(void) const;
int getY(void) const;
int getW(void) const;
int getH(void) const;
int getFrameNumber(void) const;
int getFrameTimer(void) const;
int getTimerMort(void) const;
//Fonctions
void initialize(int Ax, int Ay);
int draw(Map &map, sf::RenderWindow &window);
void copy(Explosions &explosion);
private:
//Variables de la classe en accès privé
int x, y, h, w;
int direction;
int frameNumber, frameTimer, frameMax;
int timerMort;
//Sprite
sf::Texture Texture;
sf::Sprite Sprite;
/******************/
/* Constantes */
/******************/
const int TIME_BETWEEN_2_FRAMES_PLAYER = 4;
};
#endif
|
Fichier : Créer un nouveau fichier explosions.cpp et y copier :
|
//Legends of Meruvia - C++ / SFML 2
//Copyright / Droits d'auteur : www.meruvia.fr - Jérémie F. Bellanger
#include "explosions.h"
#include "map.h"
using namespace std;
using namespace sf;
//Accesseurs
int Explosions::getX(void) const { return x; }
int Explosions::getY(void) const { return y; }
int Explosions::getW(void) const { return w; }
int Explosions::getH(void) const { return h; }
int Explosions::getFrameNumber(void) const { return frameNumber; }
int Explosions::getFrameTimer(void) const { return frameTimer; }
int Explosions::getTimerMort(void) const { return timerMort; }
//Constructeur
Explosions::Explosions()
{
//Chargement de la spritesheet de Rabidja
if (!Texture.loadFromFile("graphics/explosion.png"))
{
// Erreur
cout << "Erreur durant le chargement du sprite de l'explosion." << endl;
}
else
Sprite.setTexture(Texture);
//Initialisation des variables :
x = y = h = w = 0;
frameNumber = 0;
frameTimer = TIME_BETWEEN_2_FRAMES_PLAYER;
timerMort = 0;
frameMax = 7;
}
//Fonctions
void Explosions::initialize(int Ax, int Ay)
{
//On réinitialise la frame et le timer
frameNumber = 0;
frameTimer = TIME_BETWEEN_2_FRAMES_PLAYER;
frameMax = 7;
/* Ses coordonnées de démarrage seront envoyées en arguments */
x = Ax + 5;
y = Ay + 5;
/* Hauteur et largeur de notre explosion (ici 32 x 32 pixels) ) */
w = 32;
h = 32;
//Variable nécessaire pour savoir si on doit supprimer ou non l'explosion (après un tour)
timerMort = 0;
}
int Explosions::draw(Map &map, RenderWindow &window)
{
/* Gestion du timer */
// Si notre timer (un compte à rebours en fait) arrive à zéro
if (frameTimer <= 0)
{
//On le réinitialise
frameTimer = TIME_BETWEEN_2_FRAMES_PLAYER;
//Et on incrémente notre variable qui compte les frames de 1 pour passer à la suivante
frameNumber++;
//Mais si on dépasse la frame max, l'explosion est finie, il faut la détruire
if (frameNumber > frameMax)
return 1;
}
//Sinon, on décrémente notre timer
else
frameTimer--;
//Ensuite, on peut passer la main à notre fonction
Sprite.setPosition(Vector2f(x - map.getStartX(), y - map.getStartY()));
//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 = 40; 2 = 80...
Sprite.setTextureRect(sf::IntRect(frameNumber * w, 0, w, h));
window.draw(Sprite);
return 0;
}
void Explosions::copy(Explosions &explosion)
{
//Copie des variables nécessaires :
x = explosion.getX();
y = explosion.getY();
h = explosion.getH();
w = explosion.getW();
frameTimer = explosion.getFrameTimer();
frameNumber = explosion.getFrameNumber();
timerMort = explosion.getTimerMort();
}
|
Fichier : player.cpp : Modifier les passages du code :
|
void Player::initialize(Map &map)
{
//PV à 3
life = 3;
//Timer d'invincibilité à 0
invincibleTimer = 0;
//Indique l'état et la direction de notre héros
direction = RIGHT;
etat = IDLE;
//Indique le numéro de la frame où commencer
frameNumber = 0;
//...la valeur de son chrono ou timer
frameTimer = TIME_BETWEEN_2_FRAMES_PLAYER;
//... et son nombre de frames max (8 pour l'anim' IDLE
// = ne fait rien)
frameMax = 8;
x = map.getBeginX();
y = map.getBeginY();
//On recentre la caméra
map.setStartX(map.getBeginX() - (SCREEN_WIDTH / 2));
map.setStartY(map.getBeginY() - (SCREEN_HEIGHT / 2));
/* Hauteur et largeur de notre héros */
w = PLAYER_WIDTH;
h = PLAYER_HEIGTH;
//Variables nécessaires au fonctionnement de la gestion des collisions
timerMort = 0;
isAttacking = 0;
//On réinitialise le nombre de monstres
map.setNombreMonstres(0);
//On réinitialise le nombre de projectiles magiques
magicNumber = 0;
//On réinitialise le nombre d'explosions
nombreExplosions = 0;
map.setWarpDirection(-1);
}
void Player::reinitialize(Map &map)
{
// Coordonnées de démarrage de notre héros selon la direction de warp
//Si on n'a pas warpé, alors on commence aux coordonnées de départ de la map
if (map.getWarpDirection() == -1)
{
x = map.getBeginX();
y = map.getBeginY();
//On recentre la caméra
map.setStartX(map.getBeginX() - (SCREEN_WIDTH / 2));
map.setStartY(map.getBeginY() - (SCREEN_HEIGHT / 2));
}
//Si on a warpé en haut
else if (map.getWarpDirection() == UP)
{
//On change la valeur en y du héros pour qu'il se
//trouve en bas de la map
y = map.getMaxY() - h - 1;
//On recentre la caméra
map.setStartY(map.getMaxY() - SCREEN_HEIGHT);
}
//Si on a warpé en bas
else if (map.getWarpDirection() == DOWN)
{
//On change la valeur en y du héros pour qu'il se
//trouve en haut de la map
y = 1;
//On recentre la caméra
map.setStartY(0);
}
//Si on a warpé à gauche
else if (map.getWarpDirection() == LEFT)
{
//On change la valeur en x du héros pour qu'il se
//trouve à droite de la map
x = map.getMaxX() - w - 1;
//On recentre la caméra
map.setStartX(map.getMaxX() - SCREEN_WIDTH);
}
//Si on a warpé à droite
else if (map.getWarpDirection() == RIGHT)
{
//On change la valeur en x du héros pour qu'il se
//trouve à gauche de la map
x = 1;
//On recentre la caméra
map.setStartX(0);
}
//Si on a utilisé une warp spéciale
else if (map.getWarpDirection() == 4)
{
POINT point;
//On détecte la warp spéciale utilisée (1, 2, etc)
//et on enregistre ses coordonnées dans point
point.x = map.detectWarpSpe(numberSPE + SPE1).x;
point.y = map.detectWarpSpe(numberSPE + SPE1).y;
//On place le héros près de cette warp (sans la toucher)
//suivant la direction qu'il avait en arrivant :
//Ainsi s'il allait vers le haut, on va le faire réapparaître
//au-dessus de la warp, et ainsi de suite...
if (direction == UP)
{
x = point.x + 6;
y = point.y - h - 6;
}
else if (direction == DOWN)
{
x = point.x + 6;
y = point.y + TILE_SIZE + 6;
}
else if (direction == RIGHT)
{
x = point.x + TILE_SIZE + 6;
y = point.y + 6;
}
else if (direction == LEFT)
{
x = point.x - w - 6;
y = point.y + 6;
}
else
{
x = map.getBeginX();
y = map.getBeginY();
}
//On recentre la caméra
map.setStartX(x - (SCREEN_WIDTH / 2));
map.setStartY(y - (SCREEN_HEIGHT / 2));
}
//On réinitialise le nombre de monstres
map.setNombreMonstres(0);
//On réinitialise le nombre de projectiles magiques
magicNumber = 0;
//On réinitialise le nombre d'explosions
nombreExplosions = 0;
}
|
Ici, on rajoute simplement : nombreExplosions = 0; dans les fonctions initialize() et reinitialize() pour remettre le nombre d'explosions à zéro ! (c'est mieux !
)
Passons désormais au main() :
Fichier : main.h : Remplacer le code par :
|
//Legends of Meruvia - C++ / SFML 2
//Copyright / Droits d'auteur : www.meruvia.fr - Jérémie F. Bellanger
#include <cstdlib>
#include <iostream>
#include <SFML/Graphics.hpp>
#include "input.h"
#include "map.h"
#include "player.h"
#include "monster.h"
#include "sounds.h"
#include "magic.h"
#include "menus.h"
#include "explosions.h"
using namespace std;
using namespace sf;
//Fonctions
void update(Input &input, Map &map, Player &player, Monster monster[], Sounds &sounds,
Magic magic[], Menus &menus, Explosions explosion[]);
void draw(sf::RenderWindow &window, Map &map, Player &player, Monster monster[],
Magic magic[], Menus &menus, Explosions explosion[]);
// Taille de la fenêtre : 800x480 pixels
const int SCREEN_WIDTH = 800;
const int SCREEN_HEIGHT = 480;
//Nombre max de monstres à l'écran
const int MONSTRES_MAX = 50;
//Nombre max de projectiles magiques à l'écran
const int MAGIC_MAX = 6;
const int TIME_BETWEEN_2_SHOTS = 30;
const int MOONWALK_TIMER = 8;
//Directions
const int DOWN = 0;
const int UP = 1;
const int RIGHT = 2;
const int LEFT = 3;
//Une enum pour la gestion du menu.
const enum { START, PAUSE };
//Nombre max d'explosions à l'écran
const int EXPLOSIONS_MAX = 10;
|
Dans le header, on rajoute #include "explosions.h" en haut du fichier, ainsi que la constante EXPLOSIONS_MAX et on met à jour les prototypes de draw() et update().
|
//Legends of Meruvia - C++ / SFML 2.3.2
//Copyright / Droits d'auteur : www.meruvia.fr - Jérémie F. Bellanger
#include "main.h"
int main(int argc, char *argv[])
{
// Création d'une fenêtre en SFML
RenderWindow window(VideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, 32),
"Meruvia - Big Tuto A-RPG/SFML2 - Chapitre 18 - www.meruvia.fr");
//On active la synchro verticale
window.setVerticalSyncEnabled(true);
//Instanciation des classes
Input input;
Map map;
Player player;
Sounds sounds;
Menus menus;
//On instancie autant de classes que de monstres gérables
Monster monster[MONSTRES_MAX];
//On instancie autant de classes que de projectiles magiques gérables
Magic magic[MAGIC_MAX];
//On instancie autant de classes que d'explosions gérables
Explosions explosion[EXPLOSIONS_MAX];
//On lance la musique en boucle
sounds.PlayMusic(true);
//On commence au premier niveau
map.setLevel(1);
map.changeLevel();
//On initialise le player
player.initialize(map);
player.setGold(100);
//On commence par le menu start
menus.setOnMenu(true, START);
// Boucle infinie, principale, du jeu
while (window.isOpen())
{
// Gestion des inputs
input.gestionInputs(window);
//Updates
update(input, map, player, monster, sounds, magic, menus, explosion);
// Dessin - draw
draw(window, map, player, monster, magic, menus, explosion);
window.display();
}
// On quitte
return 0;
}
//Fonction de mise à jour du jeu : gère la logique du jeu
void update(Input &input, Map &map, Player &player, Monster monster[], Sounds &sounds, Magic magic[], Menus &menus, Explosions explosion[])
{
//Si on n'est pas dans un menu
if (!menus.getOnMenu())
{
/** MISES A JOUR - UPDATES **/
//On met à jour le player
player.update(input, map, magic, menus);
//On met à jour les monstres un par un
for (int i = 0; i < map.getNombreMonstres(); i++)
{
if (monster[i].update(i, map, player, monster, sounds) == 2)
{
//Si l'update du monstre renvoie 2, c'est qu'il doit mourir :
//On crée une explosion à sa place, si on peut
if (player.getNombreExplosions() < EXPLOSIONS_MAX)
{
explosion[player.getNombreExplosions()].initialize(monster[i].getX(), monster[i].getY());
player.setNombreExplosions(player.getNombreExplosions() + 1);
}
//on copie à sa place le dernier monstre avant de retirer un monstre
monster[i].copy(monster[map.getNombreMonstres() - 1]);
map.setNombreMonstres(map.getNombreMonstres() - 1);
}
}
//On met à jour les projectiles magiques un par un
for (int i = 0; i < player.getMagic(); i++)
{
if (magic[i].update(player, map) == 1)
{
//On supprime le projectile magique s'il sort de l'écran
magic[i].copy(magic[player.getMagic() - 1]);
player.setMagic(player.getMagic() - 1);
}
else
{
//On teste les collisions avec tous les monstres
for (int j = 0; j < map.getNombreMonstres(); j++)
{
if (magic[i].checkCollisions(monster[j]) == 1)
{
//Si la fonction renvoie 2, c'est qu'il y a contact avec le monstre
//Celui-ci doit être blessé et il faut supprimer le projectile magique.
//Et on blesse le monstre s'il n'est pas invincible
if (monster[j].getInvincibleTimer() == 0)
{
monster[j].setLife(monster[j].getLife() - 1);
monster[j].setInvincibleTimer(TIME_BETWEEN_2_SHOTS);
}
//Si le monstre est mort
if (monster[j].getLife() <= 0)
{
//On crée une explosion à sa place, si on peut
if (player.getNombreExplosions() < EXPLOSIONS_MAX)
{
explosion[player.getNombreExplosions()].initialize(monster[j].getX(), monster[j].getY());
player.setNombreExplosions(player.getNombreExplosions() + 1);
}
//On copie à sa place le dernier monstre avant de retirer un monstre
monster[j].copy(monster[map.getNombreMonstres() - 1]);
map.setNombreMonstres(map.getNombreMonstres() - 1);
}
else
{
//On le déclare touché, pour qu'il recule en arrière
monster[j].setisHurt(MOONWALK_TIMER);
//On détermine sa direction de recul, par rapport au placement
//de l'ennemi
if (magic[i].getDirection() == DOWN)
monster[j].setHurtDirection(DOWN);
else if (magic[i].getDirection() == UP)
monster[j].setHurtDirection(UP);
else if (magic[i].getDirection() == RIGHT)
monster[j].setHurtDirection(RIGHT);
else if (magic[i].getDirection() == LEFT)
monster[j].setHurtDirection(LEFT);
}
//On supprime le projectile magique
magic[i].copy(magic[player.getMagic() - 1]);
player.setMagic(player.getMagic() - 1);
//On joue le sound Fx
sounds.PlaySoundFx(sounds.DESTROY);
}
}
}
}
}
else
{
if (menus.getMenuType() == START)
menus.updateStartMenu(input, map, player);
else if (menus.getMenuType() == PAUSE)
menus.updatePauseMenu(input);
}
}
//Fonction de dessin du jeu : dessine tous les éléments
void draw(RenderWindow &window, Map &map, Player &player, Monster monster[],
Magic magic[], Menus &menus, Explosions explosion[])
{
//On efface tout
window.clear();
//Si on n'est pas dans un menu, ou si on est en pause, on affiche le jeu
if (!menus.getOnMenu() || (menus.getOnMenu() && menus.getMenuType() == PAUSE))
{
// Affiche la map de tiles : layer 2 (couche du fond)
map.draw(2, window, monster);
// Affiche la map de tiles : layer 1 (couche active : sol, etc.)
map.draw(1, window, monster);
// Affiche le joueur
player.draw(map, window);
//On affiche les monstres un par un
for (int i = 0; i < map.getNombreMonstres(); i++)
{
monster[i].draw(map, window);
}
//On affiche les projectiles magiques un par un
for (int i = 0; i < player.getMagic(); i++)
{
magic[i].draw(map, window);
}
//On affiche les explosions une par une
for (int i = 0; i < player.getNombreExplosions(); i++)
{
//Si la fonction de dessin renvoie 1, notre explosion est terminée
//et on doit la supprimer :
if (explosion[i].draw(map, window) == 1)
{
//On copie à sa place la dernière du tableau avant de retirer une explosion
explosion[i].copy(explosion[player.getNombreExplosions() - 1]);
player.setNombreExplosions(player.getNombreExplosions() - 1);
}
}
// Affiche la map de tiles : layer 3 (couche en foreground / devant)
map.draw(3, window, monster);
//Affichage du HUD
map.drawHud(player, window);
//Affichage du menu PAUSE le cas échéant
if (menus.getOnMenu() && menus.getMenuType() == PAUSE)
menus.drawPauseMenu(window, map);
}
//Sinon, on affiche l'écran-titre
else if (menus.getOnMenu() && menus.getMenuType() == START)
menus.drawStartMenu(window, map);
}
|
.jpg)
@ bientôt pour de nouveaux tutos ! ![]()
Jay

English
Français 