Réaliser un RPG en C avec SDL et FMOD/Les sprites et les collisions


Réaliser un RPG en C avec SDL et FMOD

<< Précédent | Sommaire | Suivant >>



Prérequis

Difficultée estimée de cette étape: moyenne


Pré-requis:

  • Connaissance moyenne du C (allocations dynamiques, pointeurs et gestion des entrées/sorties)
  • SDL (gestion des entrées, fonctions vidéo de base)


Notre jeu ne sera pas complet tant que nous n'aurons pas un héros qui se déplace sur notre carte. Evidemment, ses déplacements devront être contrôlés afin qu'il ne puisse pas traverser les maisons, arbres et autres décors... c'est ce que nous allons gérer dans cette étape.


Sommaire

[modifier] Les sprites

Les sprites sont des éléments graphiques qui se déplacent sur notre fond et qui sont le plus souvent animés.

Ainsi notre héros aura quatre mouvements:

  • marcher vers la gauche
  • marcher vers la droite
  • marcher vers le haut
  • marcher vers le bas

Tous ces mouvements sont créés en affichant une succession d'images fixes représentant chaque étape de l'action, tout comme dans un dessin animé.

Pour stocker le tout, nous utilisons une grande image que l'on appelle charset.


Notre charset: le héros du jeu est le troisième en haut.
Notre charset: le héros du jeu est le troisième en haut.


Pour gérer tous les sprites du jeu, nous allons avoir une librairie de sprites qui va contenir un tableau de tous les sprites. De plus, chaque sprite sera lui-même composé d'un tableau contenant toutes les animations. Chaque animation étant composée d'une liste de frame également stockés dans un tableau. Tous les tableaux sont alloués dynamiquement lors du chargement de la carte.

Pour résumer, notre type une fois alloué peut se schématiser de cette façon:

Schématisation du type gérant les sprites.
Schématisation du type gérant les sprites.


L'une des principales tâches de cette étape va être d'initialiser, charger, sauvegarder et libérer ses données. Il restera encore à gérer l'affichage proprement dit du sprite avec SDL et les collisions.

[modifier] Gestion des collisions

Le principe de détection des collisions est très simple:

  • on détermine sur quel(s) tile(s) est le sprite
  • on cherche dans le chipset si ce tile est traversable ou pas
  • si tous les tiles sont traversables, le mouvement est autorisé sinon il ne l'est pas.

Ce principe est amélioré en donnant de la marge à chaque sprite en indiquant un espacement dans lequel le sprite peut figurer dans un sprite non-traversables sans annuler le mouvement.

Le plus simple est d'illustrer cela avec un dessin:


Chaque marge définie à l'intérieur de notre sprite permet de créer un centre. Ce centre est le point critique en terme de collision: si cette zone est sur un tile non-traversable le mouvement est annulé.

Globalement, les marges sont définies de manière à créer une boîte qui englobe notre personnage au plus près.

Cependant, on peut remarquer que la marge haute est très importante. Via cette astuce, on va simuler un effet de profondeur, notre héros pourra monter très haut dans les tiles non-traversables et les recouvrir. Si le sprite est juste en-desous d'une caisse par exemple, on aura l'impression qu'il arrive réellement au pied du décor.

[modifier] Création des fichiers sprite.c et sprite.h

Besoin de réviser?

Besoin de réviser?


Ajoutez au projet deux nouveaux fichiers sprite.c et sprite.h. Comme d'habitude, insérez le code de départ de nos fichiers:

sprite.c

/*
** sprite.c
**
** Gestion des sprites
**
** Rôle:
** - initialisation et libération des sprites.
** - affichage des sprites
** - gestion des événements associés au sprites
*/
 
# include "sprite.h"


sprite.h

/*
** sprite.h
**
** Gestion des sprites
**
** Rôle:
** - initialisation et libération des sprites.
** - affichage des sprites
** - gestion des événements associés au sprites
*/
 
#ifndef SPRITE_H
# define SPRITE_H
 
# include <stdlib.h>
# include <SDL/sdl.h>
 
# include "map.h"
 
#endif /* !SPRITE_H_ */


[modifier] Implémentation des types


Nous devons tout d'abord implémenter les types décris plus haut dans sprite.h.

# define ANIMATION_HAUT   0
# define ANIMATION_DROITE 1
# define ANIMATION_GAUCHE 3
# define ANIMATION_BAS    2
 
 
 
struct s_lib_sprite
{
   unsigned int n_sprite;    /* nombre de sprites chargés */
   struct s_sprite *sprites; /* pointeur vers les sprites */
};
 
struct s_frame
{
   unsigned int x,y;         /* coordonnée de l'animation dans la surface */
};
 
struct s_anim
{
   unsigned int n_frame;     /* nombre de frames dans l'animation */
   unsigned int cur_frame;   /* numéro de la frame actuellement affichée */
   unsigned int lastfrmupdate; /* stocke le moment où l'animation est passée à la frame actuelle */
   unsigned int delaybetweenframes; /* délai entre deux frames */
   struct s_frame *frame;    /* pointeur vers les frames */
};
 
struct s_sprite
{
   unsigned int x,y;        /* coordonnées du sprite dans la carte */
 
   unsigned int width, height; /* taille du sprite */
 
   unsigned int n_anim;     /* nombre d'animations pour ce sprite */
   unsigned int cur_anim;   /* n° de l'animation courante */
   unsigned char ispaused_anim; /* booléen indiquant si l'animation est en pause */
   struct s_anim *anim;     /* pointeur vers les animations */
 
   unsigned int offsetG, offsetD, offsetH, offsetB; /* marges du sprite */
 
   SDL_Surface *surf;      /* pointeur vers la surface contenant le charset */
};


[modifier] Initialisation et libération des sprites

Besoin de réviser?

Besoin de réviser?


Attention: nous repartons du code de l'étape n°2, l'éditeur de carte est un projet à part entière.

La fonction d'initialisation de la bibliothèque de sprite met le nombre de sprite à 0 et le pointeur à NULL.

void InitSprite(struct s_lib_sprite *libsprite)
{
   libsprite->n_sprite=0;
   libsprite->sprites=NULL;
}

La fonction de libération parcours la structure en libérant tous les éléments alloués:

void FreeSprite(struct s_lib_sprite *libsprite)
{
     unsigned int i, anim;
 
     for(i=0;i<libsprite->n_sprite;i++)
     {
        SDL_FreeSurface(libsprite->sprites[i].surf);        
 
        for(anim=0;anim<libsprite->sprites[i].n_anim;anim++)
           free(libsprite->sprites[i].anim[anim].frame);
        free(libsprite->sprites[i].anim);
     }
     free(libsprite->sprites);
     libsprite->n_sprite=0;   
}


[modifier] Chargement statique de données

Nous allons créer une fonction qui va remplir la structure à la main. Quel est l'intérêt? En modifiant cette fonction, vous générez absolument n'importe quel fichier sprite sans avoir besoin de développer un éditeur graphique ce qui est coûteux en temps et relativement inutile, à part dans le cadre d'équipes de développements importantes.

J'en profite pour signaler une notation qui sera gardée dans tout le projet à partir de maintenant: le sprite n°0 représente le joueur (le sprite que l'on déplace avec le clavier).

void SpriteStaticData(struct s_lib_sprite *libsprite)
{
     libsprite->n_sprite = 1;
     libsprite->sprites=malloc(libsprite->n_sprite*sizeof(struct s_sprite));
 
     /* SPRITE 0: player */
     libsprite->sprites[0].x=256;
     libsprite->sprites[0].y=86;
 
     libsprite->sprites[0].offsetG=4;
     libsprite->sprites[0].offsetD=15;
     libsprite->sprites[0].offsetH=20;
     libsprite->sprites[0].offsetB=10;
 
     libsprite->sprites[0].surf=SDL_LoadBMP("sprites\\player.bmp");
     SDL_SetColorKey(libsprite->sprites[0].surf, SDL_SRCCOLORKEY, 
                     SDL_MapRGB(libsprite->sprites[0].surf->format,0,0,0));
     libsprite->sprites[0].width=24;
     libsprite->sprites[0].height=32;
     libsprite->sprites[0].n_anim=4;
     libsprite->sprites[0].cur_anim=ANIMATION_BAS;
     libsprite->sprites[0].ispaused_anim=1;
     libsprite->sprites[0].anim=
        malloc(libsprite->sprites[0].n_anim*sizeof(struct s_anim));
 
     /* ANIM 0: haut */
     libsprite->sprites[0].anim[0].cur_frame=0;
     libsprite->sprites[0].anim[0].n_frame=3;
     libsprite->sprites[0].anim[0].lastfrmupdate=SDL_GetTicks();
     libsprite->sprites[0].anim[0].delaybetweenframes = 100;
     libsprite->sprites[0].anim[0].frame=
        malloc(libsprite->sprites[0].anim[0].n_frame*sizeof(struct s_frame));
 
     libsprite->sprites[0].anim[0].frame[0].x=144;
     libsprite->sprites[0].anim[0].frame[0].y=0;
 
     libsprite->sprites[0].anim[0].frame[1].x=168;
     libsprite->sprites[0].anim[0].frame[1].y=0;
 
     libsprite->sprites[0].anim[0].frame[2].x=192;
     libsprite->sprites[0].anim[0].frame[2].y=0;
 
     /* ANIM 1: droite */
     libsprite->sprites[0].anim[1].cur_frame=0;
     libsprite->sprites[0].anim[1].n_frame=3;
     libsprite->sprites[0].anim[1].lastfrmupdate=SDL_GetTicks();
     libsprite->sprites[0].anim[1].delaybetweenframes = 100;
     libsprite->sprites[0].anim[1].frame=
        malloc(libsprite->sprites[0].anim[1].n_frame*sizeof(struct s_frame));
 
     libsprite->sprites[0].anim[1].frame[0].x=144;
     libsprite->sprites[0].anim[1].frame[0].y=32;
 
     libsprite->sprites[0].anim[1].frame[1].x=168;
     libsprite->sprites[0].anim[1].frame[1].y=32;
 
     libsprite->sprites[0].anim[1].frame[2].x=192;
     libsprite->sprites[0].anim[1].frame[2].y=32;
 
     /* ANIM 2: gauche */
     libsprite->sprites[0].anim[2].cur_frame=0;
     libsprite->sprites[0].anim[2].n_frame=3;
     libsprite->sprites[0].anim[2].lastfrmupdate=SDL_GetTicks();
     libsprite->sprites[0].anim[2].delaybetweenframes = 100;
     libsprite->sprites[0].anim[2].frame=
        malloc(libsprite->sprites[0].anim[2].n_frame*sizeof(struct s_frame));
 
     libsprite->sprites[0].anim[2].frame[0].x=144;
     libsprite->sprites[0].anim[2].frame[0].y=64;
 
     libsprite->sprites[0].anim[2].frame[1].x=168;
     libsprite->sprites[0].anim[2].frame[1].y=64;
 
     libsprite->sprites[0].anim[2].frame[2].x=192;
     libsprite->sprites[0].anim[2].frame[2].y=64;
 
     /* ANIM 3: bas */
     libsprite->sprites[0].anim[3].cur_frame=0;
     libsprite->sprites[0].anim[3].n_frame=3;
     libsprite->sprites[0].anim[3].lastfrmupdate=SDL_GetTicks();
     libsprite->sprites[0].anim[3].delaybetweenframes = 100;
     libsprite->sprites[0].anim[3].frame=
        malloc(libsprite->sprites[0].anim[3].n_frame*sizeof(struct s_frame));
 
     libsprite->sprites[0].anim[3].frame[0].x=144;
     libsprite->sprites[0].anim[3].frame[0].y=96;
 
     libsprite->sprites[0].anim[3].frame[1].x=168;
     libsprite->sprites[0].anim[3].frame[1].y=96;
 
     libsprite->sprites[0].anim[3].frame[2].x=192;
     libsprite->sprites[0].anim[3].frame[2].y=96;
}


[modifier] Sauvegarde des sprites

La sauvegarde des sprites reprend le même schéma que la sauvegarde des cartes. On parcourt toute la structure en sauvegardant chaque membre.

void SaveSprite(struct s_lib_sprite *libsprite, char *charset, char *filename)
{
     unsigned int i, anim, frm;
     char buffer[256];
     FILE *f;
 
     if(!libsprite) return;
     f=fopen(filename,"wb");
     if(!f) return;
 
     fwrite(&libsprite->n_sprite, sizeof(unsigned int), 1, f);
     for(i=0;i<libsprite->n_sprite;i++)
     {
        fwrite(&libsprite->sprites[i].x, sizeof(unsigned int), 1, f);
        fwrite(&libsprite->sprites[i].y, sizeof(unsigned int), 1, f);
        fwrite(&libsprite->sprites[i].width, sizeof(unsigned int), 1, f);
        fwrite(&libsprite->sprites[i].height, sizeof(unsigned int), 1, f);
        fwrite(&libsprite->sprites[i].n_anim, sizeof(unsigned int), 1, f);
        fwrite(&libsprite->sprites[i].cur_anim, sizeof(unsigned int), 1, f);
        fwrite(&libsprite->sprites[i].ispaused_anim, sizeof(unsigned char), 1, f);
 
        fwrite(&libsprite->sprites[i].offsetG, sizeof(unsigned int), 1, f);
        fwrite(&libsprite->sprites[i].offsetD, sizeof(unsigned int), 1, f);
        fwrite(&libsprite->sprites[i].offsetH, sizeof(unsigned int), 1, f);
        fwrite(&libsprite->sprites[i].offsetB, sizeof(unsigned int), 1, f);
 
        memset(&buffer,0,sizeof(buffer));
        strcpy(buffer,charset);
        fwrite(&buffer,255,sizeof(char),f);
 
        for(anim=0;anim<libsprite->sprites[i].n_anim;anim++)
        {
           fwrite(&libsprite->sprites[i].anim[anim].n_frame, sizeof(unsigned int), 1, f);
           fwrite(&libsprite->sprites[i].anim[anim].cur_frame, sizeof(unsigned int), 1, f);
           fwrite(&libsprite->sprites[i].anim[anim].lastfrmupdate, sizeof(unsigned int), 1, f);
           fwrite(&libsprite->sprites[i].anim[anim].delaybetweenframes, sizeof(unsigned int), 1, f);
 
           for(frm=0;frm<libsprite->sprites[i].anim[anim].n_frame;frm++)
           {
              fwrite(&libsprite->sprites[i].anim[anim].frame[frm].x, sizeof(unsigned int), 1, f);
              fwrite(&libsprite->sprites[i].anim[anim].frame[frm].y, sizeof(unsigned int), 1, f);
           }
        }   
     }
 
     fclose(f);
}


[modifier] Chargement des sprites

Le chargement des sprites est très similaire à ce que nous avons déjà vu. Il s'agit de réaliser l'opération inverse que précédemment en allouant les tableaux.

void LoadSprite(struct s_lib_sprite *libsprite, char *filename)
{
     unsigned int i, anim, frm;
     char buffer[256];
     FILE *f;
 
     if(!libsprite) return;
     f=fopen(filename,"rb");
     if(!f) return;
 
     fread(&libsprite->n_sprite, sizeof(unsigned int), 1, f);
     libsprite->sprites=malloc(libsprite->n_sprite*sizeof(struct s_sprite));
     for(i=0;i<libsprite->n_sprite;i++)
     {
        fread(&libsprite->sprites[i].x, sizeof(unsigned int), 1, f);
        fread(&libsprite->sprites[i].y, sizeof(unsigned int), 1, f);
        fread(&libsprite->sprites[i].width, sizeof(unsigned int), 1, f);
        fread(&libsprite->sprites[i].height, sizeof(unsigned int), 1, f);
        fread(&libsprite->sprites[i].n_anim, sizeof(unsigned int), 1, f);
        fread(&libsprite->sprites[i].cur_anim, sizeof(unsigned int), 1, f);
        fread(&libsprite->sprites[i].ispaused_anim, sizeof(unsigned char), 1, f);
 
        fread(&libsprite->sprites[i].offsetG, sizeof(unsigned int), 1, f);
        fread(&libsprite->sprites[i].offsetD, sizeof(unsigned int), 1, f);
        fread(&libsprite->sprites[i].offsetH, sizeof(unsigned int), 1, f);
        fread(&libsprite->sprites[i].offsetB, sizeof(unsigned int), 1, f);
 
        memset(&buffer,0,sizeof(buffer));
        fread(&buffer,255,sizeof(char),f);
        libsprite->sprites[i].surf=SDL_LoadBMP(buffer);
        SDL_SetColorKey(libsprite->sprites[i].surf, SDL_SRCCOLORKEY, 
           SDL_MapRGB(libsprite->sprites[i].surf->format,0,0,0));
 
        libsprite->sprites[i].anim=malloc(libsprite->sprites[i].n_anim*sizeof(struct s_anim));
        for(anim=0;anim<libsprite->sprites[i].n_anim;anim++)
        {
           fread(&libsprite->sprites[i].anim[anim].n_frame, sizeof(unsigned int), 1, f);
           fread(&libsprite->sprites[i].anim[anim].cur_frame, sizeof(unsigned int), 1, f);
           fread(&libsprite->sprites[i].anim[anim].lastfrmupdate, sizeof(unsigned int), 1, f);
           fread(&libsprite->sprites[i].anim[anim].delaybetweenframes, sizeof(unsigned int), 1, f);
 
           libsprite->sprites[i].anim[anim].frame=
              malloc(libsprite->sprites[i].anim[anim].n_frame*sizeof(struct s_frame));
           for(frm=0;frm<libsprite->sprites[i].anim[anim].n_frame;frm++)
           {
              fread(&libsprite->sprites[i].anim[anim].frame[frm].x, sizeof(unsigned int), 1, f);
              fread(&libsprite->sprites[i].anim[anim].frame[frm].y, sizeof(unsigned int), 1, f);
           }
        }   
     }
 
     fclose(f);
}


[modifier] Affichage des sprites


L'affichage des sprites se décompose en deux fonctions:

  • HandleSprites qui gère tous les sprites.
  • HandleSprite qui gère un sprite en particulier.


La première étape dans la gestion de tous les sprites est de recentrer la carte sur le héros afin de toujours le voir quels que soient ses déplacements. Le problème est que si le héro se déplace près des bordes de la carte, on ne peut pas le centrer totalement sans sortir de la carte, il faut donc vérifier que le décalage de la carte reste acceptable.

Il ne reste plus qu'à appeler HandleSprite pour chaque sprite.

void HandleSprites(struct s_lib_sprite *libsprite, struct s_map *map)
{
     int i;
 
     /* centre la vue sur le joueur */
     if(libsprite->sprites[0].x<(800/2))
        map->offsetX=0;     
     else
     {
        map->offsetX=libsprite->sprites[0].x-(800/2);
        if(map->offsetX>map->width*TILE_WIDTH-800) map->offsetX=map->width*TILE_WIDTH-800;
     }
 
     if(libsprite->sprites[0].y<(600/2))
        map->offsetY=0;     
     else
     {
        map->offsetY=libsprite->sprites[0].y-(600/2);
        if(map->offsetY>map->height*TILE_HEIGHT-600) map->offsetY=map->height*TILE_HEIGHT-600;
     }
 
 
 
     for(i=0;i<libsprite->n_sprite;i++)
        HandleSprite(&libsprite->sprites[i], map);
}


La fonction HandleSprite commence par éventuellement mettre à jour l'animation en avançant d'une frame si:

  • l'animation est en train d'être jouée (ispaused_anim<tt> à 0)
  • le temps durant lequel la frame doit être jouée est dépassée <tt>SDL_GetTicks()-lastfrmupdate > delaybetweenframes)

Si une mise à jour est nécessaire, on avance d'une frame (en utilisant un modulo pour faire revenir à zéro le compteur après la dernière frame) et on mets à jour le timer du sprite.

Ensuite, on passe au code qui permet d'afficher à l'écran le sprite et qui est très ressemblant à ce que l'on a pu voir dans la gestion de la carte.

On effectue enfin les vérifications pour redimensionner le rectangle si la surface à copier est partiellement en dehors de l'écran.

void HandleSprite(struct s_sprite *sprite, struct s_map *map)
{
     SDL_Rect src,dest;
 
     if(!sprite->ispaused_anim)
        if(SDL_GetTicks()-sprite->anim[sprite->cur_anim].lastfrmupdate > 
              sprite->anim[sprite->cur_anim].delaybetweenframes)
        {
           sprite->anim[sprite->cur_anim].cur_frame =
              (sprite->anim[sprite->cur_anim].cur_frame+1)%(sprite->anim[sprite->cur_anim].n_frame);
           sprite->anim[sprite->cur_anim].lastfrmupdate=SDL_GetTicks();
        }
 
     src.x = sprite->anim[sprite->cur_anim].frame[sprite->anim[sprite->cur_anim].cur_frame].x;
     src.y = sprite->anim[sprite->cur_anim].frame[sprite->anim[sprite->cur_anim].cur_frame].y;
     src.w = sprite->width;
     src.h = sprite->height;
 
     dest.x = sprite->x-map->offsetX;
     dest.y = sprite->y-map->offsetY;
     dest.w = sprite->width;
     dest.h = sprite->height;
 
     /* teste si le sprite est à l'écran... si ce n'est pas le cas, inutile de continuer! */
     if(dest.x<-dest.w) return;
     if(dest.y<-dest.h) return;
 
     if(dest.x<0)
     {
        src.x-=dest.x;
        src.w+=dest.x;
        dest.w+=dest.x;
        dest.x=0;
     }
     if(dest.y<0)
     {
        src.y-=dest.y;
        src.h+=dest.y;
        dest.h+=dest.h;
        dest.y=0;
     }
 
     SDL_BlitSurface(sprite->surf,&src,SDL_GetVideoSurface(),&dest);
}

[modifier] Changer l'animation d'un sprite

Il est intéressant de créer une petite fonction qui remet à jour le sprite à chaque changement d'animation. Cette fonction met à jour:

  • l'animation en cours
  • la valeur du booléen testant si l'animation est en train d'être jouée
  • met à 0 le numéro de la frame actuelle
  • met à jour le timer
void SpriteChangeAnim(struct s_sprite *sprite, unsigned int anim, unsigned char ispaused)
{
   sprite->cur_anim=anim;
   sprite->ispaused_anim=ispaused;
   sprite->anim[sprite->cur_anim].cur_frame=0;
   sprite->anim[sprite->cur_anim].lastfrmupdate=SDL_GetTicks();
}


[modifier] Déplacement d'un sprite et gestion des collisions


La fonction qui permet de déplacer le sprite intègre la détection de collision détaillée plus haut. On va calculer sur quel tile est situé chaque coin du sprite (coin haut gauche, haut droit, bas gauche, bas droit). Ces coins ne sont pas les coins réels dans la mesure où ils prennent en compte les marges définies pour chaque côté.

On teste si chacun des tiles sur lequel est positionné le sprite est traversable ou pas, s'ils sont tous non-traversables alors on effectue le déplacement en vérifiant tout de même qu'il ne sort pas de la carte.

void SpriteMove(struct s_sprite *sprite, unsigned int x, unsigned int y, struct s_map *map)
{
     unsigned int hgx,hgy, hdx,hdy, bgx,bgy, bdx,bdy;
 
     /* calcul du tile sur lequel est situé chaque bord du sprite */
     hgx = (x+sprite->offsetG)/TILE_WIDTH;
     hgy = (y+sprite->offsetH)/TILE_HEIGHT;
 
     hdx = (x+sprite->offsetG+TILE_WIDTH-sprite->offsetD)/TILE_WIDTH;
     hdy = (y+sprite->offsetH)/TILE_HEIGHT;
 
     bgx = (x+sprite->offsetG)/TILE_WIDTH;
     bgy = (y+sprite->offsetH+TILE_HEIGHT-sprite->offsetB)/TILE_HEIGHT;
 
     bdx = (x+sprite->offsetG+TILE_WIDTH-sprite->offsetD)/TILE_WIDTH;
     bdy = (y+sprite->offsetH+TILE_HEIGHT-sprite->offsetB)/TILE_HEIGHT;
 
     if (  (map->chipset[GET_TILE(hgx,hgy,map)].collision==0)
        && (map->chipset[GET_TILE(hdx,hdy,map)].collision==0)
        && (map->chipset[GET_TILE(bgx,bgy,map)].collision==0)
        && (map->chipset[GET_TILE(bdx,bdy,map)].collision==0) )
     {
        if(x>map->width*TILE_WIDTH-sprite->width) x=map->width*TILE_WIDTH-sprite->width;
        if(y>map->height*TILE_HEIGHT-sprite->height) y=map->height*TILE_HEIGHT-sprite->height;
 
        sprite->x=x;
        sprite->y=y;
     }
}


[modifier] Gestion des événements


La gestion des événement consiste à déplacer le sprite et à changer son animation selon la touche appuyée.

void SpriteEvent(SDL_Event *event, struct s_map *map, struct s_lib_sprite *libsprite)
{
     if(!event) return;
 
     libsprite->sprites[0].ispaused_anim=1;
 
     switch(event->type)
     {
      case SDL_KEYDOWN:
	    if(event->key.state==SDL_PRESSED)
	    {
          if(event->key.keysym.sym==SDLK_RIGHT)
          {
             SpriteMove(
                &libsprite->sprites[0],
                libsprite->sprites[0].x+1,
                libsprite->sprites[0].y,
                map);
             if(libsprite->sprites[0].cur_anim!=ANIMATION_DROITE)
                SpriteChangeAnim(
                &libsprite->sprites[0],
                ANIMATION_DROITE, 0);
             else
                libsprite->sprites[0].ispaused_anim=0;
          }
          else if(event->key.keysym.sym==SDLK_LEFT)
          {
             if(libsprite->sprites[0].x>0)
                SpriteMove(
                &libsprite->sprites[0],
                libsprite->sprites[0].x-1,
                libsprite->sprites[0].y,
                map);
             if(libsprite->sprites[0].cur_anim!=ANIMATION_GAUCHE)
                SpriteChangeAnim(
                &libsprite->sprites[0],
                ANIMATION_GAUCHE, 0);
             else
                libsprite->sprites[0].ispaused_anim=0;
          }
          else if(event->key.keysym.sym==SDLK_DOWN)
          {
             SpriteMove(
                &libsprite->sprites[0],
                libsprite->sprites[0].x,
                libsprite->sprites[0].y+1,
                map);
             if(libsprite->sprites[0].cur_anim!=ANIMATION_BAS)
                SpriteChangeAnim(
                &libsprite->sprites[0],
                ANIMATION_BAS, 0);
             else
                libsprite->sprites[0].ispaused_anim=0;
          }
          else if(event->key.keysym.sym==SDLK_UP)
          {
             if(libsprite->sprites[0].y>0)
                SpriteMove(
                &libsprite->sprites[0],
                libsprite->sprites[0].x,
                libsprite->sprites[0].y-1,
                map);
             if(libsprite->sprites[0].cur_anim!=ANIMATION_HAUT)
                SpriteChangeAnim(
                &libsprite->sprites[0],
                ANIMATION_HAUT, 0);
             else
                libsprite->sprites[0].ispaused_anim=0;
          }
        }
      break;
     }
}

Bien évidemment, n'oubliez pas de supprimer le comportement de la démo précédente en supprimant le contenu de MapEvent() (mais gardons la fonction, elle nous reservira peut-être plus tard).

void MapEvent(SDL_Event *event, struct s_map *map)
{
}


[modifier] Modification de la fonction main()

La fonction main() n'est pas réellement changée, il faut simplement penser à:

  • initialiser les sprites (InitSprite())
  • charger les sprites (LoadSprite())
  • à gérer les événements associés au sprite en appelant régulièrement SpriteEvent()
  • à mettre à jour les sprites régulièrement (HandleSprites())


int main(int argc, char **argv)
{
  SDL_Event event;
  struct s_map map;
  struct s_lib_sprite libsprite;
  int isdone;
 
  if(Init()>0)
     return 1;
 
  InitSprite(&libsprite);
  LoadSprite(&libsprite,"map\\01.sprite");
  LoadMap(&map, "map\\01.map");
 
  SDL_EnableKeyRepeat(10,10);
 
  isdone=0;
  while (!isdone)
  {
    /* Lecture des évènements dans la queue d'évènements */
    while (SDL_PollEvent (&event))
    {
      MapEvent(&event, &map);
      SpriteEvent(&event, &map, &libsprite);
 
      switch (event.type)
      {
      case SDL_KEYDOWN:
	    if(event.key.state==SDL_PRESSED)
	    {
          if(event.key.keysym.sym==SDLK_ESCAPE)
	        isdone = 1;
	      else if (event.key.keysym.sym==SDLK_F1)
	        SDL_SaveBMP(SDL_GetVideoSurface(),"screenshot.bmp");
 
	   }
      break;
      case SDL_QUIT:
        isdone = 1;
	  break;
      }
    }
 
    HandleMap(&map);
    HandleSprites(&libsprite, &map);
 
    SDL_Flip(SDL_GetVideoSurface());
  }
 
  FreeMap(&map);
  FreeSprite(&libsprite);
  return 0;
}

[modifier] Démo 3: Sprites et collision

Démo 3: sprites et collision

Cette nouvelle démo est dotée d'une carte plus sympathique réalisée avec l'éditeur de carte construit durant l'étape précédente.