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



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

Dernière mise à jour : 11 mai 2014

Difficulté :  




Chapitre 23

Ajouter des menus start et pause !


    Comment faire pour ajouter des menus ? Rendu là où vous en êtes, c'est certainement la question que vous vous posez !

    Eh bien, nous allons tenter d'y répondre dans ce chapitre en créant un menu Start, auquel on reviendra quand notre lapin décèdera ! Et si on a le temps, pourquoi pas un petit menu pause ? C'est toujours bien pratique, non ?


Résultat à la fin de ce chapitre : nos premiers menus !


    Pour pouvoir rajouter des menus à notre jeu, il faut en fait que celui-ci gère plusieurs états. Le premier état, celui que nous avons pour l'instant est l'état onGame, ou en jeu. Ce qui veut dire que le programme exécute le jeu normalement. Nous allons lui rajouter un deuxième état : onMenu, soit dans un menu et nous enregistrerons le type de menu dans une autre variable grâce à une énum.

    Ensuite, il suffira simplement de modifier un peu notre main. Eh oui, c'est pas plus difficile que ça ! 

   

    A. Le menu Start

    Commençons par ajouter un menu Start !

    Pour ça, nous allons rajouter 3 variables à notre structure gestion : onMenu qui nous dira si on est dans l'état menu (1) ou dans l'état jeu (0), menuType qui nous dira dans quel type de menu on est et choice qui permettra de savoir sur quelle option est notre curseur.


Nom du fichier : structs.h


 typedef struct Gestion
{

    SDL_Surface *screen;
    int nombreMonstres;
    int level;

    //HUD
    SDL_Surface *HUD_vie, *HUD_etoiles;
    int vies, etoiles;

    //Sons
    Mix_Music  *musique;

    //Sounds Fx
    Mix_Chunk  *bumper_sound, *destroy_sound, *jump_sound, *star_sound;
   
    //Gestion des menus
    int onMenu, menuType, choice;


} Gestion;

       

   
    Pour menuType, on va avoir besoin d'une enum, donc on la rajoute dans les defs :


Nom du fichier : defs.h


  enum
 {
      START,
      PAUSE
  };

 


   On va ensuite créer les fonctions qui gèreront notre menu Start. On a besoin de 2 fonctions : updateStartMenu() pour gérer le menu et drawStartMenu() pour l'afficher. Pour éviter de mélanger les menus avec le reste du jeu, on va créer 2 nouveaux fichiers : menu.c et menu.h.

    Commençons par
updateStartMenu(), qu'est-ce qu'on va y mettre ?

    Notre menu Start va indiquer le titre de notre jeu, son éditeur, et deux options : Start et Quit (oui, je sais, c'est super simpliste mais vous ferez des super menus compliqués plus tard, quand vous aurez compris les bases  !).

    La fonction
updateStartMenu() va donc lire l'état des inputs et permettre de déplacer le curseur de Start à Quit et vice versa :

Nom du fichier menu.c


   #include "menu.h"

  void updateStartMenu(void)
{
    //Si on appuie sur BAS
    if(input.down == 1)
    {
        //Si choice = O (il est sur start), on le met à 1 (quit)
        if(jeu.choice == 0)
            jeu.choice++;

        input.down = 0;
    }

    //Si on appuie sur HAUT
    if(input.up == 1)
    {
        //Si choice = 1 (il est sur Quit), on le met à 0 (Start)
        if(jeu.choice == 1)
            jeu.choice--;

        input.up = 0;
    }

    //Si on appuie sur Enter et qu'on est sur Start, on recharge le jeu et on quitte l'état menu
    if(input.enter)
    {
         if(jeu.choice == 0)
        {
            jeu.level = 1;
            initializePlayer();
            changeLevel();
            /* On réinitialise les variables du jeu */
            jeu.vies = 3;
            jeu.etoiles = 0;
            jeu.onMenu = 0;
        }
        //Sinon, on quitte le jeu
        else if(jeu.choice == 1)
        {
            exit(0);
        }
         input.enter = 0;
    }
  

}

         

    Voilà qui est très basique mais aussi très simple ! 

    Passons maintenant à la fonction
drawStartMenu() qui va se charger de l'affichage :


Nom du fichier menu.c


void drawStartMenu(void)
{

    //On crée une varuiable qui contiendra notre texte
    char text[200];

    //Le titre
    sprintf(text, "La vengeance du lapin ninja");
    drawString(text, 110, 50, font);

    //Si l'option n'est pas en surbrillance, on l'affiche normalement, sinon on rajoute ">"
    if(jeu.choice != 0)
    {
        sprintf(text, "START");
        drawString(text, 273, 200, font);
    }
    if(jeu.choice != 1)
    {
        sprintf(text, "QUIT");
        drawString(text, 273, 280, font);
    }

     //Si l'option est en surbrillance, on rajoute ">"
    if(jeu.choice == 0)
    {
        sprintf(text, "> START");
        drawString(text, 250, 200, font);
    }
    else if(jeu.choice == 1)
    {
        sprintf(text, "> QUIT");
        drawString(text, 250, 280, font);
    }

    //Le nom du studio
    sprintf(text, "Meruvia Game Studio, 2011");
    drawString(text, 120, 420, font);


}

         


    Il ne reste maintenant plus qu'à relier tout ça au reste du jeu !
    Pour ça, on va dans le main d'abord :



Nom du fichier main.c


  int main(int argc, char *argv[])
{
    unsigned int frameLimit = SDL_GetTicks() + 16;
    int go;

       /* Initialisation de la SDL */
    init("Aron");

    /* On initialise le joueur */
    initializePlayer();

    /* Chargement des ressources (graphismes, sons) */
    loadGame();

    /* Appelle la fonction cleanup à la fin du programme */
    atexit(cleanup);

    go = 1;


    /* Boucle infinie, principale, du jeu */

    while (go == 1)
    {
        /* On prend en compte les input (clavier, joystick... */
        getInput();

        //Si on n'est pas dans un menu
        if(jeu.onMenu == 0)
        {
            /* On met à jour le jeu */
            updatePlayer();
            updateMonsters();
        }
        else
        {
            if(jeu.menuType == START)
                updateStartMenu();
        }
       

        //Si on n'est pas dans un menu
        if(jeu.onMenu == 0)
        {
            /* On affiche tout */
            draw();
        }
        else
        {
            if(jeu.menuType == START)
            {
                drawImage(map.background, 0, 0);
                drawStartMenu();
                SDL_Flip(jeu.screen);
                SDL_Delay(1);
            }
               
        }
       

        /* Gestion des 60 fps (1000ms/60 = 16.6 -> 16 */
        delay(frameLimit);
        frameLimit = SDL_GetTicks() + 16;
    }

    /* Exit */
    exit(0);
}

         


    Ensuite, on met à jour quelques en-tête (sans oublier le #include "menu.h" en haut du fichier menu.c) :


Nom du fichier : menu.h


  #include "structs.h"

  /* Structures globales */
  extern Gestion jeu;
  extern TTF_Font *font;
  extern Input input;

  extern void drawString(char *text, int x, int y, TTF_Font *font);

  extern void changeLevel(void);
  extern void initializePlayer(void);

 


    Et :

Nom du fichier : main.h


  #include "structs.h"

  /* Prototypes des fonctions utilisées */

  extern void init(char *);
  extern void cleanup(void);
  extern void getInput(void);
  extern void draw(void);
  extern void loadGame(void);
  extern void delay(unsigned int frameLimit);
  extern void updatePlayer(void);
  extern void initializePlayer(void);
  extern void updateMonsters(void);
  extern void drawStartMenu(void);
  extern void updateStartMenu(void);
  extern void drawImage(SDL_Surface *image, int x, int y);



  /* Déclaration des structures globales utilisées par le jeu */

  Input input;
  Gestion jeu;
  Map map;
  GameObject player;
  GameObject monster[MONSTRES_MAX];
  /* Déclaration de notre police de caractères */
  TTF_Font *font;

         

    Plus qu'à mettre à jour notre fonction loadGame() pour que le jeu commence par le menu Start (logique ! ) :


Nom du fichier : init.c


 void loadGame(void)
{

    /* Charge l'image du fond et le tileset */
    map.background = loadImage("graphics/background.png");

    //On commence au premier niveau
    jeu.level = 1;
    changeLevel();

    //On initialise le timer
     map.mapTimer = TIME_BETWEEN_2_FRAMES * 3;
     map.tileSetNumber = 0;

    /* On initialise les variables du jeu */
    jeu.vies = 3;
    jeu.etoiles = 0;

    /* On charge le HUD */
    jeu.HUD_vie = loadImage("graphics/life.png");
    jeu.HUD_etoiles = loadImage("graphics/stars.png");

    //On charge la musique
    loadSong("music/RabidjaGo.mp3");

    /* On charge les sounds Fx */
    loadSound();
   
    //On commence par le menu start
    jeu.onMenu = 1;
    jeu.menuType = START;

}

         

    
    On compile, et ça marche !
    On commence bien par notre menu Start (que je vous laisse améliorer à votre sauce), et nos deux options sont opérationnelles !

    Maintenant, quand on meurt, on aimerait bien retourner à ce menu !
    Rien de plus simple ! Juste 2 lignes à rajouter tout en bas de notre fonction updatePlayer() !



Nom du fichier : player.c


    void updatePlayer(void) : tout en bas :


      //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 */
            jeu.vies--;
            if(jeu.vies < 0)
            {
                //On retourne au menu start
                jeu.onMenu = 1;
                jeu.menuType = START;
            }
            initializePlayer();
            changeLevel();
        }
    }

}

         

    Et voilà, si on perd toutes nos vies, on revient maintenant à l'écran-titre !
    Cela se passait souvent comme ça dans les vieux jeux ! Maintenant, rien ne vous empêche de rajouter un écran Game Over avec une option : Continue !






    B. Ajout d'un menu Pause

  
  On va maintenant rajouter un menu Pause. Vu que l'essentiel est déjà en place, ça ne devrait pas être trop dur.
    On va donc créer un menu tout simple qui va stopper l'action quand on appuiera sur la touche ENTER et afficher PAUSE, tout simplement.

    On crée donc 2 nouvelles fonctions pour s'occuper de ça dans notre fichier menu.c : updatePauseMenu() et
drawPauseMenu() :


Nom du fichier : menu.c


 void updatePauseMenu(void)
{
   
    //Si on appuie sur Enter on quitte l'état menu
    if(input.enter)
    {
        jeu.onMenu = 0;
        input.enter = 0;
    }

}


void drawPauseMenu(void)
{
     char text[200];  

    //On écrit PAUSE
    sprintf(text, "** PAUSE **");
    drawString(text, 240, 240, font);

}

         

    Comme vous pouvez le voir, c'est vraiment très basique. Mais dans un premier temps, ça suffit .
    Plus qu'à le gérer dans le main (en affichant quand même le jeu en-dessous, c'est plus joli) :



Nom du fichier : main.c


 int main(int argc, char *argv[])
{
    unsigned int frameLimit = SDL_GetTicks() + 16;
    int go;
    int i;


       /* Initialisation de la SDL */
    init("Aron");

    /* On initialise le joueur */
    initializePlayer();

    /* Chargement des ressources (graphismes, sons) */
    loadGame();

    /* Appelle la fonction cleanup à la fin du programme */
    atexit(cleanup);

    go = 1;


    /* Boucle infinie, principale, du jeu */

    while (go == 1)
    {
        /* On prend en compte les input (clavier, joystick... */
        getInput();

        //Si on n'est pas dans un menu
        if(jeu.onMenu == 0)
        {
            /* On met à jour le jeu */
            updatePlayer();
            updateMonsters();
        }
        else
        {
            if(jeu.menuType == START)
                updateStartMenu();
            else if(jeu.menuType == PAUSE)
                updatePauseMenu();
        }


        //Si on n'est pas dans un menu
        if(jeu.onMenu == 0)
        {
            /* On affiche tout */
            draw();
        }
        else
        {
            if(jeu.menuType == START)
            {
                drawImage(map.background, 0, 0);
                drawStartMenu();
                SDL_Flip(jeu.screen);
                SDL_Delay(1);
            }
            else if(jeu.menuType == PAUSE)
            {
                drawImage(map.background, 0, 0);
                drawMap();
                drawAnimatedEntity(&player);
                for(i = 0 ; i < jeu.nombreMonstres ; i++)
                {
                    drawAnimatedEntity(&monster[i]);
                }
                drawHud();
                drawPauseMenu();
                SDL_Flip(jeu.screen);
                SDL_Delay(1);

            }

        }


        /* Gestion des 60 fps (1000ms/60 = 16.6 -> 16 */
        delay(frameLimit);
        frameLimit = SDL_GetTicks() + 16;
    }

    /* Exit */
    exit(0);
}

         

    Puis on met à jour notre en-tête en rajoutant ces deux lignes :


Nom du fichier : main.h


  extern void drawPauseMenu(void);
  extern void updatePauseMenu(void);

  extern void drawMap(void);
  extern void drawAnimatedEntity(GameObject *entity);
  extern void drawHud(void);

         

    Plus qu'à mettre le jeu en pause quand on appuie sur Enter dans la fonction updatePlayer();
  


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;
            changeAnimation(&player, "graphics/walkleft.png");
        }
    }

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

        if(player.etat != WALK_RIGHT && player.onGround == 1)
        {
            player.etat = WALK_RIGHT;
            changeAnimation(&player, "graphics/walkright.png");
        }
    }

    //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)
            {
                changeAnimation(&player, "graphics/IdleLeft.png");
            }
            else
            {
                changeAnimation(&player, "graphics/IdleRight.png");
            }

        }

    }


    //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;
            playSoundFx(JUMP);
        }
        /* 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;
            playSoundFx(JUMP);
        }
        input.jump = 0;
    }
   
    if(input.enter == 1)
    {
        //On met le jeu en pause
        jeu.onMenu = 1;
        jeu.menuType = PAUSE;
        input.enter = 0;
    }


   Etc...

         


   Voilà, si vous compilez maintenant, nos deux menus fonctionnent ! Alors certes le main peut être un peu allégé à l'aide de fonctions draw() différentes suivant ce que l'on veut afficher, mais je vous laisse le faire, c'est pas bien compliqué !

    Et libre à vous de rajouter tous les menus que vous voudrez !

   
    Bon, voilà qui cloture officiellement ce Big tuto. Normalement, vous avez appris là l'essentiel de ce dont vous aurez besoin pour créer le jeu de plateformes de vos rêves. Mais bien sûr, d'autres tutos sont en préparation (et peut-être quelque chose du côté du C# avec XNA ) !

    Et si vous avez des questions, les forums sont là pour ça !

    A bientôt sur Meruvia ! Et faites passer votre passion pour la prog' et les jeux vidéos !!

 
   


   




 

 

 

Connexion

CoalaWeb Traffic

Today74
Yesterday238
This week997
This month475
Total1745374

3/05/24