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é :  




9. Animons notre héros !


    Maintenant que le sprite de notre héros s'affiche correctement, nous allons l'animer en faisant défiler les frames (images) de son animation à l'aide d'un timer (chronomètre). Rassurez-vous, ça ne sera pas si difficile que cela !
 


Résultat à la fin de ce chapitre : notre héros va prendre vie et marcher  !



A. Mettons à jour la structure de notre héros

    Nous allons commencer par mettre à jour notre structure Hero avec deux variables qui nous seront utiles pour animer notre sprite :
        - frameNumber : qui contiendra le numéro de la frame en cours pour notre animation.
        - frameTimer : qui sera notre chronomètre entre 2 frames.


Nom du fichier : structs.h


   /* Structure pour gérer notre héros */

   typedef struct Hero
   {
       //Sprite du héros (pas d'animation pour l'instant)
       SDL_Surface *sprite;

       /* Coordonnées du héros */
       int x, y;
   
    /* Variables utiles pour l'animation */
    int frameNumber, frameTimer;

   } Hero;

   

    Et tant que nous y sommes, nous allons aussi rajouter une constante à notre fichier defs.h :
   

Nom du fichier : defs.h


   /* Constantes pour l'animation */
   #define TIME_BETWEEN_2_FRAMES 8

 

    Par défaut, je lui ai donné la valeur 8. Cela signifie que tous les 8 tours de boucle on change de frame. Vous pourrez vous amuser à modifier cette valeur pour accélérer/décélérer la vitesse de l'animation (dans Aron's Journey in Dreamland, cette valeur change en fonction de la vitesse du héros : plus il va vite, plus l'animation est rapide !) .


B. Ajoutons un fichier dédié à l'animation

    Nous allons maintenant créer deux nouveaux fichiers qui contiendront tout ce qui concernera l'animation (de notre héros dans un premier temps, puis aussi des monstres, etc.) : animation.c et animation.h. Pour cela, faites comme d'habitude : File/New/Empty File et entrez le nom du fichier à créer.

    Commençons par animation.h. Nous allons y ajouter la déclaration externe de nos structures globales pour que le compilateur sache bien où aller les chercher. Nous aurons besoin de notre structure player, bien sûr   mais aussi de la structure jeu.

Nom du fichier : animation.h


    #include "structs.h"

    extern Gestion jeu;
    extern Hero player;


   /* Prototype des fonctions externes que nous allons utiliser ensuite */
    extern void drawplayer(void);

    Passons maintenant au fichier animation.c et créons notre fonction drawanimatedplayer() qui se chargera d'animer notre héros :


Nom du fichier : animation.c

 

#include "animation.h"


void drawanimatedplayer()
{

    /* Gestion du timer */

    // Si notre timer (un compte à rebours en fait) arrive à zéro

    if (player.frameTimer <= 0)
    {
        //On le réinitialise

        player.frameTimer = TIME_BETWEEN_2_FRAMES;

        //Et on incrémente notre variable qui compte les frames de 1 pour passer à la suivante

        player.frameNumber++;

        //Mais si on dépasse la frame max, il faut revenir à la première
        //Pour connaître la frame max, il suffit de diviser la longueur du spritesheet
        //par la longueur de notre héros : 480 / 40 = 12 frames
        //Puisque la première frame est la numéro 0, la dernière est donc la numéro 11

        if(player.frameNumber >= player.sprite->w / PLAYER_WIDTH)
            player.frameNumber = 0;

    }
    //Sinon, on décrémente notre timer
    else
        player.frameTimer--;


    //Ensuite, on peut passer la main à notre fonction
    drawplayer();


}  


    Comme vous pouvez le voir, cette fonction gère surtout le timer du héros avant de rendre la main à notre fonction précédente : drawplayer(). Le fonctionnement du timer est expliqué dans le code. Je ne m'attarderai donc pas dessus, vu que c'est assez évident  .

    Bon, il est maintenant temps de reprendre notre fonction drawplayer() et de la mettre à jour !

   
Nom du fichier : player.c


    void drawplayer()
   {
    /* Rectangle de destination à blitter */
    SDL_Rect dest;

    // On soustrait des coordonnées de notre héros, ceux du début de la map, pour qu'il colle
    //au scrolling :
    dest.x = player.x - map.startX;
    dest.y = player.y - map.startY;

    dest.w = PLAYER_WIDTH;
    dest.h = PLAYER_HEIGTH;

    /* Rectangle source à blitter */
    SDL_Rect src;

    //Pour connaître le X de la bonne frame à blitter, il suffit de multiplier
    //la largeur du sprite par le numéro de la frame à afficher -> 0 = 0; 1 = 40; 2 = 80...
    src.x = player.frameNumber * PLAYER_WIDTH;

    src.y = 0;
    src.w = PLAYER_WIDTH;
    src.h = PLAYER_HEIGTH;

    /* Blitte notre héros sur l'écran aux coordonnées x et y */

    SDL_BlitSurface(player.sprite, &src, jeu.screen, &dest);

}


    Les changements sont là aussi indiqués en bleu.
    En fait, il s'agit de mettre à jour, à la fois le rectangle source pour blitter la bonne frame (et pas toujours la première) et le rectangle d'arrivée pour que les coordonnées du sprite correspondent maintenant à ceux de la map et pas seulement à ceux de l'écran !


    Pour connaître la bonne frame à blitter, rien de plus simple : on multiplie le numéro de la frame à blitter par la largeur du sprite.
    Si le sprite fait 40 pixels de large : la première frame se trouve à 0 x 40 = 0 pixels, la deuxième à 1 x 40 = 40 pixels, la troisième à 2 x 40 = 8à pixels, etc.
    Y ne change pas, puisque notre spritesheet ne comporte qu'une ligne (contrairement à notre tileset).

    Maintenant, pour que notre lapin-ninja reste au début du niveau quand on scrolle, il suffit de soustraire les coordonnées x et y du début de la map à afficher, à ses coordonnées. Rien de plus simple !

   
    Voilà, n'oublions pas de mettre à jour player.h pour qu'il connaisse l'existence de notre structure map : 
 
   Nom du fichier : player.h
 

#include "structs.h"

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

/* Prototypes des fonctions utilisées */
extern SDL_Surface *loadImage(char *name);

     Maintenant que tout est prêt, il nous faut mettre à jour la fonction draw() dans le fichier du même nom, en remplaçant drawplayer() par notre nouvelle fonction drawanimatedplayer() :

Nom du fichier : draw.c


 void draw(void)
{

    /* Affiche le fond (background) aux coordonnées (0,0) */
    drawImage(map.background, 0, 0);

    /* Affiche la map de tiles */
    drawMap();

    /* Affiche le joueur */
    drawanimatedplayer();

    /* Affiche l'écran */
    SDL_Flip(jeu.screen);

    /* Delai */

    SDL_Delay(1);

}

 

    Sans oublier de mettre à jour aussi le prototype dans draw.h :

Nom du fichier : draw.h


  #include "structs.h"

 /* Prototypes des fonctions utilisées */
  extern void drawMap(void);
  extern void drawanimatedplayer(void);

/* Structures globales */
  extern Gestion jeu;
  extern Map map;

 

 
    Eh voilà, si vous compilez puis lancez le programme, vous verrez notre super lapin ninja s'afficher et se mettre à marcher sur place !

      Et si on scrolle, **Magie**, notre lapin reste sagement au début du niveau !

   Bon, maintenant que vous savez qu'animer un sprite n'est pas aussi difficile que vous ne le pensiez (hein, avouez !?), on va pouvoir commencer à en prendre le contrôle dans un prochain chapitre !  


   


   





 

 

 

Connexion

CoalaWeb Traffic

Today146
Yesterday297
This week943
This month4617
Total1743824

26/04/24