Défilement de texte dans une fenêtre en C/SDL

 

Tutoriel présenté par : Robert Gillard (Gondulzak)
Publication : 14 mai 2013
Dernière mise à jour : 8 novembre 2015

      Préliminaires

    Ce tutoriel est le premier d'une série que je voudrais vous faire partager. ;)

   Mon but est d'aider à la realisation de Rpgs (ou jeux de rôles ;) ) en 2D qui vont sûrement vous intéresser. Ces tutoriaux devraient vous permettre d'aller un peu plus loin dans l'elaboration de vos jeux.
   Car après tout, ne sommes nous pas ici sur un site de jeux où l'on crée notamment des Rpgs en 2D ? smiley

   Je vais decouper ce tuto en 3 parties :

        1- Introduction : De quoi s'agit-il ?
        2- Etudions nos fonctions.
        3- Modifier le programme en fonctions, utilisables dans n'importe quel projet.

    Bien, on se lance donc maintenant ! cool

 

    1- Introduction : de quoi s'agit-il ?

   Dans les Rpgs (ou Jeux de roles ;) ), le héros qui parcourt le vaste monde rencontre bien souvent des livres ou des parchemins qu'il peut lire afin de se documenter ou progresser dans l'histoire.

   C'est ce que nous allons programmer ici : en apprenant à faire defiler du texte ! wink

   Mais notre programme ne fera pas que cela ... Nous allons decouvrir quelques astuces plus pros.
   N'ayez pas peur... Si, si, je sais que vous serez capable de tout comprendre. smiley

   Voici enfin ce que nous allons faire avec ce programme : nous commencerons par afficher une image (en png) en fond d'écran ainsi qu'une tile de 32x32 pixels représentant un livre. 

 

Voici le fond ou background ;) !

 

   Cette tile va être découpée dans une petite feuille de sprites contenant 4 tiles (il faut bien la prendre quelque part hein ?) laugh et blittée vers le coin inférieur gauche de notre fenêtre de jeu.


   Il s'agit de la première tile en bas à gauche, mais vous pourrez choisir celle que vous voudrez ;) .

 

 

   Le déplacement de la souris sur cette tile (livre) va agir comme une infobulle et afficher l'info : Lire le livre.

 

 

   Un clic gauche sur le livre ouvre alors une fenêtre représentant une boîte de dialogue dans laquelle notre texte est affiché.
   Il ne reste plus qu'à faire défiler le texte avec les touches fléchées HAUT et BAS de notre clavier. (Et pourquoi pas plus tard avec un curseur...?) cheeky

 

Début de notre texte...


                          Pendant le scrolling...


                            Et en fin de texte...

 

   Quoi ?... mais c'est tout ce que ce programme fait... ? frown

   Attendez... Oui, on scrolle dans une fenêtre, mais imaginez ce que vous allez pouvoir apporter en plus à votre jeu : lire ce qu'un personnage a à dire et cela, sans limite de texte, pouvoir intégrer des lectures de livres se rapportant à votre jeu...

   Cool, non ? cool

   Mais tout ceci ne va pas se faire en claquant des doigts... Il y a pas mal de code à etudier mais ne vous en faites pas, celui-ci est déjà abondamment commenté dans chaque fonction et j'ajouterai en plus les explications nécessaires à la fin du code de celles-ci.

   Et ceci nous amène donc à la seconde partie de ce tuto...

 

      2. Etudions nos fonctions

   Nous allons avoir besoin de pas mal de constantes et de variables pour notre programme, nous allons voir tout cela dans ce chapitre. 
   Nous commencerons, et ceci pour découper et mieux comprendre notre programme, par créer un fichier "constantes.h".
   Voici le code de ce fichier, il est abondamment commenté mais nous y ajouterons quelques explications après : wink
 

//Constantes.h
#ifndef DEF_CONSTANTES
#define DEF_CONSTANTES
 
//Les attributs de l'ecran (800 * 600)
const int SCREEN_WIDTH = 800; // Largeur ecran
const int SCREEN_HEIGHT = 600; // Hauteur ecran
const int SCREEN_BPP = 32; // Resolution en bits par pixel
 
//Les attributs de la feuille de sprites
const int SHEET_WIDTH = 64; // Largeur de la feuille de sprites
const int SHEET_HEIGHT = 64; // Hauteur de la feuille de sprites
 
//Les attributs d'un sprite // Ici, un carre
const int SPRITE_WIDTH = 32; // Largeur du sprite
const int SPRITE_HEIGHT = 32; // Hauteur du sprite
 
//Le nom du parchemin
const char *oldParchem[] = {"Lire le livre"}; //titre du parchemin a lire
 
//Nb de lignes affichees dans la fenetre
// (sera modifie ici en fonction de votre propre fenetre)
const int nb_lignes_affichees = 5;
 
//Variable de controle de lignes de texte
/* (sera modifie ici en fonction de votre propre fenetre)
Represente la difference entre le nombre de lignes total du texte et le
nombre de lignes affichees dans la fenetre) */
const int maxLines = 9; //Apres les 5 premieres lignes affichees, il en reste 9
 
//Distance entre deux lignbes de texte (en pixels)
// (sera modifie ici en fonction de votre propre fenetre)
const int DELTA_LINE = 16;
 
//Un message a afficher
/* Je ne l'ai pas fait ici mais le texte peut bien entendu etre charge a partir
d'un fichier contenant vos textes */
const char *text[]={
"        Aron le pirate des 12 mers",
" ",
"Ce fond d'ecran est issu du jeu Wiwi's,",
"Adventures 3 du Studio Meruvia : www.meruvia.fr.",
"Il s'affiche ici a titre d'exemple, avec ce texte,",
"pour illustrer le tutoriel propose par Gondulzac",
"sur le site web. Bien entendu, si vous decidez de",
"reutiliser ce code pour l'un de vos projets,",
"vous remplacerez ce texte par le votre ;) .",
"Ici, ce n'est qu'un exemple de ce que vous",
"pouvez faire avec. Il peut etre, en effet, tres",
"utile pour afficher des dialogues dans un RPG, ou encore",
"un debriefing de mission, ou simplement de l'aide.",
"                                    A bientot sur www.meruvia.fr"
 
};
 
#endif

 

   Comme vous pouvez le voir, le fichier commence et se termine par des directives de préprocesseur :

#ifndef DEF_CONSTANTES
#define DEF_CONSTANTES
              au début du fichier, et
#endif à la fin.

 

   Il ne s'agit que d'une protection contre les inclusions infinies et cela ne doit pas vous préoccuper. wink
   Sachez seulement qu'il est préférable de mettre de telles instructions au début et à la fin de tous vos fichiers d'en-tête (headers .h). C'est une bonne habitude à prendre, mais sachez que si vous ne le faites pas, le compilateur ne vous en voudra pas... laugh
    - La constante SCREEN_BPP représente les couleurs 32 bits.
    - Le texte à afficher est inclus en entier dans une constante de type const char qui se trouve être un pointeur sur le tableau text[].
   Vous voyez que chaque ligne du texte est separée par une virgule (à ne pas oublier ! cheeky) sauf à la derniere ligne qui est la fin du tableau.


Attention au début du tableau qui commence par { et se termine par };

   Je crois qu'il n'y a rien d'autre à expliquer ici et que tout est clair, nous passons donc à la suite. smiley

   Les fonctions suivantes que nous allons etudier font toutes partie du fichier parchemin.cpp. Nous allons les voir une à une avant d'afficher le programme en entier.

   Nous initialisons d'abord nos variables globales :
  

//parchemin.cpp
#include "constantes.h"
 
//Les fichiers d'entete
#include "SDL/SDL.h"
#include "SDL/SDL_image.h"
#include "SDL/SDL_ttf.h"
 
//Les surfaces
SDL_Surface *sprite_sheet = NULL; //la feuille de sprites ou l'on decoupera le parchemin
SDL_Surface *background = NULL; //l'image png de fond d'ecran
SDL_Surface *screen = NULL;  
SDL_Surface *message = NULL; 
SDL_Surface *textParch = NULL; 
SDL_Surface *papyrus = NULL;
 
//La structure d'evenements
SDL_Event event;
 
//Position de l'image Fond Ecran
SDL_Rect positionFond;
 
/*Position dans la feuille de sprites du sprite
que l'on desire decouper. Il s'agit d'un sprite 32x32 qui represente le
parchemin a lire */
int IndexSprite_x = 0; //colonne 0
int IndexSprite_y = 32; //ligne 1 (du bas)
 
//Les differentes parties de la feuille de sprites qui vont etre blittees
//Une seule actuellement, le parchemin.
SDL_Rect clip;
 
/* Position sur l'ecran du sprite a afficher (represente le parchemin a lire et
sur lequel on clique gauche pour ouvrir la fenetre qui nous affiche une partie
du texte que l'on fera defiler a l'aide des touches flechees) */
SDL_Rect offset;
int position_x = 47;
int position_y = 535;
 
//Position du papyrus (la fenetre qui affichera notre texte)
SDL_Rect papyPos;
int papy_x = 85;
int papy_y = 450;
 
//Les entiers de controle des evenements
int quit = 0;
int mouseClicked = 0;
int dialog = 0; //pour la lecture du texte
 
//Le font qu'on va utiliser
TTF_Font *font;
 
//La couleur du Font et du message
SDL_Color textColor = {255, 255, 255}; //Texte en blanc
SDL_Color messageColor = {0, 255, 255}; //Message en bleu
int compteur = 0; //pour compter les lignes a afficher
int noLigne = 0; //premiere ligne du texte
int ligneActuelle = 0;
 
//Coordonnees du texte a afficher
SDL_Rect textPos;
int tpos_x = 115;
int tpos_y = 470;
 
//Coordonnees du message a afficher ("Vieux parchemin")
SDL_Rect messagePos;
int mess_x = 85;
int mess_y = 520;
 
//Coordonnees du pointeur de la souris
int x;
int y;

 

   Voilà les variables qui seront utilisées pour le bon deroulement du programme, nous allons voir tout cela dans nos différentes fonctions.

Nous n'oublierons pas d'inclure le fichier constantes.h au début de parchemin.cpp ! wink

   Passons maintenant à la première fonction de parchemin.cpp. Nous commencerons avec la fonction d'optimisation d'une image que vous avez déjà vue dans le BIG TUTO SDL 1.2/2.


      1 - Fonction : load_image (char *) 

 
//La fonction d'optimisation d'une image
SDL_Surface *load_image( char *filename )
{
 
   //L'image qui est chargee
   SDL_Surface* loadedImage = NULL;
 
   //L'image optimisee qu'on va utiliser
   SDL_Surface* optimizedImage = NULL;
 
   //Chargement de l'image
   loadedImage = IMG_Load( filename );
 
   //Si l'image est chargee
   if( loadedImage != NULL )
   {
 
      //Creation de l'image optimisee
      optimizedImage = SDL_DisplayFormat( loadedImage );
 
      //Liberation de l'ancienne image
      SDL_FreeSurface( loadedImage );
 
      //Si la creation de l'image optimisee s'est bien passee
      if( optimizedImage != NULL )
      {
 
           SDL_SetColorKey( optimizedImage, SDL_SRCCOLORKEY,
           SDL_MapRGB( optimizedImage->format, 0,255,255 ) );
 
      }
 
   }
 
   //On retourne l'image optimisee
   return optimizedImage;
 
}

 

    Mais que fait cette fonction ? sad
   Elle vérifie simplement le format de l'image chargée. Si la fonction rencontre une erreur, loadedImage sera ignorée et si le chargement de l'image se fait correctement, la fonction SDL_DisplayFormat() sera appelée, ce qui créera une nouvelle version de loadedImage dans le même format que l'écran.

   La raison de ceci est que quand vous essayez de blitter une surface sur une autre d'un différent format, la SDL convertit la première afin que toutes deux aient le même format (Ex : une image 16 bits sur une image 24 bits).

   Créer une surface convertie chaque fois que vous blittez une image coûte énormément en puissance de calculs au processeur. Et comme cette fonction est appelée avant le blittage d'une image, les surfaces auront déjà le même format au moment de blitter, ce qui accélèrera grandement le processus.
   Une fois l'image optimisée créée, nous libérons la mémoire qu'utilisait loadedImage en appelant la fonction SDL_FreeSurface(). wink

   Passons maintenant à notre seconde fonction : la fonction d'initialistion.


   2 - Fonction : init()

int Init()
{
   //Initialisation de tous les sous-systemes de SDL
   if( SDL_Init( SDL_INIT_EVERYTHING ) == -1 )
   {
        return 1;
   }
 
   //Mise en place de l'ecran
   screen = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP,
   SDL_HWSURFACE | SDL_DOUBLEBUF);
 
   //S'il y a une erreur lors de la mise en place de l'ecran
   if( screen == NULL )
   {
        return 1;
   }
 
   //Initialisation de SDL_ttf
   if(TTF_Init() == -1)
   {
        return 1;
   }
 
   return 0;
}

 

   La fonction init() ne présente aucune difficulté, TTF_Init() initialise l'affichage sur l'écran.
   Nous passons à notre troisième fonction, le chargement des images en mémoire. smiley


     3 - Fonction loadfiles()

 
//Chargement des images en memoire
int loadfiles()
{
 
   //Chargement de la feuille de sprite
   sprite_sheet = load_image( "Biblio.png" );
 
   //S'il y a eu un probleme au chargement de la feuille de sprites
   if( sprit2e_sheet == NULL )
   {
        return 1;
   }
   //Chargement de l'image de fond
   background = load_image ( "Fond.png");
  
   //S'il y a eu un probleme au chargement de l'image de fond
   if( background == NULL )
   {
        return 1;
   }
 
   //Chargement du papyrus
   papyrus = load_image( "Papyrus2.png");
 
   //S'il y a eu un probleme au chargement du papyrus
   if( papyrus == NULL )
   {
        return 1;
   }
 
   //Ouverture du Font
   font = TTF_OpenFont("CaslonBold.ttf", 13);
  
   //Si il y a eu une erreur au chargement du Font
   if( font == NULL )
   {
        return 1;
   }
 
   return 0;
 
}

 

   Nous chargeons ici successivement :

   - La feuille de sprites Biblio.png, qui contient notre "livre rouge" que nous allons découper dans la fonction suivante.
   - Fond.png (qui est notre fond d'écran 800x600 pixels).
   - Papyrus2.png (qui est notre fenêtre qui représente la boîte de dialogue dans laquelle va défiler notre texte).
   - Et bien entendu notre police de caractères (Ici CaslonBold.ttf). Mais je ne serai pas fâché si vous voulez utiliser une autre police. Vous devrez simplement faire attention à la hauteur des caractères si vous ne voulez pas modifier les dimensions de la fenêtre...

   Et voila notre quatrième fonction : la découpe du "livre rouge" dans la feuille de sprites :


   4 - Fonction cutSprite()

 
void cutSprite()
{
   //On coupe le sprite (parchemin) aux coordonnees voulues
   clip.x = IndexSprite_x;
   clip.y = IndexSprite_y;
   clip.w = SPRITE_WIDTH;
   clip.h = SPRITE_HEIGHT;
}

 

   IndexSprite_x est l'abcisse du livre dans la feuille de sprites (donc 0).
   IndexSprite_y est l'ordonnée du livre dans la feuille de sprites (donc 32 puisqu'il est sur la deuxième ligne).
   Notez que SDL_Rect clip a été défini au début de parchemin.cpp. wink

   Notre cinquième fonction va afficher le texte dans la fenêtre.
 

      5 - Fonction ShowText(char*, int)

int ShowText(const char *tab[], int nblines)
{
 
   int compteur;
   // On affiche les nblines phrases du texte (ici 5)
 
   for(compteur = noLigne; compteur < nblines; compteur++)
   {
 
        message = TTF_RenderText_Solid(font, *(text + noLigne), textColor);
 
        if(message == NULL)
        {
             return 1;
        }
 
        SDL_BlitSurface(message, NULL, screen, &textPos);
        textPos.y += DELTA_LINE; //Le texte est ecrit tous les 16 pixels
        noLigne++;
 
   }
 
   return 0;
}

 

   Nous affichons ici nos 5 premières lignes de texte (en fait, il s'agit du nombre de lignes que nous avons decidé d'afficher en un fois dans notre fenêtre, mais vous pouvez le changer et faire une fenêtre plus grande ;) ).
   Pour ce faire, nous initialisons une variable compteur à 0. N'oublions pas que *text[] est un pointeur sur le premier élément d'un tableau donc :

*(text + 0) pointe sur la premiere ligne du tableau,
*(text + 1) pointe sur la seconde ligne du tableau,

et ainsi de suite jusqu'à *(text + 4) qui représente la cinquième ligne du tableau.


   TTf_RenderText_Solid est l'une des formes d'écriture de SDL_ttf.
   Et nous écrivons nos 5 lignes avec un écart de 16 pixels. Rien de bien compliqué ici, nous passons un pointeur sur le tableau et un entier représentant le nombre de lignes à écrire en paramètres à notre fonction ShowText.

   Nous pouvons donc passer à notre sixième fonction : IsInside()


   6 - Fonction : IsInside()

   Et voici notre délicieuse fonction IsInside() : smiley

// Les coordonnees de la souris sont-elles sur le sprite ?

int isInside(int x, int y)

{

      return ( (x>offset.x)&&(x<offset.x + offset.w)&&

                         (y>offset.y)&&(y<offset.y + offset.h) );

}

 

   Que fait cette fonction ?
   Lorsque nous faisons voyager notre curseur sur l'écran, les coordonnées de celui-ci sont enregistrées par des "event.motion" que nous verrons dans la fonction main(). Cette fonction vérifie donc que le curseur se trouve sur le livre et renvoie la position de x et y du curseur sur celui-ci.
   En effet, si offset.x représente la position en x de notre livre et que offset.y représente la position en y du livre, les largeur et hauteur du livre sont representées respectivement par offset.x + offset.w et par offset.y + offset.h (ce qui délimite notre tile livre).
   Et si l'on fait un clic gauche sur le livre, nous allons ouvrir notre fenêtre d'affichage et pouvoir lire le joli texte que nous y avons écrit. wink 

   Notre septième fonction : La fonction de rafraîchissement de l'écran.


7 - Fonction : Refresh()
  

void Refresh()
{
 
   /* On rafraichit l'ecran : On affiche le fond d'ecran et le parchemin en modifiant sa
   transparence */
   SDL_BlitSurface(background, NULL, screen, &positionFond); //Repositionnement du                                                                                                   //background
 
   SDL_SetAlpha(sprite_sheet, SDL_SRCALPHA, 180);  //On modifie la transparence alpha du
                                                                          //parchemin
 
   SDL_BlitSurface(sprite_sheet, &clip, screen, &offset); // Et on blitte.
 
   /* On enleve la couleur de fond de papyrus (ici, le noir) */
   SDL_SetColorKey(papyrus, SDL_SRCCOLORKEY,
                                      SDL_MapRGB(papyrus->format, 0,0,0));
  
   /* On applique le papyrus a l'ecran en modifiant sa transparence */
   SDL_SetAlpha(papyrus, SDL_SRCALPHA, 255); //transparence alpha nulle
 
   SDL_BlitSurface(papyrus, NULL, screen, &papyPos);
 
   dialog = 1; // la fenetre de dialogue a ete affichee
 
}

 

   Rien à expliquer ici, les commentaires parlent d'eux-mêmes. ;)
   Fonction 8 : Libération de la mémoire
 

   8 - Fonction : clean_up()

void clean_up()
{
 
   //On libere la feuille de sprites
   SDL_FreeSurface( sprite_sheet );
 
   //On libere l'image de fond
   SDL_FreeSurface( background );
 
   //On libere le papyrus
   SDL_FreeSurface( papyrus );
 
   //On quitte SDL_ttf
   TTF_CloseFont(font);
 
   //On quitte SDL
   SDL_Quit();
 
}

 

    Et voici enfin notre fonction main() :

   Pas de stress, elle est plus longue que difficile... ;) Elle est déjà très fortement commentée, et croyez-moi, elle ne mord pas... smiley
  

int main( int argc, char* args[] )
{
 
   //Initialisation
   Init(); 
 
   //Chargement des fichiers en memoire
   loadfiles();
 
   //Appel de la fonction de decoupe de sprites
   cutSprite();
 
   //On applique le fond d'ecran
   positionFond.x = 0;
   positionFond.y = 0;
   SDL_BlitSurface(background, NULL, screen, &positionFond);
 
   //On enleve la couleur de fond du sprite
   SDL_SetColorKey(sprite_sheet, SDL_SRCCOLORKEY,
   SDL_MapRGB(sprite_sheet->format, 0,0,0));
   
   //On applique le sprite sur l'ecran
   SDL_SetAlpha(sprite_sheet, SDL_SRCALPHA, 255);
   offset.x = position_x;
   offset.y = position_y;
   offset.w = position_x + SPRITE_WIDTH;
   offset.h = position_y + SPRITE_HEIGHT;
   SDL_BlitSurface(sprite_sheet, &clip, screen, &offset);
 
   //Coordonnees du papyrus
   papyPos.x = papy_x;
   papyPos.y = papy_y;
 
   //Coordonnees du texte a afficher
   textPos.x = tpos_x;
   textPos.y = tpos_y;
 
   //Coordonnees du message a afficher
   messagePos.x = mess_x;
   messagePos.y = mess_y;


   /**************************/
   /*** Boucle de messages ***/
   /**************************/
 
   SDL_EnableKeyRepeat(10, 10); //pour scroller en laissant une fleche appuyeee
 
   //Tant que l'utilisateur n'a pas quitte
   while( quit == 0 )
   {
 
      //Tant qu'il y a un evenement
      SDL_WaitEvent(&event); /* Ou SDL_PollEvent si d'autres evenements
                                doivent se produire en meme temps */
 
      switch(event.type)
      {
 
         case SDL_QUIT:
            quit = 1;
            break;
 
            case SDL_KEYDOWN:
            switch (event.key.keysym.sym)
            {
 
               case SDLK_ESCAPE:
                  /*Appui sur la touche Echap, on arrete le programme*/
                  quit = 1;
                  break;
 
               case SDLK_DOWN:  /* on presse la touche BAS, le texte defile vers le bas en
                                 affichant 5 lignes */
 
               textPos.y = tpos_y;   //position de la ligne de texte affichee
 
               if ((mouseClicked)&&(dialog)) 
               {
 
                  if(ligneActuelle < maxLines)  /* Nous avons vu au debut du tutorial que
                                                          maxLines representait le nb de lignes restant
                                                          apres avoir affiche 5 lignes de texte */
                  {
 
                     noLigne++; // on incremente le numero de ligne
                     Refresh(); //gestion de la fenetre
 
                     // On affiche les 5 phrases du texte
                     for(compteur =0;compteur<nb_lignes_affichees; compteur++)
                     {

 

                        message = TTF_RenderText_Solid(font, *(text + (noLigne)), textColor);
 
                        if(message == NULL)
                        {
                           return 1;
                        }
 
                        SDL_BlitSurface(message, NULL, screen, &textPos);
                        textPos.y += DELTA_LINE; //Le texte est ecrit tous les 16 pixels
                        noLigne++;
 
                      }
 
                      mouseClicked = 1;
 
                      //Repositionnement du test de clic
                      ligneActuelle++;   // On incremente la ligne actuelle
                      noLigne = ligneActuelle;  //Et le numero de ligne devient la ligne actuelle
                  }
 
                  SDL_Delay(100); //pour un defilement pas trop rapide
               }
               break;
 
               case SDLK_UP:
                  textPos.y = tpos_y;
 
                  if ((mouseClicked)&&(dialog))
                  {
 
                     if(ligneActuelle > 0)
                     {
 
                        noLigne--;
                        Refresh(); //gestion de la fenetre
 
                       // On affiche 5 phrases du texte
                       for(compteur = 0; compteur < nb_lignes_affichees; compteur++)
                       {
 
                          message = TTF_RenderText_Solid(font, *(text + (noLigne)), textColor);

 

                          if(message == NULL)
                          {
                             return 1;
                          }
 
                          SDL_BlitSurface(message,NULL,screen,&textPos); // et on blitte
                          textPos.y += DELTA_LINE;
                          //Le texte est ecrit tous les 16 pixels
                          noLigne++;  //on incremente le numero de ligne
 
                       }
 
                       mouseClicked = 1;
                       //Repositionnement du test du clic
                       ligneActuelle--; //on decremente ligneActuelle
                       noLigne = ligneActuelle; //Et le numero de ligne devient la ligne actuelle
 
                     }
 
                    SDL_Delay(100); //pour un defilement pas trop rapide
 
                  }
                  break;
               }
               break;
 
               case SDL_MOUSEMOTION : // La souris voyage sur l'ecran
               {
                  //On recupere ses coordonnees a chaque mouvement
                  x = event.motion.x;
                  y = event.motion.y;
 
                  /* Si le pointeur de la souris est sur le livre et que l'on n'a
                     pas encore clique */
                  if((isInside(x, y))&&(!mouseClicked))
                  {
 
                     //On indique ce que represente le parchemin
                     textParch = TTF_RenderText_Solid(font,
                                                     *(oldParchem), messageColor);
                     if(textParch == NULL)
                     {
                        return 1;
                     }
 
                     /* On affiche le livre a l'ecran en modifiant sa transparence */
                     SDL_BlitSurface(background, NULL, screen, &positionFond);
                     SDL_SetAlpha(sprite_sheet, SDL_SRCALPHA, 180);
                     SDL_BlitSurface(sprite_sheet, &clip, screen, &offset);
 
                     //On affiche le message a l'ecran
                     SDL_BlitSurface(textParch, NULL, screen, &messagePos);
 
                  }
            else
                  /* Si le pointeur de la souris est en dehors du livre et
                  que l'on a pas encore clique */
                  if(!mouseClicked)
                  {
                     //On rafraichit l'ecran
                     SDL_BlitSurface(background, NULL, screen,&positionFond);
                     SDL_SetAlpha(sprite_sheet, SDL_SRCALPHA, 255);
                     SDL_BlitSurface(sprite_sheet, &clip, screen, &offset);
                  }
             }
             break;
 
             case SDL_MOUSEBUTTONDOWN :
                //Si on appuie sur le bouton gauche de la souris
                if((event.button.button) == SDL_BUTTON_LEFT)
                {
                   //On recupere les coordonnees du pointeur
                   x = event.button.x;
                   y = event.button.y;
 
                   //Si le pointeur de la souris se trouve sur le livre
                   if(isInside(x, y))
                   {
                      //A-t-on deja clique ?
                      if(mouseClicked) //si mouseClicked = 1
                      {
                         /* Si oui on rafraichit l'ecran, on repositionne
                            l'ordonnee du texte et on change la valeur du test */
                         SDL_BlitSurface(background, NULL, screen, &positionFond);
                         SDL_SetAlpha(sprite_sheet,SDL_SRCALPHA, 255);
                         SDL_BlitSurface(sprite_sheet, &clip, screen, &offset);
                         textPos.y = tpos_y;
                         mouseClicked = 0; //Repositionnement du test du clic
                      }
                      else
                      {
                          Refresh(); //gestion de la fenetre
                          // On affiche les 5 phrases du texte
                          noLigne = 0;
                          ShowText(text, nb_lignes_affichees);
                          mouseClicked = 1;
                          //Repositionnement du test du clic
                      }
 
                   }
                   else
                     /* Si l'on a clique et que le pointeur de la souris ne se
                        trouve pas sur le livre*/
                     {
                        /* On rafraichit l'ecran avec le livre non transparent */
                        SDL_BlitSurface(background, NULL, screen, &positionFond);
                        SDL_SetAlpha(sprite_sheet, SDL_SRCALPHA, 255);
                        SDL_BlitSurface(sprite_sheet, &clip, screen, &offset);
 
                        //On repositionne le test de clic et l'ordonnee du texte
                        mouseClicked = 0;
                        textPos.y = tpos_y;
                        dialog = 0; //On n'est plus en mode dialogue
                }
                break;
          }
          break;
     }
 
     //Mise a jour de l'ecran
     if( SDL_Flip( screen ) == -1 )
     {
         return 1;
     }
 
   }
 
 
   /***************************/
   /*** Sortie de programme ***/
   /***************************/
 
   //On libere les images et on quitte SDL
   clean_up();
 
   return 0;
 
}

 

    Voilà, c'est tout... laugh
   Que se passe-t-il dans la boucle de messages ?
   Retenons simplement que seules les touches HAUT et BAS ansi que le clic gauche de la souris initialisent une série de tests (les commentaires sont dans le programme). Sachant ça, c'est tout simple. ;)
   Ceci termine donc la seconde partie du tutoriel. :) 

 

      3. Modifier le programme en une fonction utilisable dans un projet

   Si vous compilez le programme, vous allez voir qu'il affiche bien notre texte et que vous pouvez le faire défiler vers le bas et vers le haut.
   Ceci est très bien pour voir une solution de défilement dans un but pédagogique (même s'il y a d'autres facons de programmer un défilement de texte en C). ;)
   Cependant je sais que vous voudriez essayer ceci dans un de vos projets SDL et pour ce faire nous allons modifier quelques petites choses pour que vous puissiez l' intégrer dans votre jeu sans trop de difficultés. cool
   Nous commencerons par modifier notre fonction ShowText(char*, int) en la remplacant par celle-ci.

 
 int ShowText(const char *tab[], int nblines)
 {
      int compteur;
 
      // On affiche les nblines phrases du texte (ici 5)
      for(compteur = 0; compteur < nb_lignes_affichees; compteur++)
      {
 
         message = TTF_RenderText_Solid(font, *(text+noLigne), textColor);
 
         if(message == NULL)
         {
             return 1;
         }
 
         SDL_BlitSurface(message, NULL, screen, &textPos);
         textPos.y += DELTA_LINE; //Le texte est ecrit tous les 16 pixels
         noLigne++;
 
      }
 
      if(up==1) //Si on presse la touche HAUT
      {
            ligneActuelle++; //On incremente la ligne actuelle
            noLigne = ligneActuelle; //Le numero de ligne devient la ligne actuelle
      }
 
      return 0;
 }

 

   Qu'y a-t-il de neuf dans cette fonction ? 
   J'y ai ajouté un test sur une variable ("up") qui est "vraie" si on a pressé la touche HAUT. Et on incrémente la variable ligneActuelle au sein de la fonction en donnant à la variable noLigne la valeur de ligneActuelle.
   Pourquoi, me demanderez-vous ? frown
   Mais tout simplement pour aérer notre fonction main() en y supprimant des lignes de code redondantes et en y insérant notre fonction pour rendre le tout plus fonctionnel. ;)

   Ne vous en faîtes pas, je vous donnerai la seconde version de parchemin.cpp afin que vous puissiez la tester aussi et surtout pouvoir plus facilement l'intégrer au contenu de votre jeu. :)
   Je ne remets pas ici tout le code de la fonction main() mais simplement les parties modifiées des case SDLK_UP et case SDLK_DOWN :

 
case SDLK_DOWN: //On presse la touche BAS
 
   textPos.y = tpos_y;
   up = 1;
   if ((mouseClicked)&&(dialog))
   {
 
      if(ligneActuelle <= maxLines)
      {
 
         Refresh(); //gestion de la fenetre
 
         //On affiche ici notre fonction ShowText
         ShowText(text, nb_lignes_affichees);
 
         mouseClicked = 1;
 
      }
      SDL_Delay(100); //pour un defilement pas trop rapide
 
   }
   break;

    C'est quand même mieux non ? cool
   Et pour terminer :

 
case SDLK_UP: // On presse la touche HAUT
 
   textPos.y = tpos_y; // On reinitialise l'ordonnee de la premiere phrase a blitter
   up = 0;
 
   if ((mouseClicked)&&(dialog))
   {
 
      if(ligneActuelle > 0)
      {
 
         Refresh(); //gestion de la fenetre
         ligneActuelle--; //On decremente la ligne actuelle
         noLigne = ligneActuelle; //La ligne actuelle devient le numero de ligne
 
         //on affiche ici notre fonction ShowText
         ShowText(text, nb_lignes_affichees);
 
         mouseClicked = 1;
         //Repositionnement du test du clic
 
      }
      SDL_Delay(100); //pour un defilement pas trop rapide
 
   }
   break;

 

    Voilà, c'est tout. :)
   - Quand on presse la touche BAS, on affiche 5 lignes de texte et on incrémente les variables associées.
   - Quand on presse la touche HAUT, on décrémente les variables associées et on affiche 5 lignes de texte.
     Ce programme est compilable sous Code::blocks et visual c++ avec les paramètres "compiler en code C" ou "compiler en code C++" (j'ai essayé les deux sans probleme ;) ).

   Vous pouvez télécharger (lien au début) les fichiers :
- constantes.h
- parchemin.cpp
- Fond.png
- biblio.png
- papyrus2.png
               pour compiler la première version du programme.
   Et remplacer parchemin.cpp par parchemin2.cpp pour compiler la version optimisée. ;)

   J'espère que ce tuto vous aura apporté quelques idées supplémentaires pour vos projets de jeux et je vous dis à bientot pour d'autres tutos (venez notamment voir mon Big Tuto XNA 4 qui vous permettra de créer un jeu de shoot de A à Z) !
   @ bientôt,
             Gondulzak. 

 

 

 

Connexion

CoalaWeb Traffic

Today72
Yesterday185
This week257
This month3157
Total1758738

16/07/24