Réaliser un RPG en C avec SDL et FMOD/L'éditeur de carte
Réaliser un RPG en C avec SDL et FMOD
<< Précédent | Sommaire | Suivant >>
Difficultée estimée de cette étape: moyen
Pré-requis:
Le moment est venu de nous intéresser à une tâche annexe du développement d'un RPG, la création d'un éditeur de cartes.
De nombreux outils très performants existent, le but n'est pas ici de les concurrencer mais de réutiliser ce qui est déjà fait afin de pouvoir concevoir les cartes que nous souhaitons le plus rapidement possible.
Ainsi, nous allons repartir du code de la démo 1 pour développer notre éditeur de cartes.
Le but est d'arriver au résultat que l'on peut voir à droite:
- à gauche la liste des tuiles que l'on peut utiliser
- à droite la carte sur laquelle on travaille
Le travail est donc divisé en deux parties:
- la gestion de la liste des tuiles à gauche (avec la gestion du défilement). Ceci concerne HandleMap() principalement.
- la gestion des clics et des actions spéciales que l'on peut effectuer sur la carte. Ces modifications affecteront MapEvent().
[modifier] Scission du projet
Créez un nouveau répertoire pour votre éditeur de carte et collez le contenu de la démo 1 à l'intérieur.
Ouvrez le projet avec Dev C++ et changez le titre de notre application dans les options du projet (Projet/Options du projet et changez le contenu de la zone de texte Nom).
[modifier] Changements dans le type struct t_map
struct s_map { unsigned int n_tile; struct s_chipset *chipset; SDL_Surface *surf_chipset; unsigned int width; unsigned int height; unsigned int *data; unsigned int offsetX; unsigned int offsetY; unsigned int chipsetOffsetY; /* NOUVEAU */ unsigned int tile_sel; /* NOUVEAU */ FMUSIC_MODULE *bg_sound; };
- chipsetOffsetY va gérer le scrolling des tiles.
- tile_sel contient le numéro du tile sélectionné.
Profitons-en pour définir une taille pour la bordure entre la carte et la listes des tuiles (à insérer dans map.h):
# define BORDER_SIZE 20
[modifier] Changements dans InitMap()
Comme nous avons rajouté deux nouveaux éléments, il ne faut pas oublier des les initialiser!
void InitMap(struct s_map *map) { map->n_tile=map->width=map->height=map->offsetX=map->offsetY=map->chipsetOffsetY=0; map->tile_sel=0; map->chipset=NULL; map->surf_chipset=NULL; map->bg_sound=NULL; }
[modifier] Changements dans RandomizeMap()
RandomizeMap va simplement servir à initialiser les cartes lors de leur création. Pour simplifier l'utilisation de l'éditeur, nous changeons simplement le code afin de rendre les tiles non-traversables par défaut.
void RandomizeMap(struct s_map *map, int width, int height, char *chipset) { int i,x,y; InitMap(map); map->surf_chipset=SDL_LoadBMP(chipset); SDL_SetColorKey(map->surf_chipset, SDL_SRCCOLORKEY, SDL_MapRGB(map->surf_chipset->format,8,33,82)); map->n_tile=(map->surf_chipset->w/TILE_WIDTH)*(map->surf_chipset->h/TILE_HEIGHT); map->chipset=calloc((map->n_tile),sizeof(struct s_chipset)); x=y=0; for(i=0;i<map->n_tile;i++) { map->chipset[i].x=x; map->chipset[i].y=y; map->chipset[i].collision=1; /* LE SEUL CHANGEMENT */ map->chipset[i].nextTile=0; x += TILE_WIDTH; if(x>(map->surf_chipset->w-TILE_WIDTH)) { x=0; y += TILE_HEIGHT; } } map->width = width; map->height = height; map->data = malloc(map->width*map->height*sizeof(unsigned int)); for(x=0;x<map->width;x++) for(y=0;y<map->height;y++) map->data[x+y*map->width]=rand()%(map->n_tile); }
[modifier] Changements dans HandleMap()
Les gros changements sont réalisés dans cette partie: tout d'abord nous avons un paramètre supplémentaire left qui indique à partir d'où la carte va être affichée. En effet, nous avons besoin d'espace à gauche pour afficher la liste des tuiles.
void HandleMap(struct s_map *map, int left) { int x,y, tx,ty, i; SDL_Rect src, dest; SDL_FillRect(SDL_GetVideoSurface(), NULL, 0); x=-(map->offsetX%TILE_WIDTH)+left; /* CHANGEMENT */ y=-(map->offsetY%TILE_HEIGHT); tx=map->offsetX/TILE_WIDTH; ty=map->offsetY/TILE_HEIGHT; while(y<600) { x=-(map->offsetX%TILE_WIDTH)+left; /* CHANGEMENT */ tx=map->offsetX/TILE_WIDTH; while(x<800) { src.x=map->chipset[GET_TILE(tx,ty,map)].x; src.y=map->chipset[GET_TILE(tx,ty,map)].y; dest.x=x; dest.y=y; src.w=dest.w=TILE_WIDTH; src.h=dest.h=TILE_HEIGHT; if(dest.x<left) { /* CHANGEMENT */ src.x-=dest.x-left; src.w+=dest.x-left; dest.w+=dest.x-left; dest.x=left; } if(dest.y<0) { src.y-=dest.y; src.h+=dest.y; dest.h+=dest.h; dest.y=0; } SDL_BlitSurface(map->surf_chipset,&src,SDL_GetVideoSurface(),&dest); x+=TILE_WIDTH; tx++; } y+=TILE_HEIGHT; ty++; } /* AFFICHAGE DU CHIPSET - NOUVEAU */ x=1; y=1; for(i=map->chipsetOffsetY;i<map->n_tile;i++) { src.x=map->chipset[i].x; src.y=map->chipset[i].y; dest.x=x; dest.y=y; src.w=dest.w=TILE_WIDTH; src.h=dest.h=TILE_HEIGHT; if(i==map->tile_sel) { dest.x--; dest.y--; dest.w+=2; dest.h+=2; SDL_FillRect(SDL_GetVideoSurface(), &dest, (map->chipset[i].collision==0)? SDL_MapRGB(SDL_GetVideoSurface()->format, 0,255,0) : SDL_MapRGB(SDL_GetVideoSurface()->format, 255,0,0)); dest.x++; dest.y++; dest.w-=2; dest.h-=2; } SDL_BlitSurface(map->surf_chipset,&src,SDL_GetVideoSurface(),&dest); dest.x+=TILE_WIDTH-2; dest.y+=TILE_HEIGHT-2; dest.w=dest.h=2; SDL_FillRect(SDL_GetVideoSurface(), &dest, (map->chipset[i].collision==0)? SDL_MapRGB(SDL_GetVideoSurface()->format, 0,255,0) : SDL_MapRGB(SDL_GetVideoSurface()->format, 255,0,0)); x += TILE_WIDTH+1; if(x>(left-TILE_WIDTH-BORDER_SIZE)) { x=1; y+=TILE_HEIGHT+1; } } }
Les changements signalés au début du code sont mineurs et ne sont là que pour gérer le paramètre left mais la seconde partie de la fonction qui est totalement nouvelle sert à afficher les tuiles dans la partie gauche de la fenêtre.
De plus, on affiche 4 pixels de couleur, en bas à droite de chaque tuile, afin de spécifier si la tuile est traversable ou pas:
- vert: traversable
- rouge: non-traversable
[modifier] Changements dans HandleEvent()
La fonction qui gère les événements a également été très largement modifiée.
Nous gérons en plus:
- les clics gauche:
- souris dans la zone des tuiles: on sélectionne une autre tuile
- souris sur la carte: on change la tuile dans la carte
- les clics droits:
- souris dans la zone des tuiles: changement tuile traversable/non-traversable
- le clavier:
- f: remplit la carte avec la tuile sélectionnée
- l: charge la carte
- s: sauvegarde la carte
- flèches directionnelles: fait défiler la carte (déjà implémenté dans l'étape 3)
De plus, si l'on laisse CTRL enfoncé pendant que l'on change une tuile dans la carte, les 4 tuiles en haut, à droite de la souris seront modifiés. Si l'on laisse ALT enfoncé, ce sont les 9 tuiles tout autour de la tuile sélectionnée qui sont modifiés.
void MapEvent(SDL_Event *event, struct s_map *map, int left) { FILE *f; int i,j, ntileparcol; if(!event) return; /* nombre de tile dans chaque colonne... dans la démo: 5 */ ntileparcol=(left-BORDER_SIZE)/(TILE_WIDTH+1); switch(event->type) { case SDL_KEYDOWN: if(event->key.state==SDL_PRESSED) { if(event->key.keysym.sym==SDLK_RIGHT) { if (map->offsetX<(map->width*TILE_WIDTH-800)) map->offsetX++; } else if(event->key.keysym.sym==SDLK_LEFT) { if (map->offsetX!=0) map->offsetX--; } else if(event->key.keysym.sym==SDLK_DOWN) { if (map->offsetY<(map->height*TILE_HEIGHT-600)) map->offsetY++; } else if(event->key.keysym.sym==SDLK_UP) { if (map->offsetY!=0) map->offsetY--; } /* CHIPSET */ else if(event->key.keysym.sym==SDLK_PAGEUP) { if (map->chipsetOffsetY>=ntileparcol) map->chipsetOffsetY-=ntileparcol; } else if(event->key.keysym.sym==SDLK_PAGEDOWN) { if (map->chipsetOffsetY<map->n_tile-ntileparcol) map->chipsetOffsetY+=ntileparcol; } /* FONCTION SPECIALES */ else if(event->key.keysym.sym==SDLK_f) /* FILL */ { for(i=0;i<map->width;i++) for(j=0;j<map->height;j++) GET_TILE(i,j,map)=map->tile_sel; } else if(event->key.keysym.sym==SDLK_s) /* SAVE */ { SaveMap(map, "map\\save.map", "musique\\Opening1.mid", "tiles\\mchip0.bmp"); } else if(event->key.keysym.sym==SDLK_l) /* LOAD */ { f=fopen("map\\save.map","r"); if(f) { fclose(f); FreeMap(map); InitMap(map); LoadMap(map, "map\\save.map"); } } } break; case SDL_MOUSEBUTTONDOWN: if(event->button.button==SDL_BUTTON_LEFT) { if(event->button.x<=(left-BORDER_SIZE)) { map->tile_sel=map->chipsetOffsetY +(event->button.x/(TILE_WIDTH+1)) +ntileparcol*(event->button.y/(TILE_HEIGHT+1)); if(map->tile_sel>=map->n_tile) map->tile_sel=map->n_tile-1; } else if(event->button.x>=left) { i=map->offsetX+event->button.x-left; j=map->offsetY+event->button.y; i /= TILE_WIDTH; j /= TILE_HEIGHT; if(i>=map->width) i=map->width-1; if(j>=map->height) j=map->height-1; GET_TILE(i,j,map)=map->tile_sel; /* on appuie sur CTRL */ if((SDL_GetModState() & KMOD_CTRL) || (SDL_GetModState() & KMOD_ALT)) { if(i<(map->width-1)) GET_TILE(i+1,j,map)=map->tile_sel; if(j>0) GET_TILE(i,j-1,map)=map->tile_sel; if((j>0) && (i<(map->width-1))) GET_TILE(i+1,j-1,map)=map->tile_sel; if(SDL_GetModState() & KMOD_ALT) { if(i>0) GET_TILE(i-1,j,map)=map->tile_sel; if(j<(map->height-1)) GET_TILE(i,j+1,map)=map->tile_sel; if((i>0) && (j<(map->height-1))) GET_TILE(i-1,j+1,map)=map->tile_sel; if((i>0) && (j>0)) GET_TILE(i-1,j-1,map)=map->tile_sel; if((j<(map->height-1)) && (i<(map->width-1))) GET_TILE(i+1,j+1,map)=map->tile_sel; } } } } else if(event->button.button==SDL_BUTTON_RIGHT) { if(event->button.x<=(left-BORDER_SIZE)) { i=map->chipsetOffsetY +(event->button.x/(TILE_WIDTH+1)) +ntileparcol*(event->button.y/(TILE_HEIGHT+1)); if(i>=map->n_tile) i=map->n_tile-1; map->chipset[i].collision=(map->chipset[i].collision+1)%2; } } break; } }
[modifier] Changements dans Main()
Très peu de changement dans cette partie, il faut simplement préciser le paramètre supplémentaire left. Ici nous le fixons à 185 (pour obtenir 5 colonnes).
La fonction SDL_EnableKeyRepeat() permet la répétition des touches (si l'on laisse la flèche droite appuyée, la carte défilera petit à petit, on n'a pas besoin de relâcher et de réappuyer sur celle-ci).
int main(int argc, char **argv) { SDL_Event event; struct s_map map; int isdone; if(Init()>0) return 1; RandomizeMap(&map,100,100,"tiles\\mchip0.bmp"); SDL_EnableKeyRepeat(100, 10); isdone=0; while (!isdone) { /* Lecture des évènements dans la queue d'évènements */ while (SDL_PollEvent (&event)) { MapEvent(&event, &map, 185); 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, 185); SDL_Flip(SDL_GetVideoSurface()); } FreeMap(&map); return 0; }
[modifier] Ce que sait faire et ne sait pas faire notre éditeur de carte
Notre but est de faire un éditeur de carte utilisable à moindre frais. C'est ce qui a été réalisé mais au prix de quelques sacrifices, ainsi vous devrez recompiler l'éditeur pour changer:
- la taille des cartes
- le chipset
- la musique de fond
Et l'éditeur de carte ne gère pas les tuiles animées.
[modifier] Démo 2: l'éditeur de cartes
- Télécharger la démo (version linux pour anjuta disponible)
- Voir les sources
Catégories: C | SDL | FMOD


