Big Tuto SFML 2 : Rabidja v. 3.0

Chapitre 19 : Lançons des shurikens !

Tutoriel présenté par : Jérémie F. Bellanger (Jay81)
Date d'écriture : 1er mars 2015
Date de révision : 20 mars 2016

      Prologue

   Bon, sauter sur la tête des zombies, c'est cool, mais un vrai ninja se doit de lancer des shurikens ! angel Quand même ! cool

   Et c'est ce qu'on va faire ici, en nous inspirant de la gestion de nos précédents sprites, comme les monstres ou les plateformes flottantes. wink

   Allez, on est parti ! smiley

 

      Nouveaux fichiers à ajouter au projet

   Avant de commencer, il va nous falloir ajouter une nouvelle image pour nos shurikens ! wink

   Enregistrez donc l'image suivante dans le dossier graphics de votre projet, sous le nom shuriken.png : angel

shuriken.png

      Le code

   La création de nos shurikens va beaucoup ressembler à celle de nos monstres (et dans le chapitre suivant à celle de nos explosions). wink

   Avant de créer notre nouvelle classe Shurikens, nous allons donc rajouter quelques variables à notre classe Player pour gérer leur nombre (comme pour les monstres).

   Commençons par l'en-tête de la classe :

Fichier : player.h : Modifier le code :

//Rabidja 3 - nouvelle version convertie en SFML 2
//Copyright / Droits d'auteur : www.meruvia.fr - Jérémie F. Bellanger
 
#ifndef PLAYER_H
#define PLAYER_H
 
#include <SFML/Graphics.hpp>
#include <iostream>
 
class Map;
class Input;
class Sounds;
class Menus;
class Shurikens;
 
 
class Player
{
 
public:
 
//Structures
struct Point { int x, y; };
 
//Constructeur
Player();
 
//Accesseurs
int getX(void) const;
int getY(void) const;
int getW(void) const;
int getH(void) const;
float getDirX(void) const;
float getDirY(void) const;
int getOnGround(void) const;
int getLife(void) const;
int getVies(void) const;
int getEtoiles(void) const;
int getDirection(void) const;
int getNombreShurikens(void) const;
 
//Mutateurs
void setX(int valeur);
void setY(int valeur);
void setW(int valeur);
void setH(int valeur);
void setDirX(float valeur);
void setDirY(float valeur);
void setOnGround(bool valeur);
void setTimerMort(int valeur);
void setVies(int valeur);
void setEtoiles(int valeur);
void setCheckpoint(bool valeur);
void killPlayer(Sounds &sounds);
void playerHurts(Sounds &sounds);
void setNombreShurikens(int valeur);
 
//Fonctions
void initialize(Map &map, bool newLevel);
void draw(Map &map, sf::RenderWindow &window);
void update(Input &input, Map &map, Sounds &sounds, Menus &menus, Shurikens shuriken[]);
void centerScrolling(Map &map);
void mapCollision(Map &map, Sounds &sounds);
Point segment2segment(int Ax0, int Ay0, int Bx0, int By0, int Cx0, int Cy0, int Dx0, int Dy0);
void getSlopeSegment(int tx, int ty, int pente, Point &s1, Point &s2);
int slopeEquation(int pente, double *a, double *b);
int checkSlope(Map &map);
void getItem(int itemNumber, Sounds &sounds);
 
 
private:
 
//Variables de la classe en accès privé
 
// Points de vie/santé + chrono d'invicibilité
int life, invincibleTimer;
 
//Vies et étoiles (100 étoiles = 1 vie)
int vies, etoiles;
 
// Coordonnées du sprite
int x, y;
 
// Largeur, hauteur du sprite
int h, w;
 
// Checkpoint pour le héros (actif ou non)
bool checkpointActif;
// + coordonnées de respawn (réapparition)
int respawnX, respawnY;
 
// Variables utiles pour l'animation :
// Numéro de la frame (= image) en cours + timer
int frameNumber, frameTimer, frameMax;
// Nombre max de frames, état du sprite et direction
// dans laquelle il se déplace (gauche / droite)
int etat, direction;
 
// Variables utiles pour la gestion des collisions :
//Est-il sur le sol, chrono une fois mort
int timerMort;
bool onGround;
 
//Vecteurs de déplacement temporaires avant détection
//des collisions avec la map
float dirX, dirY;
//Sauvegarde des coordonnées de départ
int saveX, saveY;
 
//Variable pour le double saut
bool Playerjump;
 
//Gestion des pentes par Stephantasy
float dirXmem, dirYmem;
int posXmem, posYmem;
int wasOnGround;
int wasOnSlope;
 
//Spritesheet de Rabidja
sf::Texture rabidjaTexture;
sf::Sprite rabidja;
 
//Nombre de shurikens à l'écran
int nombreShurikens;
 
 
 
/******************/
/* Constantes */
/******************/
 
/* Taille maxi de la map : 400 x 150 tiles */
const int MAX_MAP_X = 400;
const int MAX_MAP_Y = 150;
 
/* Taille d'une tile (32 x 32 pixels) */
const int TILE_SIZE = 32;
 
/* Constante pour l'animation */
const int TIME_BETWEEN_2_FRAMES_PLAYER = 4;
 
/* Taille du sprite de notre héros (largeur = width et hauteur = heigth) */
const int PLAYER_WIDTH = 40;
const int PLAYER_HEIGTH = 50;
 
//Vitesse de déplacement en pixels du sprite
const int PLAYER_SPEED = 4;
 
//Valeurs attribuées aux états/directions
const int IDLE = 0;
const int WALK = 1;
const int JUMP1 = 2;
const int JUMP2 = 3;
const int DEAD = 4;
 
const int RIGHT = 1;
const int LEFT = 2;
 
//Constantes définissant la gravité et la vitesse max de chute
const double GRAVITY_SPEED = 0.6;
const int MAX_FALL_SPEED = 15;
const int JUMP_HEIGHT = 10;
 
// Taille de la fenêtre : 800x480 pixels
const int SCREEN_WIDTH = 800;
const int SCREEN_HEIGHT = 480;
 
//Constantes pour les limites de la caméra avant scrolling
const int LIMITE_X = 400;
const int LIMITE_Y = 220;
const int LIMITE_W = 100;
const int LIMITE_H = 80;
 
//Enum pour les boutons
const enum{ up, down, right, left, attack, jump, enter };
 
//Nombre max de levels
const int LEVEL_MAX = 2;
 
//Une enum pour la gestion du menu.
const enum { START, PAUSE };
 
//Nombre max de shurikens à l'écran
const int SHURIKEN_MAX = 6;
 
 
/*************************/
/* VALEURS DES TILES */
/************************/
 
// Constante définissant le seuil entre les tiles traversables
// (blank) et les tiles solides
const int BLANK_TILE = 99;
 
//Plateformes traversables
const int TILE_TRAVERSABLE = 80;
 
//Tiles Power-ups
const int TILE_POWER_UP_DEBUT = 77;
const int TILE_POWER_UP_FIN = 79;
const int TILE_POWER_UP_COEUR = 78;
 
//Autres Tiles spéciales
const int TILE_RESSORT = 125;
const int TILE_CHECKPOINT = 23;
const int TILE_MONSTRE = 136;
const int TILE_PIKES = 127;
 
//Tiles plateformes mobiles
const int TILE_PLATEFORME_DEBUT = 130;
const int TILE_PLATEFORME_FIN = 131;
 
// Tiles pentes à 26.5° ; BenH = de BAS en HAUT ; HenB = De HAUT en BAS
const int TILE_PENTE_26_BenH_1 = 69;
const int TILE_PENTE_26_BenH_2 = 70;
const int TILE_PENTE_26_HenB_1 = 71;
const int TILE_PENTE_26_HenB_2 = 72;
 
};
#endif
 
   Voici ce qui change :
- Ajout de class Shurikens;; en haut du fichier, à la suite des autres,
- Ajout de la variable nombreShurikens, qui nous permettra de savoir combien on a de shurikens dans la nature,
- Ajout de la constante SHURIKEN_MAX pour définir le nombre max de shurikens qu'on peut balancer à l'écran,
- Ajout du prototype de l'accesseur getNombreShurikens(),
- Ajout du prototype du mutateur setNombreShurikens(),
- Ajout de l'argument Shurikens shuriken[] au prototype de la fonction update(), pour qu'elle puisse par la suite créer de nouveaux shurikens quand on appuiera sur la touche attaquer ! wink
 
   Passons maintenant au code du fichier player.cpp. Je n'ai reproduit ci-dessous que ce qui change :

 

Fichier : player.cpp : Modifier le code :

//Rabidja 3 - nouvelle version intégralement en SFML 2
//Copyright / Droits d'auteur : www.meruvia.fr - Jérémie F. Bellanger
 
#include "player.h"
#include "map.h"
#include "input.h"
#include "sounds.h"
#include "menus.h"
#include "shurikens.h"
 
 
using namespace std;
using namespace sf;
 
 
//Code coupé...
 
 
//Accesseurs
 
//Code coupé...
 
int Player::getNombreShurikens(void) const { return nombreShurikens; }
 
 
//Mutateurs
 
//Code coupé...
 
void Player::setNombreShurikens(int valeur) { nombreShurikens = valeur; }
 
 
//Code coupé...
 
 
//Fonctions
 
void Player::initialize(Map &map, bool newLevel)
{
//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;
 
/* Coordonnées de démarrage/respawn de notre héros */
if (checkpointActif == true)
{
x = respawnX;
y = respawnY;
}
else
{
x = map.getBeginX();
y = map.getBeginY();
}
 
//On réinitiliase les coordonnées de la caméra
//si on change de niveau
if (newLevel == true)
{
map.setStartX(map.getBeginX());
map.setStartY(map.getBeginY());
}
 
/* 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;
onGround = false;
 
//On réinitialise le nombre de monstres
map.setNombreMonstres(0);
 
//On réinitialise le nombre de plateformes
map.setNombrePlateformes(0);
 
//On réinitialise le nombre de shurikens
nombreShurikens = 0;
 
}
 
 
//Code coupé...
 
 
void Player::update(Input &input, Map &map, Sounds &sounds, Menus &menus, Shurikens shuriken[])
{
//On rajoute un timer au cas où notre héros mourrait lamentablement en tombant dans un trou...
//Si le timer vaut 0, c'est que tout va bien, sinon, on le décrémente jusqu'à 0, et là,
//on réinitialise.
//C'est pour ça qu'on ne gère le joueur que si ce timer vaut 0.
if (timerMort == 0)
{
//On gère le timer de l'invincibilité
if (invincibleTimer > 0)
invincibleTimer--;
 
//On réinitialise notre vecteur de déplacement latéral (X), pour éviter que le perso
//ne fonce de plus en plus vite pour atteindre la vitesse de la lumière ! ;)
//Essayez de le désactiver pour voir !
dirX = 0;
 
// La gravité fait toujours tomber le perso : on incrémente donc le vecteur Y
dirY += GRAVITY_SPEED;
 
//Mais on le limite pour ne pas que le joueur se mette à tomber trop vite quand même
if (dirY >= MAX_FALL_SPEED)
dirY = MAX_FALL_SPEED;
 
 
//Voilà, au lieu de changer directement les coordonnées du joueur, on passe par un vecteur
//qui sera utilisé par la fonction mapCollision(), qui regardera si on peut ou pas déplacer
//le joueur selon ce vecteur et changera les coordonnées du player en fonction.
if (input.getButton().left == true)
{
dirX -= PLAYER_SPEED;
//Et on indique qu'il va à gauche (pour le flip
//de l'affichage, rappelez-vous).
direction = LEFT;
 
//Si ce n'était pas son état auparavant et qu'il est bien sur
//le sol (car l'anim' sera différente s'il est en l'air)
if (etat != WALK && onGround == true)
{
//On enregistre l'anim' de la marche et on l'initialise à 0
etat = WALK;
frameNumber = 0;
frameTimer = TIME_BETWEEN_2_FRAMES_PLAYER;
frameMax = 8;
}
}
 
//Si on détecte un appui sur la touche fléchée droite
else if (input.getButton().right == true)
{
//On augmente les coordonnées en x du joueur
dirX += PLAYER_SPEED;
//Et on indique qu'il va à droite (pour le flip
//de l'affichage, rappelez-vous).
direction = RIGHT;
 
//Si ce n'était pas son état auparavant et qu'il est bien sur
//le sol (car l'anim' sera différente s'il est en l'air)
if (etat != WALK && onGround == true)
{
//On enregistre l'anim' de la marche et on l'initialise à 0
etat = WALK;
frameNumber = 0;
frameTimer = TIME_BETWEEN_2_FRAMES_PLAYER;
frameMax = 8;
}
}
 
//Si on n'appuie sur rien et qu'on est sur le sol, on charge l'animation marquant l'inactivité (Idle)
else if (input.getButton().right == false && input.getButton().left == false && onGround == true)
{
//On teste si le joueur n'était pas déjà inactif, pour ne pas recharger l'animation
//à chaque tour de boucle
if (etat != IDLE)
{
//On enregistre l'anim' de l'inactivité et on l'initialise à 0
etat = IDLE;
frameNumber = 0;
frameTimer = TIME_BETWEEN_2_FRAMES_PLAYER;
frameMax = 8;
}
 
}
 
 
//Et voici la fonction de saut très simple :
//Si on appuie sur la touche saut et qu'on est sur le sol, alors on attribue une valeur
//négative au vecteur Y
//parce que sauter veut dire se rapprocher du haut de l'écran et donc de y=0.
if (input.getButton().jump == true)
{
if (onGround == true)
{
dirY = -JUMP_HEIGHT;
onGround = false;
Playerjump = true;
 
//On joue le sound Fx
sounds.PlaySoundFx(sounds.JUMP);
}
// Si on est en saut 1, on peut faire un deuxième bond et on remet jump1 à 0
else if (Playerjump == true)
{
dirY = -JUMP_HEIGHT;
Playerjump = false;
 
//On joue le sound Fx
sounds.PlaySoundFx(sounds.JUMP);
}
input.setButton(jump, false);
}
 
//On gère le lancer de shurikens
if (input.getButton().attack)
{
//Si on le peut, on crée un nouveau shuriken
if (nombreShurikens < SHURIKEN_MAX)
{
shuriken[nombreShurikens].initialize(direction, x, y);
nombreShurikens++;
}
input.setButton(attack, false);
}
 
//Si on appuie sur Enter
if (input.getButton().enter)
{
//On met le jeu en pause
menus.setOnMenu(true, PAUSE);
input.setButton(enter, false);
}
 
 
/* Réactive la possibilité de double saut si on tombe sans sauter */
if (onGround == true)
Playerjump = true;
 
 
//On gère l'anim du saut
if (onGround == false)
{
//Si on est en saut 1, on met l'anim' du saut normal
if (Playerjump == true)
{
if (etat != JUMP1)
{
etat = JUMP1;
frameNumber = 0;
frameTimer = TIME_BETWEEN_2_FRAMES_PLAYER;
frameMax = 2;
}
}
else
{
if (etat != JUMP2)
{
etat = JUMP2;
frameNumber = 0;
frameTimer = TIME_BETWEEN_2_FRAMES_PLAYER;
frameMax = 4;
}
}
}
 
//On rajoute notre fonction de détection des collisions qui va mettre à
//jour les coordonnées de notre super lapin.
mapCollision(map, sounds);
 
//On gère le scrolling (fonction ci-dessous)
centerScrolling(map);
 
}
 
//Gestion de la mort quand le héros tombe dans un trou :
//Si timerMort est différent de 0, c'est qu'il faut réinitialiser le joueur.
//On ignore alors ce qui précède et on joue cette boucle (un wait en fait) jusqu'à ce que
// timerMort == 1. A ce moment-là, on le décrémente encore -> il vaut 0 et on réinitialise
//le jeu avec notre bonne vieille fonction d'initialisation ;) !
if (timerMort > 0)
{
timerMort--;
 
if (timerMort == 0)
{
//On perd une vie
vies--;
 
// Si on est mort, on réinitialise le niveau
map.changeLevel();
initialize(map, false);
 
//Sauf si on a plus de vies, on retourne à l'écran-titre
if (vies < 0)
{
//Dans ce cas on retourne au menu start
menus.setOnMenu(true, START);
}
}
}
}

 

   Passons maintenant rapidement en revue ce qui a changé :
on a tout d'abord ajouté #include "shurikens.h" en haut du fichier,
- puis on a ajouté un accesseur getNombreShurikens() et un mutateur setNombreShurikens(), pour pouvoir modifier notre variable nombreShurikens de l'extérieur,
- ensuite, on a mis à jour notre fonction initialize() en remettant le nombre de shurikens à zéro,
- et enfin dans la fonction update() : 
- on lui a ajouté l'argument Shurikens shuriken[],
- et on a rajouté un petit bout de code qui va tester si on appuie sur la touche d'attaque. Si c'est le cas, on crée un shuriken s'il en reste de disponible.
 
   Et voilà, il est maintenant temps de créer notre nouvelle classe Shurikens !
 

Fichier : Créer un nouveau fichier shurikens.h et y copier :

//Rabidja 3 - nouvelle version convertie en SFML 2
//Copyright / Droits d'auteur : www.meruvia.fr - Jérémie F. Bellanger
#ifndef SHURIKEN_H
#define SHURIKEN_H
 
#include <SFML/Graphics.hpp>
#include <iostream>
 
class Player;
class Map;
class Monster;
 
 
class Shurikens
{
 
 
public:
 
//Constructeur
Shurikens();
 
//Accesseurs
int getX(void) const;
int getY(void) const;
int getW(void) const;
int getH(void) const;
int getDirection(void) const;
double getRotation(void) const;
sf::Vector2f getCenter(void) const;
 
//Fonctions
void initialize(int Adirection, int Ax, int Ay);
int update(Player &player, Map &map);
void draw(Map &map, sf::RenderWindow &window);
int checkCollisions(Monster &monster);
void copy(Shurikens &shuriken);
 
 
 
private:
 
//Variables de la classe en accès privé
int x, y, h, w;
int direction;
 
//Pour gérer la rotation du sprite (shuriken)
double rotation;
sf::Vector2f center;
 
//Sprite
sf::Texture Texture;
sf::Sprite Sprite;
 
 
/******************/
/* Constantes */
/******************/
 
const int RIGHT = 1;
const int LEFT = 2;
 
const int TILE_SIZE = 32;
const int SCREEN_WIDTH = 800;
 
 
};
#endif

 

   Comme vous pouvez le voir, pour gérer nos shurikens, les variables nécessaires ne sont pas très nombreuses. wink
   Il nous faut simplement:
- ses coordonnées x et y,
- ses dimensions w et h,
- son centre center et son angle de rotation pour pouvoir le faire tourner sur lui-même, comme un vrai shuriken qui se respecte ! laugh
- et enfin son Sprite et sa Texture.
 
   Le reste est très basique. On l'a déjà vu avec la classe Monster, notamment. wink
   Passons donc au code du fichier shurikens.cpp :
 

Fichier : Créer un nouveau fichier shurikens.cpp et y copier :

//Rabidja 3 - nouvelle version intégralement en SFML 2
//Copyright / Droits d'auteur : www.meruvia.fr - Jérémie F. Bellanger
 
#include "player.h"
#include "shurikens.h"
#include "map.h"
#include "monster.h"
 
 
using namespace std;
using namespace sf;
 
 
//Accesseurs
int Shurikens::getX(void) const { return x; }
int Shurikens::getY(void) const { return y; }
int Shurikens::getW(void) const { return w; }
int Shurikens::getH(void) const { return h; }
int Shurikens::getDirection(void) const { return direction; }
double Shurikens::getRotation(void) const { return rotation; }
sf::Vector2f Shurikens::getCenter(void) const { return center; }
 
 
 
//Constructeur
 
Shurikens::Shurikens()
{
//Chargement de la spritesheet de Rabidja
if (!Texture.loadFromFile("graphics/shuriken.png"))
{
// Erreur
cout << "Erreur durant le chargement du sprite du shuriken." << endl;
}
else
Sprite.setTexture(Texture);
 
//Initialisation des variables :
x = y = h = w = 0;
rotation = 0.0f;
center.x = center.y = 10;
direction = 0;
}
 
 
//Fonctions
void Shurikens::initialize(int Adirection, int Ax, int Ay)
{
//On initialise notre shuriken
 
//Rotation
rotation = 0;
 
//Centre du shuriken pour la rotation (le sprite fait 20 x 20 pixels)
center.x = 10;
center.y = 10;
 
/* Direction du shuriken
Les valeurs sont proportionnelles au perso - à adpater
manuellement selon les cas :) */
if (Adirection == RIGHT)
{
x = Ax + 35;
y = Ay + 20;
direction = RIGHT;
}
else
{
x = Ax - 5;
y = Ay + 20;
direction = LEFT;
}
}
 
 
int Shurikens::update(Player &player, Map &map)
{
/* On se déplace : vers la droite : */
if (direction == RIGHT)
{
x += 10;
}
/* - vers la gauche : */
else
{
x -= 10;
}
 
//On fait tourner le shuriken
rotation += 20;
if (rotation >= 360)
rotation = 0;
 
 
// S'il sort de l'écran, on renvoie la valeur 1 pour le supprimer, sinon 0
if (x < map.getStartX() || x > map.getStartX() + SCREEN_WIDTH)
return 1;
else
return 0;
}
 
 
void Shurikens::draw(Map &map, RenderWindow &window)
{
 
//On dessine
Sprite.setPosition(Vector2f(x - map.getStartX(), y - map.getStartY()));
Sprite.setOrigin(center);
Sprite.setRotation(rotation);
window.draw(Sprite);
 
}
 
 
 
int Shurikens::checkCollisions(Monster &monster)
{
 
//On teste pour voir s'il n'y a pas collision, si c'est le cas, on renvoie 0
if ((monster.getX() >= x + w)
|| (monster.getX() + monster.getW() <= x)
|| (monster.getY() >= y + h)
|| (monster.getY() + monster.getH() <= y)
)
return 0;
//Sinon, il y a collision et on renvoie 1.
else
return 1;
 
}
 
 
void Shurikens::copy(Shurikens &shuriken)
{
//Copie des variables nécessaires :
x = shuriken.getX();
y = shuriken.getY();
h = shuriken.getH();
w = shuriken.getW();
direction = shuriken.getDirection();
rotation = shuriken.getRotation();
center = shuriken.getCenter();
}
 
   Revenons un peu sur le contenu de notre fichier :
- on commence par quelques accesseurs classiques pour les variables de notre classe,
- on a ensuite notre constructeur, qui charge le sprite et initialise les variables.
- puis la fonction initialize() qui crée un shuriken, si c'est possible, et l'oriente en fonction de la direction de notre héros (ce qui est plus logique ! indecision),
- update() s'occupe, quant à elle, de gérer leurs déplacements suivant qu'ils ont été lancés à droite ou à gauche, de les faire tourner en augmentant leur angle de rotation, et vérifie s'ils sortent de l'écran (auquel cas, on les supprimera dans le main() wink).
draw() affiche simplement le shuriken, en prenant en compte sa rotation.
- checkCollisions() sert à tester les collisions entre le shuriken et un monstre. On s'en servira plus tard, en l'appelant depuis  le main().
- enfin copy() fera une copie de notre objet dans un autre, quand il sera détruit (comme pour les monstres wink).
   Comme vous pouvez le voir, on reste dans du classique. wink
   Bon, maintenant, que c'est fait, on va pouvoir mettre à jour notre main() :
 

Fichier : main.h : Modifier le code :

//Rabidja 3 - nouvelle version convertie en 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 "plateformes.h"
#include "menus.h"
#include "shurikens.h"
 
using namespace std;
using namespace sf;
 
// 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 plateformes à l'écran
const int PLATEFORMES_MAX = 50;
 
//Une enum pour la gestion du menu.
const enum { START, PAUSE };
 
//Nombre max de shurikens à l'écran
const int SHURIKEN_MAX = 6;

 

  
   Bon, l'en-tête ne bouge pas beaucoup :
- on rajoute simplement #include "shurikens.h",
- et on n'oublie pas la constante SHURIKEN_MAX.
 

Fichier : main.cpp : Modifier le code :

//Rabidja 3 - nouvelle version convertie en SFML 2
//Copyright / Droits d'auteur : www.meruvia.fr - Jérémie F. Bellanger
//Big Tuto C++/SFML 2.2 - Février 2015 - Mise à jour 1.2
 
#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),
"Rabidja 3.0 - Chapitre 19 : Shurikens - Big Tuto SFML2 - www.meruvia.fr");
 
//Limite les fps à 60 images / seconde
window.setFramerateLimit(60);
 
//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 plateformes gérables
Plateformes plateforme[PLATEFORMES_MAX];
//On instancie autant de classes que de shurikens gérables
Shurikens shuriken[SHURIKEN_MAX];
 
//On lance la musique en boucle
sounds.PlayMusic(true);
 
//On commence au premier niveau (vous pouvez aussi mettre 2 pour tester le 2ème niveau)
map.setLevel(1);
map.changeLevel();
 
//On initialise le player
player.initialize(map, true);
player.setVies(3);
player.setEtoiles(0);
 
//On commence par le menu start
menus.setOnMenu(true, START);
 
// Boucle infinie, principale, du jeu
while (window.isOpen())
{
/** GESTION DES INPUTS (CLAVIER, JOYSTICK) **/
input.gestionInputs(window);
 
//Si on n'est pas dans un menu
if (!menus.getOnMenu())
{
/** MISES A JOUR - UPDATES **/
//On met à jour le player : Rabidja
player.update(input, map, sounds, menus, shuriken);
 
//On met à jour les monstres un par un
for (int i = 0; i < map.getNombreMonstres(); i++)
{
if (monster[i].update(map, player, sounds) == 2)
{
//Si l'update du monstre renvoie 2, c'est qu'il doit mourir :
//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 plateformes une par une
for (int i = 0; i < map.getNombrePlateformes(); i++)
{
plateforme[i].update(player);
 
//On teste les collisions avec le joueur
plateforme[i].checkCollisions(player);
}
 
//On met à jour les shurikens un par un
for (int i = 0; i < player.getNombreShurikens(); i++)
{
if (shuriken[i].update(player, map) == 1)
{
//On supprime le shuriken s'il sort de l'écran
shuriken[i].copy(shuriken[player.getNombreShurikens() - 1]);
player.setNombreShurikens(player.getNombreShurikens() - 1);
}
else
{
//On teste les collisions avec tous les monstres
for (int j = 0; j < map.getNombreMonstres(); j++)
{
if (shuriken[i].checkCollisions(monster[j]) == 1)
{
//Si la fonction renvoie 2, c'est qu'il y a contact avec le monstre
//Celui-ci doit mourrir et il faut supprimer le shuriken.
 
//On copie à sa place le dernier monstre avant de retirer un monstre
monster[j].copy(monster[map.getNombreMonstres() - 1]);
map.setNombreMonstres(map.getNombreMonstres() - 1);
 
//On supprime le shuriken
shuriken[i].copy(shuriken[player.getNombreShurikens() - 1]);
player.setNombreShurikens(player.getNombreShurikens() - 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);
}
 
/** DESSIN - DRAW **/
//On dessine 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))
{
//On affiche le background
map.drawBackground(window);
 
// Affiche la map de tiles : layer 2 (couche du fond)
map.draw(2, window, monster, plateforme);
 
// Affiche la map de tiles : layer 1 (couche active : sol, etc.)
map.draw(1, window, monster, plateforme);
 
// 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 plateformes une par une
for (int i = 0; i < map.getNombrePlateformes(); i++)
{
plateforme[i].draw(map, window);
}
 
//On affiche les shurikens un par un
for (int i = 0; i < player.getNombreShurikens(); i++)
{
shuriken[i].draw(map, window);
}
 
// Affiche la map de tiles : layer 3 (couche en foreground / devant)
map.draw(3, window, monster, plateforme);
 
//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);
 
window.display();
}
 
// On quitte
return 0;
 
}
 
   C'est dans le main(), où ça change le plus, forcément :
- on instancie d'abord nos objets Shurikens (comme les monstres et les plateformes),
 
- Puis, dans la boucle principale :
- on ajoute l'update des shurikens, hors menus, avec détection des collisions avec les monstres, pour pouvoir les tuer (et supprimer le shuriken par la même occasion), si on a un contact ! surprise
- on ajoutedu le draw des shurikens (sinon, ce sera l'attaque des shurikens invisibles ! indecision),
- et enfin, on ajoute l'argument shuriken à l'appel de player.update(), pour ne pas avoir d'erreur à la compilation. wink
 
   Eh voilà ! On compile, et ça marche !!! Hourra ! wink
   Notre lapin ninja peut maintenant envoyer des volées de 6 shurikens dévastateurs ! cool
 
   Derniers réglages :
   Vous pouvez maintenant adapter le lancer des shurikens à vos besoins :
- en changeant l'image Shuriken_image,
- leur nombre en changeant la constante : SHURIKEN_MAX,
- leur vitesse en changeant la valeur 10 dans la fonction shuriken.update().
- leur position de départ en changeant les x et y relatifs à la position (et à la taille) de votre héros.
   

   @ bientôt pour le chapitre 20 ! angel

                                                    Jay 

 

 

 

This site uses cookies to enable you to log in. We do not store or sell any personal data. By continuing to use this website, you agree to their use. Thanks!