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




Tutoriel présenté par : Jérémie F. Bellanger
Dernière mise à jour : 20 novembre 2013
Difficulté :  

 


   3. Le moteur du jeu : ouvrir une fenêtre

 

    Dans ce chapitre, nous allons enfin entrer dans le vif du sujet. Eh oui ! Que d'efforts jusqu'à présent, pour rien ! Pour rien ? Non, pas du tout puisque vous avez maintenant les bases qui vous serviront à créer le jeu de vos rêves. Et d'ici peu de temps, vous pourrez déjà faire tourner votre première application. Au début, ce sera modeste, certes, mais vous en serez fier, et au fur et à mesure, vous complexifierez votre programme pour le transformer en killer app'  !

    Alors, n'oubliez pas, le tout est d'avancer tranquillement, étape par étape et petit à petit votre jeu se construira. Rien ne sert de viser trop haut, si ce n'est à ne redescendre que plus vite. Ce tutoriel est là pour vous, laissez vous guider ! Alors, on y va !



A. Ouvrir une fenêtre en SDL


     La première étape va consister à initialiser la SDL et à ouvrir une fenêtre qui servira ensuite à afficher notre jeu. Si vous avez bien lu, les tutoriels que je vous ai recommandés, vous devriez déjà savoir faire. Cependant, je vous invite à survoler cette partie, car nous allons mettre en place la première ossature de notre jeu.

 

Configuration de Code::Blocks en mode GUI :

 

    Par défaut, Code::Blocks ouvre les programmes en mode console. Or nous voulons que notre jeu apparaisse dans sa jolie fenêtre sans console derrière. Nous allons donc configurer Code::Blocks.

 

    Faites un clic droit sur le nom de votre projet (ici : Aron) et choisissez Properties tout en bas :

 

 

    Une boîte de dialogue s'ouvre. Choisissez le deuxième onglet, qui ressemble à ça :

 

    Pour Debug et pour Release (à gauche), changez le type de Console application à GUI application. Faites OK et le tour est joué !!


Le code source

 

    On va enfin rentrer dans le vif du sujet : le code source. Notre but est ici d'ouvrir une fenêtre. Or on peut ouvrir une fenêtre avec un simple main comprenant tout le code (vous l'aurez sûrement déjà testé dans d'autres tutoriels), mais comme notre but, à terme, n'est pas simplement de s'amuser à ouvrir des fenêtres (Ah bon ?! ) nous allons déjà mettre en forme l'ossature de notre programme pour qu'il soit plus clair et donc plus facilement modifiable après !

    On va donc y aller progressivement ensemble. Pour chaque partie du code, je vais vous donner le nom de la page à créer en cliquant sous Code::Blocks sur File / New / Empty File, en sauvegardant votre projet puis en rentrant le nom de la page à créer. Ensuite je vous donnerai le code commenté dans un cadre au fond blanc, puis une explication, plus ou moins longue selon le besoin.

 

    Ok ! On y va ?!
        C'est parti !!!!!


Nom du fichier : defs.h


  #include <stdio.h>
  #include <string.h>
  #include <stdlib.h>
  #include <math.h>
  #include <SDL.h>

  #define SCREEN_WIDTH 640
  #define SCREEN_HEIGHT 480

 

    Ce premier fichier va contenir toutes les définitions de notre jeu, utiles pour le préprocesseur à la compilation ainsi que les en-têtes des bibliothèques à inclure.

    Quel intérêt, me direz-vous ? C'est tout simple, plus tard, par exemple, on va définir la vitesse moyenne de notre héros : plutôt que de devoir la changer partout dans le code quand on s'apercevra qu'elle est pas bien, on écrira dans notre code PLAYER_SPEED, et quand on voudra changer la vitesse du héros, on viendra le faire ici en changeant la valeur du define. C'est donc super utile ! Et si vous voulez reprendre votre code plus tard, pour faire un autre jeu plus lent ou plus rapide, vous n'aurez presque rien à changer !

    Bon, pour l'instant, on ne définit que deux paramètres : la largeur et la hauteur de notre fenêtre, qui fera donc 640 x 480 pixels.


Nom du fichier : structs.h

 


  #include "defs.h"

  /* Structures qui seront utilisées pour gérer le jeu */

  /* Structure pour gérer l'input (clavier puis joystick) */

   typedef struct Input
  {

    int left, right, up, down, jump, attack, enter, erase, pause;

  } Input;


  /* Structure pour gérer le niveau (à compléter plus tard) */

  typedef struct Gestion
  {

    SDL_Surface *screen;

  } Gestion;


 

    Ce fichier contiendra toutes les structures utilisées par notre jeu. Une structure est très utile car elle permet de regrouper toutes les variables d'un même ensemble. Ainsi Input contiendra la valeur de toutes les touches enfoncées ou non, qui nous intéressent. Ainsi, on pourra tester input.jump pour savoir si notre héros doit sauter (1 = touche enfoncée) ou non (0 = touche relâchée).

    La structure Gestion gèrera au fur et à mesure toutes les variables utiles à la gestion globale du jeu. Ces structures seront globales aux fichiers qui en auront besoin : ainsi, par exemple, Input sera globale au fichier traitant les entrées clavier/joystick mais n'apparaîtra pas, par exemple dans le fichier traitant l'affichage du level où on n'en a pas besoin  .


Nom du fichier : main.c

 


    #include "main.h"

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

    /* Initialisation de la SDL dans une fonction séparée (voir après) */
        init("Aron");

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

    go = 1;


    /* Boucle infinie, principale, du jeu */

    while (go == 1)
    {

        /* On vérifie l'état des entrées (clavier puis plus tard joystick */
        getInput();

        /* On affiche tout */
        draw();

        /* Gestion des 60 fps ( 60 images pas seconde : soit 1s ->1000ms/60 = 16.6 -> 16
        On doit donc attendre 16 ms entre chaque image (frame) */

        delay(frameLimit);
        frameLimit = SDL_GetTicks() + 16;

    }

    /* Exit */
    exit(0);

    }


 

    Eh le voilà enfin, notre main !! Comme vous pourrez le voir, pour l'instant, il est très minimaliste , mais c'est normal, puisqu'on veut simplement ouvrir une fenêtre pour l'instant ! Cependant, il contient déjà l'ossature de notre jeu. En effet, d'abord on initialise la SDL en appelant la fonction init() qui prend comme argument le nom du programme (à afficher en haut de la fenêtre), on prévoit le nettoyage à la sortie (cleanup) pour libérer la mémoire et quitter la SDL, et puis on passe à notre boucle principale où on teste l'état du clavier avec getInput() puis on affiche (ici un écran noir...) et enfin, on laisse respirer le processeur (pour éviter de prendre 100% de ses ressources) et pour bloquer l'affichage à 60 images/stories/seconde, ce qui est très fluide pour un jeu vidéo !

    Voilà, il n'y a pas grand chose à dire de plus, c'est vraiment très basique, et ça ne devrait pas vous poser de problème si vous avez déjà lu les tutoriels sur la SDL que je vous ai recommandés .

 

    Bon passons rapidement à l'en-tête maintenant :


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 delay(unsigned int);


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

  Input input;
  Gestion jeu;


 

    Comme vous pouvez le voir, ça reste très simple : d'abord on donne les prototypes des fonctions utilisées dans le main en indiquant devant extern, pour préciser qu'elles se trouvent dans un autre fichier externe, et ensuite on déclare nos deux structures globales : input pour la gestion des entrées (clavier, joystick) et jeu pour la gestion du jeu (qui contient seulement un pointeur vers l'écran (screen) pour l'instant).


Nom du fichier : init.c

 


  #include "init.h"

   /* Fonction qui initialise le jeu */

   void init(char *title)
   {
  
    /* Initialise SDL Video. Si la valeur de retour est inférieure à zéro, la SDL n'a pas pu
     s'initialiser et on quite */


    if (SDL_Init(SDL_INIT_VIDEO ) < 0)
    {
        printf("Could not initialize SDL: %s\n", SDL_GetError());
        exit(1);
    }


     /* On crée la fenêtre, représentée par le pointeur jeu.screen en utilisant la largeur et la
     hauteur définies dans les defines (defs.h). On utilise aussi la mémoire vidéo
     (
SDL_HWPALETTE) et le double buffer pour éviter que ça scintille
     (
SDL_DOUBLEBUF) */

    jeu.screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, 0,
                                                        SDL_HWPALETTE|SDL_DOUBLEBUF);

     /* Si on y arrive pas, on quitte */

    if (jeu.screen == NULL)
        {
            printf("Couldn't set screen mode to %d x %d: %s\n", SCREEN_WIDTH,  
                     SCREEN_HEIGHT, SDL_GetError());
            exit(1);
        }


    /* On entre le titre de la fenêtre */

    SDL_WM_SetCaption(title, NULL);


    /* On cache le curseur de la souris */

    SDL_ShowCursor(SDL_DISABLE);

    }



   /* Fonction qui quitte le jeu proprement */

    void cleanup()
   {

      /* Quitte la SDL */
      SDL_Quit();

    }

 

    Ce fichier contient donc deux fonctions : la première initialise la SDL, crée la fenêtre, définit son titre et cache le curseur de la souris. La deuxième fonction, très courte pour l'instant, quitte la SDL.


Nom du fichier : init.h

 


   #include "structs.h"

   extern Gestion jeu;


    Le header (en-tête) est très simple : on lui indique simplement qu'il va avoir besoin de la structure globale : jeu, déjà déclarée ailleurs (Mais où ça ? Dans le main bien sûr ! Et sa définition est dans structs.h).


Nom du fichier : input.c

 


  #include "input.h"


  void getInput()
 {
    SDL_Event event;

    /* Keymapping : gère les appuis sur les touches et les enregistre
    dans la structure input */

    while (SDL_PollEvent(&event))
    {
        switch (event.type)
        {

            case SDL_QUIT:
                exit(0);
            break;

            case SDL_KEYDOWN:
                switch (event.key.keysym.sym)
                {
                    case SDLK_ESCAPE:
                        exit(0);
                    break;

                    case SDLK_DELETE:
                        input.erase = 1;
                    break;

                    case SDLK_c:
                        input.jump = 1;
                    break;

                      case SDLK_v:
                        input.attack = 1;
                    break;

                    case SDLK_LEFT:
                        input.left = 1;
                    break;

                    case SDLK_RIGHT:
                        input.right = 1;
                    break;

                    case SDLK_DOWN:
                        input.down = 1;
                    break;

                    case SDLK_UP:
                        input.up = 1;
                    break;

                    case SDLK_RETURN:
                        input.enter = 1;
                    break;

                    default:
                    break;
                }
            break;

            case SDL_KEYUP:
                switch (event.key.keysym.sym)
                {
                    case SDLK_DELETE:
                        input.erase = 0;
                    break;

                    case SDLK_c:
                        input.jump = 0;
                    break;

                    case SDLK_LEFT:
                        input.left = 0;
                    break;

                    case SDLK_RIGHT:
                        input.right = 0;
                    break;

                    case SDLK_DOWN:
                        input.down = 0;
                    break;

                    case SDLK_UP:
                        input.up = 0;
                    break;

                    case SDLK_RETURN:
                        input.enter = 0;
                    break;

                    default:
                    break;
                }
            break;

        }

    }
 }

 

    Cette fonction est assez longue mais néanmoins très simple : elle enregistre tous les events/événements, c'est-à-dire ici principalement les entrées clavier (on verra plus tard comment rajouter d'autres events pour le joystick ou la gestion de la fenêtre). Ensuite, elle traite ces événements dans une boucle (while) jusqu'à ce qu'il n'y en ait plus. Là, il y a 3 grands cas traités dans un switch : ou on veut quitter en cliquant sur la croix, ou on appuie sur une touche, ou on la relache. Si on appuie ou on relache une touche, on met la valeur correspondante dans l'input à 1 ou à 0.

    Ainsi, tant que la touche C est enfoncée, input.jump vaut 1 et quand on la relache, elle vaut 0. L'utilisation qu'on en fera se trouvera plus tard dans la fonction doPlayer() qui s'occupera de déplacer notre héros et tester les collisions.


Nom du fichier : input.h

 


  #include "structs.h"

  extern Input input;

 

    Là encore, rien de spécial. On indique juste que la structure input a déjà été déclarée ailleurs.

Nom du fichier : draw.c

 


#include "draw.h"


  void draw(void)
  {

    /* Affiche l'écran */

    SDL_Flip(jeu.screen);


    /* Delai */

    SDL_Delay(1);

  }



  void delay(unsigned int frameLimit)
  {

    /* Gestion des 60 fps (images/stories/seconde) */

    unsigned int ticks = SDL_GetTicks();

    if (frameLimit < ticks)
    {
        return;
    }

    if (frameLimit > ticks + 16)
    {
        SDL_Delay(16);
    }

    else
    {
        SDL_Delay(frameLimit - ticks);
    }
  }

 

    Et voici les fonctions qui vont se charger de l'affichage. Pour l'instant, c'est très embryonnaire, puisqu'on affiche... rien ! draw() se contente donc de basculer l'affichage vers jeu.screen qui ne contient rien (enfin... du noir, quoi !). Quant à la fonction delay, elle permet d'attendre le temps nécessaire pour respecter les 60 images/stories/seconde (donc une image toutes les 16ms à peu près).


Nom du fichier : draw.h

 


  #include "structs.h"

  extern Gestion jeu;


    Eh voilà, on a maintenant l'ossature complète de notre jeu !!! Il ne reste plus qu'à compiler, en choisissant BUILD puis à tester en choisissant RUN (on peut aussi faire les 2 à la suite avec BUILD AND RUN). Tadaaaaam ! On a notre fenêtre noire !!!


 

    Hum, c'est pas encore super top, mais c'est le début ! Alors vivement la suite !!!


 

 

Connexion

CoalaWeb Traffic

Today31
Yesterday282
This week825
This month3118
Total1742325

19/04/24