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


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

Dernière mise à jour : 15 décembre 2013

Difficulté :  



12. TP : Ajouter 2 nouvelles animations et gérer le double saut


    Et maintenant, ça va être à vous de jouer ! Dans ce petit TP, vous allez devoir rajouter 2 nouvelles animations : une quand Aron le lapin-ninja est inactif et l'autre quand il saute.

    Votre deuxième mission, si vous l'acceptez, sera de rajouter à notre ninja sauteur la possibilité de faire un double saut (essentiel si on veut être un bon lapin-ninja, n'est-ce pas ! ).

    Allez, c'est parti !


Résultat à la fin de ce chapitre :
vous aurez rajouté 2 nouvelles anims + le double saut à notre lapin ninja 
!


    
A. Quelques pistes de travail...

    Allez, comme je suis gentil (si, si ! ), je vais vous aider un peu à démarrer, et aussi vous donner les sprites nécessaires pour ne pas que vous ayez besoin de les dessiner (merci, qui ? ) !

    1. Les animations :

    Bon, comme vous l'avez vu précédemment, le changement d'animations se situe au niveau de la fonction updatePlayer(). Il vous suffira donc de tester les états, la direction et de charger le bon spritesheet.

    Attention aussi à ne le recharger que quand c'est nécessaire et pas à chaque tour de boucle, sinon vous allez bouffer les ressources !

    Ah, un dernier petit conseil : n'hésitez pas à rajouter des defs, si vous n'en avez pas assez avec WALK, IDLE et JUMP ! (comme 
WALK_RIGHT, WALK_LEFT, etc...)

   Bon voilà maintenant les spritesheet. Enregistrez-les dans le dossier graphics sous leurs noms respectifs :


IdleLeft.png


IdleRight.png


JumpLeft.png


JumpRight.png


   2. Le double saut :

    Pour gérer le double saut, regardez comment le simple saut fonctionne : à l'aide de la variable onGround. Pourquoi ne pas utiliser une variable "jump", alors ? Elle vaudrait 1 si on peut faire un double saut et 0 sinon. Et elle se réinitialiserait quand on touche le sol !

    Allez, à vos marques, prêts, partez !
    Et n'allez pas lire la solution avant d'avoir essayé, hein ?


Chut !   On travaille ici !



B. La soluce !

    Attention, ne lisez les lignes qui suivent QUE si vous avez travaillé au moins un peu (quand même, hein ? ) !
    Bon, on va commencer par l'ajout des animations.

    1. Les animations :

    Donc, comme je vous avais mis sur la piste, ça se situe bien au niveau de la fonction updatePlayer().
    Cependant, pour parer à tous les problèmes, il valait mieux étoffer notre liste de defs. Je vous conseille donc de remplacer WALK, IDLE et JUMP par :

Nom du fichier : defs.h


    //Valeurs attribuées aux états/directions
   #define WALK_RIGHT 1
   #define WALK_LEFT 2
   #define IDLE 3
   #define JUMP_RIGHT 4
   #define JUMP_LEFT 5
   #define RIGHT 1
   #define LEFT 2

         

    Maintenant, rien de bien sorcier. Il suffit simplement de faire les bons tests au bon endroit et de procéder de façon logique. A chaque fois qu'il y a un input, on vérifie l'état de notre héros et si ce n'est pas le bon, on met à jour son état et son spritesheet.

    Bon, c'est vrai que parfois, ça peut être un peu galère, et vous avez pu vous embrouiller dans les anims. Mais bon, là il n'y en a que 3, imaginez les jeux de baston, qui ont un nombre incroyable d'anims à gérer !

    Enfin, la réponse s'offre maintenant à vos yeux ébahis ci-dessous. Comme d'habitude, j'ai signalé les changement en bleu.

Nom du fichier : player.c


 void updatePlayer(void)
{

   //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 (player.timerMort == 0)
  {

    //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 !
    player.dirX = 0;

    // La gravité fait toujours tomber le perso : on incrémente donc le vecteur Y
    player.dirY += GRAVITY_SPEED;

    //Mais on le limite pour ne pas que le joueur se mette à tomber trop vite quand même
    if (player.dirY >= MAX_FALL_SPEED)
    {
        player.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.left == 1)
    {
        player.dirX -= PLAYER_SPEED;
        player.direction = LEFT;

        if(player.etat != WALK_LEFT && player.onGround == 1)
        {
            player.etat = WALK_LEFT;
            player.sprite = loadImage("graphics/walkleft.png");
            player.frameNumber = 0;
        }
    }

    else if (input.right == 1)
    {
        player.dirX += PLAYER_SPEED;
        player.direction = RIGHT;

        if(player.etat != WALK_RIGHT && player.onGround == 1)
        {
            player.etat = WALK_RIGHT;
            player.sprite = loadImage("graphics/walkright.png");
            player.frameNumber = 0;
        }
    }

    //Si on n'appuie sur rien et qu'on est sur le sol, on charge l'animation marquant l'inactivité (Idle)
    else if(input.right == 0 && input.left == 0 && player.onGround == 1)
    {
        //On teste si le joueur n'était pas déjà inactif, pour ne pas recharger l'animation
        //à chaque tour de boucle

        if(player.etat != IDLE)
        {
            player.etat = IDLE;
            //On change l'animation selon la direction
            if(player.direction == LEFT)
            {
                player.sprite = loadImage("graphics/IdleLeft.png");
                player.frameNumber = 0;
            }
            else
            {
                player.sprite = loadImage("graphics/IdleRight.png");
                player.frameNumber = 0;
            }

        }

    }



    //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.jump == 1 && player.onGround == 1)
    {
        player.dirY = -JUMP_HEIGHT;
        player.onGround = 0;

    }

    //On gère l'anim du saut
    if(player.onGround == 0)
    {
        if(player.direction == RIGHT && player.etat != JUMP_RIGHT)
        {
            player.etat = JUMP_RIGHT;
            player.sprite = loadImage("graphics/JumpRight.png");
            player.frameNumber = 0;
        }
        else if(player.direction == LEFT && player.etat != JUMP_LEFT)
        {
            player.etat = JUMP_LEFT;
            player.sprite = loadImage("graphics/JumpLeft.png");
            player.frameNumber = 0;
        }

    }



    //On rajoute notre fonction de détection des collisions qui va mettre à jour les coordonnées
    //de notre super lapin, puis on centre le scrolling comme avant.
    mapCollision(&player);
    centerScrollingOnPlayer();

  }

    //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 (player.timerMort > 0)
    {
        player.timerMort--;

        if (player.timerMort == 0)
        {
            /* Si on est mort */
            initializePlayer();
        }
    }

}

 
    Maintenant n'oubliez pas de mettre à jour la fonction initializePlayer() car on a changé les defs !


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;

  }

   

     Eh voilà, la première partie du TP est résolue ! C'était si dur que ça ? 
    

   2. Le double saut :

    Pour le double saut, on va rajouter une nouvelle variable dans notre structure Hero qui va nous indiquer si le héros peut sauter ou non :

   
Nom du fichier : structs.h


  //Variable pour le double saut
    int jump;
 


    Voilà, il ne reste plus qu'à gérer ça dans la fonction updatePlayer(). Comme vous allez le voir, c'est (assez) facile !
    Il y a juste un ou deux petit(s) piège(s)...
   
   
Nom du fichier : player.c


  void updatePlayer(void)
{

   //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 (player.timerMort == 0)
  {

    //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 !
    player.dirX = 0;

    // La gravité fait toujours tomber le perso : on incrémente donc le vecteur Y
    player.dirY += GRAVITY_SPEED;

    //Mais on le limite pour ne pas que le joueur se mette à tomber trop vite quand même
    if (player.dirY >= MAX_FALL_SPEED)
    {
        player.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.left == 1)
    {
        player.dirX -= PLAYER_SPEED;
        player.direction = LEFT;

        if(player.etat != WALK_LEFT && player.onGround == 1)
        {
            player.etat = WALK_LEFT;
            player.sprite = loadImage("graphics/walkleft.png");
            player.frameNumber = 0;
        }
    }

    else if (input.right == 1)
    {
        player.dirX += PLAYER_SPEED;
        player.direction = RIGHT;

        if(player.etat != WALK_RIGHT && player.onGround == 1)
        {
            player.etat = WALK_RIGHT;
            player.sprite = loadImage("graphics/walkright.png");
            player.frameNumber = 0;
        }
    }

    //Si on n'appuie sur rien et qu'on est sur le sol, on charge l'animation marquant l'inactivité (Idle)
    else if(input.right == 0 && input.left == 0 && player.onGround == 1)
    {
        //On teste si le joueur n'était pas déjà inactif, pour ne pas recharger l'animation
        //à chaque tour de boucle

        if(player.etat != IDLE)
        {
            player.etat = IDLE;
            //On change l'animation selon la direction
            if(player.direction == LEFT)
            {
                player.sprite = loadImage("graphics/IdleLeft.png");
                player.frameNumber = 0;
            }
            else
            {
                player.sprite = loadImage("graphics/IdleRight.png");
                player.frameNumber = 0;
            }

        }

    }



    //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.jump == 1)
    {
        if(player.onGround == 1)
        {
            player.dirY = -JUMP_HEIGHT;
            player.onGround = 0;
            player.jump = 1;
        }
        /* Si on est en saut 1, on peut faire un deuxième bond et on remet jump1 à 0 */
        else if (player.jump == 1)
        {
            player.dirY = -JUMP_HEIGHT;
            player.jump = 0;
        }
        //On remet les input à 0 par sécurité
        input.jump = 0;
    }

    /* Réactive la possibilité de double saut si on tombe sans sauter */
    if (player.onGround == 1)
        player.jump = 1;


    //On gère l'anim du saut
    if(player.onGround == 0)
    {
        if(player.direction == RIGHT && player.etat != JUMP_RIGHT)
        {
            player.etat = JUMP_RIGHT;
            player.sprite = loadImage("graphics/JumpRight.png");
            player.frameNumber = 0;
        }
        else if(player.direction == LEFT && player.etat != JUMP_LEFT)
        {
            player.etat = JUMP_LEFT;
            player.sprite = loadImage("graphics/JumpLeft.png");
            player.frameNumber = 0;
        }

    }


    //On rajoute notre fonction de détection des collisions qui va mettre à jour les coordonnées
    //de notre super lapin, puis on centre le scrolling comme avant.
    mapCollision(&player);
    centerScrollingOnPlayer();

  }

    //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 (player.timerMort > 0)
    {
        player.timerMort--;

        if (player.timerMort == 0)
        {
            /* Si on est mort */
            initializePlayer();
        }
    }

}


    Et voilà, si vous compilez et lancez le programme, vous pourrez maintenant explorer toute la carte grâce au double saut, et profiter des magnifiques animations de notre lapin NINJA !

    Si vous voulez prolonger un peu le TP et continuer à bidouiller (si, si, y'a des courageux qui le feront !
), vous pouvez vous amuser à changer les réglages pour obtenir la maniabilité qui vous convient le mieux, rajouter des anims, et même changer tous les graphismes pour obtenir VOTRE jeu. Et n'oubliez pas de poster vos résultats dans le forum du site, ça nous fera toujours plaisir !

      Bon, maintenant que notre héros est fonctionnel, il va falloir rajouter quelques monstres, sinon il risque de s'ennuyer !    

   


   




 

 

 

Connexion

CoalaWeb Traffic

Today34
Yesterday185
This week34
This month5204
Total1744411

29/04/24