Créons un jeu de plateformes de A à Z !



Tutoriel présenté par : Jérémie F. Bellanger

Dernière mise à jour : 16 août 2011

Difficulté :  




14. Gérons les collisions monstres - joueur !


    Dans ce court chapitre, nous allons maintenant gérer les collisions entre le joueur et les monstres. Nous utiliserons d'abord un système à la Mario (avec bond sur la tête) parce que c'est sans doute le plus simple. 

    Je ne m'attarderai pas sur la théorie, car j'ai déjà écrit un autre tuto là-dessus et je vous y renvoie donc si vous ne l'avez pas déjà lu.

    Bon, il est temps d'y aller ! Notre lapin a envie d'en découdre !
   


Résultat à la fin de ce chapitre : les monstres peuvent tuer notre super lapin !


    Vu que nous avons déjà mis en place une structure pour gérer la mort des monstres et du joueur (si par inadvertance, ils tombaient dans un trou... ) avec un timer (en plus ! ), il ne nous reste plus qu'à créer une fonction qui va gérer les collisions et nous dire qui on doit tuer (logique).

    Voilà donc cette magnifique fonction collide, simple et rapide. Pour info, c'est celle qui est utilisée dans le jeu Aron's Journey in Dreamland.
Elle a donc largement fait ses preuves !


Nom du fichier : monster.c


   //Fonction de gestion des collisions
   int collide(GameObject *player, GameObject *monster)
 {
    //On teste pour voir s'il n'y a pas collision, si c'est le cas, on renvoie 0
    if ((player->x >= monster->x + monster->w)
    || (player->x + player->w <= monster->x)
    || (player->y >= monster->y + monster->h)
    || (player->y + player->h <= monster->y)
    )
        return 0;
    //Sinon, il y a collision. Si le joueur est au-dessus du monstre (avec une marge
    //de 10 pixels pour éviter les frustrations dues au pixel perfect), on renvoie 2.
    //On devra alors tuer le monstre et on fera rebondir le joueur.
    else if (player->y + player->h <= monster->y + 10)
    {
        player->dirY = -JUMP_HEIGHT;
        return 2;
    }
    //Sinon, on renvoie 1 et c'est le joueur qui meurt...
    else
        return 1;
}

         


    Son fonctionnement est simple : on teste si les deux sprites ne se touchent pas. Si c'est la cas, on renvoie 0.

    Sinon, c'est qu'ils se touchent. Mais là on rajoute un test pour savoir si le joueur est au-dessus du monstre. Si c'est le cas, il lui rebondit dessus, on renvoie 2, et on devra supprimer le monstre . Sinon, c'est notre lapin qui morfle et on renvoie 1 pour le tuer...

    Remarque : on aurait pu se débrouiller sans pointeur, mais si on veut tester plus tard les collisions avec d'autres GameObject que les monstres (pièges, boules de feu, etc...), ce sera plus pratique.

    Mettons maintenant à jour notre fonction monsterUpdate() :


Nom du fichier : monster.c


   void updateMonsters(void)
{

    int i;

    //On passe en boucle tous les monstres du tableau
    for ( i = 0; i < jeu.nombreMonstres; i++ )
    {
        //Même fonctionnement que pour le joueur
        if (monster[i].timerMort == 0)
        {

            monster[i].dirX = 0;
            monster[i].dirY += GRAVITY_SPEED;


            if (monster[i].dirY >= MAX_FALL_SPEED)
                monster[i].dirY = MAX_FALL_SPEED;

            //Le monstre va toujours à gauche
            monster[i].dirX -= 1;

            //On détecte les collisions avec la map comme pour le joueur
            mapCollision(&monster[i]);

            //On détecte les collisions avec le joueur
            //Si c'est égal à 1, on tue le joueur... Sniff...
            if (collide(&player, &monster[i]) == 1)
            {
                //On met le timer à 1 pour tuer le joueur intantanément
                player.timerMort = 1;
            }
            else if (collide(&player, &monster[i]) == 2)
            {
                //On met le timer à 1 pour tuer le monstre intantanément
                monster[i].timerMort = 1;
            }

          }

        //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)
            {
                monster[i] = monster[jeu.nombreMonstres-1];
                jeu.nombreMonstres--;
            }
        }

    }


}

         

    Rien de bien sorcier ici.

    On fait tous les tests dans la fonction monsterUpdate et pas playerUpdate car on s'inscrit dans la boucle qui passe en revue tous les monstres (pas besoin d'en rajouter une ailleurs ! ).

    Voilà, maitenant on met à jour le fichier d'en-tête :

Nom du fichier : monster.h


   #include "structs.h"

  extern GameObject monster[];
  extern Gestion jeu;
  extern GameObject player;

  /* Prototypes des fonctions utilisées */
  extern SDL_Surface *loadImage(char *name);
  extern void mapCollision(GameObject *entity);
  extern int collide(GameObject *player, GameObject *monster);

    Voilà, on peut maintenant compiler et lancer le programme.

    Cela marche ! Si le joueur rencontre un monstre, il meurt et s'il saute sur un monstre, le monstre meurt  .

    Maintenant, au bout d'un moment, il n'y aura plus de monstres... Comment faire ?  Et bien, il suffit simplement de recharger la map quand on meurt !

Nom du fichier : player.c


   void initializePlayer(void)
{

    /* Charge le sprite de notre héros */
    player.sprite = loadImage("graphics/walkright.png");

    //Indique l'état et la direction de notre héros
    player.direction = RIGHT;
    player.etat = IDLE;

    //Réinitialise le timer de l'animation et la frame
    player.frameNumber = 0;
    player.frameTimer = TIME_BETWEEN_2_FRAMES;

    /* Coordonnées de démarrage de notre héros */
    player.x = 0;
    player.y = 0;

    /* 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;
   
    //Nombre de monstres (à déplacer plus tard dans initialzeGame())
    jeu.nombreMonstres = 0;

    //On recharge la map
    loadMap("map/map1.txt");

}

         


    Sans oublier l'en-tête :

Nom du fichier : player.h


  #include "structs.h"

extern Gestion jeu;
extern GameObject player;
extern Input input;
extern Map map;

/* Prototypes des fonctions utilisées */
extern SDL_Surface *loadImage(char *name);
extern void centerScrollingOnPlayer(void);
extern void mapCollision(GameObject *entity);
extern void loadMap(char *name);
         

   Et le tour est joué !

    On commence maintenant à avoir quelque chose qui ressemble vraiment à un jeu de plateformes : on a une map, éditable qui s'affiche et qui scrolle, un joueur qui s'anime, qu'on peut contrôler et qui peut sauter et double sauter, des monstres qui bougent et la gestion des collisions s'effectue correctement entre tout ce petit monde ! Si elle est pas belle la vie ?!

    Bon, bien sûr, il reste encore pas mal de choses à affiner, mais ça va être à vous de le faire  ! (Faut bien que je vous fasse bosser un peu quand même !
)

    Le prochain chapitre sera donc un TP où vous allez essayer de rendre nos monstres un peu plus intelligents (pour éviter qu'ils ne tombent lamentablement dans le vide et fassent demi-tour s'ils sont coincés !). Vous allez donc créer votre première IA (intelligence artificielle) !


   


   





 

 

 

Connexion

CoalaWeb Traffic

Today26
Yesterday185
This week26
This month5196
Total1744403

29/04/24