Big Tuto SFML 2 : Rabidja v. 3.0

Chapitre 15 : Ajoutons des power-ups et des tiles spéciales !

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

      Prologue

   Bon, après quelques chapitres bien longs et bien chargés, on va y aller plus doucement ! cheeky

   Et on va commencer en rajoutant des power-ups (vous vous rappelez, les vies et les étoiles que vous avez mises partout dans vos niveaux ?! cheeky) Mais comme ce sera très vite fait, on va en profiter pour rajouter également la gestion de 2 tiles spéciales : les pics et le ressort. Et comme vous allez le voir, on va faire ça dans la foulée, sans se fouler ! laugh

   Allez, on est reparti !

 

      Le code

   On va commencer par aller dans notre fichier player.cpp pour rajouter une nouvelle fonction qui gèrera la prise de power-ups par notre lapin-ninja ! wink

   Cette fonction est simple, on lui envoie le numéro de l'objet récupéré par rapport à la tile touchée par notre lapin. Pour cela, on fait un simple calcul, qui apparaît dans la fonction suivante :

map.tile[y2][x2] - TILE_POWER_UP_DEBUT + 1

   C'est-à dire qu'on enlève la constante TILE_POWER_UP_DEBUT à la tile sélectionnée, tout en rajoutant 1, pour que la première tile power-up soit la numéro 1, la seconde la 2, etc. Vous avez pigé le concept ? wink Maintenant, vous pourrez rajouter vos propres power-ups pour personnaliser votre jeu ! cheeky

- Si c'est 1, on ajoute une étoile, puis on teste si on en a 100. Si c'est le cas, on remet le compteur à 0 et on ajoute une vie (qu'on limite à 99 max).

- Si c'est 2, on rajoute un coeur si on en a moins de 3.

- Si c'est 3, on rajoute une vie si on en a moins de 99.    

   Dans tous les cas, on joue le sound Fx STARangel

 

Fichier : player.cpp : Rajouter la fonction :

void Player::getItem(int itemNumber, Sounds &sounds)
{
switch (itemNumber)
{
//Gestion des étoiles
case 1:
//On incrémente le compteur Etoile
etoiles++;
 
//On joue le sound Fx
sounds.PlaySoundFx(sounds.STAR);
 
//On teste s'il y a 100 étoiles : on remet le compteur à 0 et on rajoute une vie ;)
if (etoiles >= 100)
{
etoiles = 0;
 
//On incrémente le nombre de vies (max 99)
if (vies < 99)
vies++;
}
break;
 
 
//Gestion des coeurs
case 2:
//On incrémente le compteur Etoile
if (life < 3)
life++;
 
//On joue le sound Fx
sounds.PlaySoundFx(sounds.STAR);
break;
 
 
//Gestion des vies
case 3:
//On incrémente le nombre de vies (max 99)
if (vies < 99)
vies++;
 
//On joue le sound Fx
sounds.PlaySoundFx(sounds.STAR);
break;
 
default:
break;
}
 
}

   Et n'oublions pas son prototype :

Fichier : player.h : Rajouter le prototype :

void getItem(int itemNumber, Sounds &sounds);

    Voilà, maintenant, il va nous falloir détecter les collisions avec les tiles Power-ups pour appeler getItem(). Pour cela, tout va se passer dans la fonction mapCollision() du fichier player.cpp. Vous remarquerez aussi que l'argument sounds, passé en référence au chapitre précédent, va maintenant nous servir à jouer les sons des pics et du ressort. wink

   Cette fonction est relativement complexe (encore plus maintenant cheeky) mais assez redondante et vous l'avez déjà vue dans les chapitres précédents. wink Je vous laisse lire ce qui a changé :

 

Fichier : player.cpp : Remplacer la fonction précédente par :

void Player::mapCollision(Map &map, Sounds &sounds)
{
 
int i, x1, x2, y1, y2;
 
// Récup des infos pour la gestion des pentes (par Stephantasy)
dirXmem = dirX;
wasOnGround = onGround;
dirYmem = dirY;
posXmem = x;
posYmem = y;
 
/* D'abord, on considère le joueur en l'air jusqu'à temps
d'être sûr qu'il touche le sol */
onGround = 0;
 
/* Ensuite, on va tester les mouvements horizontaux en premier
(axe des X). On va se servir de i comme compteur pour notre boucle.
En fait, on va découper notre sprite en blocs de tiles pour voir
quelles tiles il est susceptible de recouvrir.
On va donc commencer en donnant la valeur de Tile_Size à i pour qu'il
teste la tile où se trouve le x du joueur mais aussi la suivante SAUF
dans le cas où notre sprite serait inférieur à la taille d'une tile.
Dans ce cas, on lui donnera la vraie valeur de la taille du sprite
Et on testera ensuite 2 fois la même tile. Mais comme ça notre code
sera opérationnel quelle que soit la taille de nos sprites ! */
 
if (h > TILE_SIZE)
i = TILE_SIZE;
else
i = h;
 
 
//On lance alors une boucle for infinie car on l'interrompra selon
//les résultats de nos calculs
for (;;)
{
//On va calculer ici les coins de notre sprite à gauche et à
//droite pour voir quelle tile ils touchent.
x1 = (x + dirX) / TILE_SIZE;
x2 = (x + dirX + w - 1) / TILE_SIZE;
 
//Même chose avec y, sauf qu'on va descendre au fur et à mesure
//pour tester toute la hauteur de notre sprite, grâce à notre
//fameuse variable i.
y1 = (y) / TILE_SIZE;
y2 = (y + i - 1) / TILE_SIZE;
 
//De là, on va tester les mouvements initiés dans updatePlayer
//grâce aux vecteurs dirX et dirY, tout en testant avant qu'on
//se situe bien dans les limites de l'écran.
if (x1 >= 0 && x2 < MAX_MAP_X && y1 >= 0 && y2 < MAX_MAP_Y)
{
//Si on a un mouvement à droite
if (dirX > 0)
{
 
//Test des tiles Power-up
if (map.getTile(y1, x2) >= TILE_POWER_UP_DEBUT
&& map.getTile(y1, x2) <= TILE_POWER_UP_FIN)
{
//On appelle la fonction getItem()
getItem(map.getTile(y1, x2) - TILE_POWER_UP_DEBUT + 1, sounds);
 
//On remplace la tile power-up par une tile transparente
map.setTile(y1, x2, 0);
}
 
else if (map.getTile(y2, x2) >= TILE_POWER_UP_DEBUT
&& map.getTile(y2, x2) <= TILE_POWER_UP_FIN)
{
//On appelle la fonction getItem()
getItem(map.getTile(y2, x2) - TILE_POWER_UP_DEBUT + 1, sounds);
 
//On remplace la tile power-up par une tile transparente
map.setTile(y2, x2, 0);
}
 
//On vérifie si les tiles recouvertes sont solides
if (map.getTile(y1, x2) > BLANK_TILE || map.getTile(y2, x2) > BLANK_TILE)
{
// Si c'est le cas, on place le joueur aussi près que possible
// de ces tiles, en mettant à jour ses coordonnées. Enfin, on
//réinitialise son vecteur déplacement (dirX).
 
x = x2 * TILE_SIZE;
x -= w + 1;
dirX = 0;
 
}
}
 
//Même chose à gauche
else if (dirX < 0)
{
 
//Test des tiles Power-up : Etoile et vie
if (map.getTile(y1, x1) >= TILE_POWER_UP_DEBUT
&& map.getTile(y1, x1) <= TILE_POWER_UP_FIN)
{
//On appelle la fonction getItem()
getItem(map.getTile(y1, x1) - TILE_POWER_UP_DEBUT + 1, sounds);
 
//On remplace la tile power-up par une tile transparente
map.setTile(y1, x1, 0);
}
 
else if (map.getTile(y2, x1) >= TILE_POWER_UP_DEBUT
&& map.getTile(y2, x1) <= TILE_POWER_UP_FIN)
{
//On appelle la fonction getItem()
getItem(map.getTile(y2, x1) - TILE_POWER_UP_DEBUT + 1, sounds);
 
//On remplace la tile power-up par une tile transparente
map.setTile(y2, x1, 0);
}
 
if (map.getTile(y1, x1) > BLANK_TILE || map.getTile(y2, x1) > BLANK_TILE)
{
x = (x1 + 1) * TILE_SIZE;
dirX = 0;
}
 
}
 
}
 
//On sort de la boucle si on a testé toutes les tiles le long de la hauteur du sprite.
if (i == h)
{
break;
}
 
//Sinon, on teste les tiles supérieures en se limitant à la heuteur du sprite.
i += TILE_SIZE;
 
if (i > h)
{
i = h;
}
}
 
 
//On recommence la même chose avec le mouvement vertical (axe des Y)
if (w > TILE_SIZE)
i = TILE_SIZE;
else
i = w;
 
 
for (;;)
{
x1 = (x) / TILE_SIZE;
x2 = (x + i) / TILE_SIZE;
 
y1 = (y + dirY) / TILE_SIZE;
y2 = (y + dirY + h) / TILE_SIZE;
 
if (x1 >= 0 && x2 < MAX_MAP_X && y1 >= 0 && y2 < MAX_MAP_Y)
{
if (dirY > 0)
{
// Déplacement en bas
//Test des tiles Power-up)
if (map.getTile(y2, x1) >= TILE_POWER_UP_DEBUT
&& map.getTile(y2, x1) <= TILE_POWER_UP_FIN)
{
//On appelle la fonction getItem()
getItem(map.getTile(y2, x1) - TILE_POWER_UP_DEBUT + 1, sounds);
 
//On remplace la tile power-up par une tile transparente
map.setTile(y2, x1, 0);
}
else if (map.getTile(y2, x2) >= TILE_POWER_UP_DEBUT
&& map.getTile(y2, x2) <= TILE_POWER_UP_FIN)
{
//On appelle la fonction getItem()
getItem(map.getTile(y2, x2) - TILE_POWER_UP_DEBUT + 1, sounds);
 
//On remplace la tile power-up par une tile transparente
map.setTile(y2, x2, 0);
}
 
/* Gestion des pics */
if ((map.getTile(y2, x1) == TILE_PIKES) || (map.getTile(y2, x2) == TILE_PIKES))
{
 
//On joue le sound Fx
sounds.PlaySoundFx(sounds.DESTROY);
 
//On fait sauter le joueur
dirY = -JUMP_HEIGHT;
 
if (life > 1)
{
//Si le timer d'invincibilité est à 0
//on perd un coeur
if (invincibleTimer == 0)
{
life--;
invincibleTimer = 80;
}
}
else
{
//On met le timer à 1 pour tuer le joueur intantanément
timerMort = 1;
//On joue le sound Fx
sounds.PlaySoundFx(sounds.DESTROY);
}
}
 
/* Gestion du ressort */
else if ((map.getTile(y2, x1) == TILE_RESSORT) || (map.getTile(y2, x2) == TILE_RESSORT))
{
dirY = -20;
//On indique au jeu qu'il a atterri pour réinitialiser le double saut
onGround = 1;
//On joue le sound Fx
sounds.PlaySoundFx(sounds.BUMPER);
}
 
/** !! Attention à ne pas oublier le else avant le prochain if, sinon vous resterez
coincés sur les pics et les ressorts !! **/
 
 
//Gestion des plateformes traversables : elles se situent juste avant
//les tiles bloquantes dans notre tileset (dont la valeur butoire est
//BLANK_TILE). Il suffit donc d'utiliser le numéro de la première tile
//traversable au lieu de BLANK_TILE pour bloquer le joueur,
//seulement quand il tombe dessus (sinon, il passe au-travers
//et le test n'est donc pas effectué dans les autres directions
else if (map.getTile(y2, x1) > TILE_TRAVERSABLE || map.getTile(y2, x2) > TILE_TRAVERSABLE)
{
//Si la tile est une plateforme ou une tile solide, on y colle le joueur et
//on le déclare sur le sol (onGround).
y = y2 * TILE_SIZE;
y -= h;
dirY = 0;
onGround = 1;
}
 
}
 
else if (dirY < 0)
{
//Test des tiles Power-up
if (map.getTile(y1, x1) >= TILE_POWER_UP_DEBUT
&& map.getTile(y1, x1) <= TILE_POWER_UP_FIN)
{
//On appelle la fonction getItem()
getItem(map.getTile(y1, x1) - TILE_POWER_UP_DEBUT + 1, sounds);
 
//On remplace la tile power-up par une tile transparente
map.setTile(y1, x1, 0);
}
 
if (map.getTile(y1, x2) >= TILE_POWER_UP_DEBUT
&& map.getTile(y1, x2) <= TILE_POWER_UP_FIN)
{
//On appelle la fonction getItem()
getItem(map.getTile(y1, x2) - TILE_POWER_UP_DEBUT + 1, sounds);
 
//On remplace la tile power-up par une tile transparente
map.setTile(y1, x2, 0);
}
 
// Déplacement vers le haut
if (map.getTile(y1, x1) > BLANK_TILE || map.getTile(y1, x2) > BLANK_TILE)
{
y = (y1 + 1) * TILE_SIZE;
dirY = 0;
}
 
}
}
 
//On teste la largeur du sprite (même technique que pour la hauteur précédemment)
if (i == w)
{
break;
}
 
i += TILE_SIZE;
 
if (i > w)
{
i = w;
}
}
 
// Contrôle des pentes
checkSlope(map);
 
/* Maintenant, on applique les vecteurs de mouvement si le sprite n'est pas bloqué */
x += dirX;
y += dirY;
 
//Et on contraint son déplacement aux limites de l'écran.
if (x < 0)
{
x = 0;
}
 
else if (x + w >= map.getMaxX())
{
//Si on touche le bord droit de l'écran, on annule
//et on limite le déplacement du joueur
x = map.getMaxX() - w - 1;
}
 
//Maintenant, s'il sort de l'écran par le bas (chute dans un trou sans fond), on lance le timer
//qui gère sa mort et sa réinitialisation (plus tard on gèrera aussi les vies).
if (y > map.getMaxY())
{
timerMort = 60;
}
}

   Vous voyez que pour chaque direction que peut prendre notre héros, on va tester les collisions avec les tiles Power-ups. C'est important, parce que sinon, il pourrait passer au-travers en allant dans certaines directions ! angry

   On va donc tester, pour chaque tile que recouvre notre sprite (2 x 2 tiles avec la taille de notre lapin wink), s'il entre ou pas en collision avec les tiles power-ups définies en constantes quelques chapitres auparavant. Si c'est le cas, on va alors appeler getItem() pour donner l'item correspondant au joueur et on va effacer la tile en la mettant à 0 (tile vide).

   Eh oui, c'est aussi simple que ça ! cheeky

 

   Vous noterez que tant qu'à modifier cette grosse fonction, j'ai aussi rajouté la gestion des pics et du ressort, quand le joueur se dirige vers le bas. En effet, seule cette direction nous importe ici, car on ne veut pas se faire piquer ou rebondir en venant de la gauche, de la droite ou du dessous. cheeky C'est toutefois envisageable de rajouter des tiles correspondantes pour créer des bumpers, par exemple, dans votre jeu. wink

   Dans le cas du ressort, on fait simplement sauter le joueur (plus haut que d'ordinaire wink) et dans le cas des pics, on blesse ou tue le joueur comme on l'a fait précédemment avec les monstres. angel

   Vous noterez aussi qu'on joue le sound Fx correspondant (tant qu'à faire ! cheeky).

 

   Et voilà, c'est tout !angel

   Quoi, c'est déjà fini ?! surprise

   Eh oui, avouez que c'était plus simple que vous ne le pensiez ! indecision

   Maintenant, on compile et on lance le programme ! wink   

   Et hourra, on peut maintenant prendre les power-ups, se faire piquer le c** par les pics et rebondir comme un fou ! angel

   D'ailleurs, si vous voulez tester tout ça avec les niveaux par défaut, je vous conseille de passer au niveau 2 (en changeant la valeur dans le main()) ! Amusez-vous bien ! angel

   

 

   @ bientôt pour le chapitre 16 ! 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!