Tile Mapping : défilement automatique

Fvirtman

L'exemple précédent proposait de se déplacer dans un monde, avec scrolling manuel : c'est-à-dire que c'était à vous de faire défiler l'écran avec des touches.

Tout joueur que vous êtes, vous savez que dans les jeux, ça ne se passe pas comme ça, que ce n'est pas vous qui faites manuellement défiler l'écran. L'ordinateur le fait pour vous.

Nous allons voir dans ce chapitre comment faire en sorte que l'ordinateur le fasse pour vous, qu'il suive automatiquement votre personnage pour un meilleur confort de jeu.

Commentez

- Titre : Tile Mapping : défilement automatique

- Auteur : Fvirtman

- Parution : 21 novembre 2013

- Public visé :

- Licence : Le contenu de cet article est rédigé par Fvirtman et est mis à disposition selon les termes de la Licence Creative Commons Attribution 3.0 non transposé. Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright ® 2013 Developpez.com.

Sommaire

- Navigation

- II. Introduction

- III. Centre du monde !

- III-A. Quelques calculs

- III-B. Clamping

- IV. Défilement à sous-boîte limite

- IV-A. Calculs

- V. Code exemple

- Navigation

Navigation

Tutoriel précédent : insertion dans un monde de tuiles

Sommaire

II - Introduction

L'exemple précédent proposait de se déplacer dans un monde, avec scrolling manuel : c'est-à-dire que c'était à vous de faire défiler l'écran avec des touches.

Tout joueur que vous êtes, vous savez que dans les jeux, ça ne se passe pas comme ça, que ce n'est pas vous qui faites manuellement défiler l'écran. L'ordinateur le fait pour vous.

Nous allons voir dans ce chapitre comment faire en sorte que l'ordinateur le fasse pour vous, qu'il suive automatiquement votre personnage pour un meilleur confort de jeu.

Je proposerai plusieurs méthodes.

III - Centre du monde !

Si vous aimez être le centre du monde, vous allez aimer cette méthode !

Avec cette méthode, nous souhaitons que le joueur soit toujours bien au milieu.

Nous allons donc ajuster le fenêtrage du scrolling pour faire en sorte que notre joueur soit au milieu. Nous déplaçons librement notre joueur, à l'ordinateur de nous suivre.

Si nous regardons le dessin ci-dessous :

Nous voyons le fenêtrage de scrolling en rouge. Nous voyons notre personnage bien centré au milieu du cadre rouge.

Le point bleu, en haut à gauche, se situe aux coordonnées de nos paramètres xscroll et yscroll. Ce sont ces paramètres qu'il faut mettre à jour en fonction de la position de notre personnage.

C'est un peu de géométrie !

III-A - Quelques calculs

J'ai :

- xscroll, yscroll : coordonnées du point haut gauche de la fenêtre de scrolling :

- largeur_fenetre, hauteur_fenetre : largeur et hauteur de cette fenêtre :

- perso->x et perso->y : point haut gauche de mon perso (du rectangle vert) :

- perso->w et perso->h : largeur et hauteur du perso.

On veut que le centre de la fenêtre corresponde au centre du perso.

Le centre du perso, c'est :

x=perso&#8722;>x+perso&#8722;>w/2y=perso&#8722;>y+perso&#8722;>h/2

Le centre de la fenêtre c'est :

x=xscroll+largeurfenetre/2y=yscroll+hauteurfenetre/2

On veut que ces centres coïncident, donc :

perso&#8722;>x+perso&#8722;>w/2=xscroll+largeurfenetre/2perso&#8722;>y+perso&#8722;>h/2=yscroll+hauteurfenetre/2

On cherche xscroll et yscroll, on résout, on trouve :

xscroll=perso&#8722;>x+perso&#8722;>w/2&#8722;largeurfenetre/2yscroll=perso&#8722;>y+perso&#8722;>h/2&#8722;hauteurfenetre/2

Et voilà !

III-B - Clamping

Nous avons besoin également de prendre une précaution.

Imaginons que notre perso soit très à gauche du monde. Si nous centrons la fenêtre de scrolling autour, alors une partie de cette fenêtre sortira du monde.

Avec les fonctions présentées depuis le début du tutoriel, il ne faut pas que cette fenêtre sorte du monde. Il faut qu'elle soit bloquée entièrement dans le monde.

Dans le jargon, si on empêche la fenêtre de sortir du monde, on dit qu'on la restreint (« clamp »).

Si jamais la fonction de clamping modifie xscroll et yscroll pour la remettre dans l'écran, on aura alors le seul cas où notre personnage ne sera pas forcément au centre du monde, mais pourra aller se coller contre le bord. En effet, la fenêtre de scrolling s'arrête, mais vous, rien ne vous empêche de continuer à marcher jusqu'au bord, donc de voir réellement votre personnage se déplacer à droite dans votre fenêtre. Ceci se voit bien dans les jeux vidéos quand on approche du bord du stage, c'est le seul cas où on peut se coller au bord de l'écran.

Cette méthode était donc à présenter pour notre culture, mais ce n'est pas celle que nous utiliserons à terme. Essayez-la quand même dans le code d'en bas, pour voir.

IV - Défilement à sous-boîte limite

Dans la majorité des jeux de plate-forme, ou vu de dessus, le personnage peut se déplacer dans l'écran. Le scrolling ne se met à le suivre que s'il va « trop au bord ».

Voyons quelques schémas pour comprendre.

Nous reconnaissons le rectangle rouge : fenêtrage de scrolling, avec son point haut gauche qui est toujours xscroll et yscroll.

Nous définissons un rectangle jaune, à l'intérieur, que nous appellerons rectangle limite.

Le concept est simple : notre personnage peut se promener dans tout le rectangle jaune. Nous considérons son centre (du personnage). Si son centre est dans le rectangle limite, on ne scrolle pas. Par contre, s'il en sort, on défile de façon à le ramener dedans.

Il faudra alors calculer de combien on sort, en x et en y, et ajuster xscroll et yscroll de ces valeurs pour ramener notre personnage à la limite où il ne sort pas.

Avant de passer aux calculs, regardons notre dessin.

- Le cas A est un rectangle jaune à égale distance du rectangle rouge dans les quatre directions . La conséquence, c'est que dès que le personnage arrive près du bord, il y a défilement. Cependant, si un ennemi arrive, comme on est près du bord, on aura peu de temps pour le voir.

- Le cas B est meilleur pour les jeux de plate-forme où on avance à droite. Dès qu'on franchit le milieu, on défile. On voit ainsi bien les ennemis arriver. On peut revenir en arrière, et on ne défilera que si vraiment on va trop en arrière.

- Le cas C présente un rectangle réduit à une seule ligne. Conséquence, le personnage sera toujours au centre horizontalement, mais la caméra ne fera pas de bond avec le personnage au moment des sauts. Si on revient en arrière, la caméra suit immédiatement.

Vous pouvez ajuster votre rectangle limite comme vous le souhaitez pour avoir la meilleure ergonomie possible.

Notez que le cas limite où le rectangle est réduit à un seul point, au centre de l'écran, nous ramène dans le cas « centre du monde » ci-dessus.

IV-A - Calculs

Voici les données que nous avons :

- xcsroll, yscroll : coordonnées haut gauche du fenêtrage de scrolling (rectangle rouge) ;

- largeurfenetre, hauteurfenetre : largeur et hauteur de fenêtre de scrolling ;

- xlim,ylim : coordonnées relatives du point haut gauche du rectangle jaune par rapport à xscroll et yscroll : distance fixe entre ces deux points ;

- wlim, hlim : largeur et hauteur de la fenêtre jaune. Plus petite que la hauteur et largeur de la fenêtre rouge ;

- xperso, yperso : coordonnées haut gauche perso ;

- wperso, hperso : largeur et hauteur perso.

Nous disions que si le centre du perso sort, alors on fait évoluer le scrolling. Donc tout ce qui compte pour le perso, c'est son centre.

Ce centre est défini par (cxperso,cyperso) de la façon suivante :

cxperso=xperso+wperso/2cyperso=yperso+hperso/2

Calculons les limites, à un instant donné, à ne pas dépasser. Cette limite évolue avec le scrolling.

Les coordonnées xlimmin et ylimin représenteront le coin haut gauche de la limite, et xlimmax et ylimmax le coin bas droit, dans le grand monde.

Pour le coin haut gauche, une simple addition suffit :

xlimmin=xscroll+xlimylimmin=yscroll+ylim

Pour le coin bas droit : c'est le coin haut gauche auquel on ajoute la largeur et la hauteur de la fenêtre de limite :

xlimmax=xlimmin+wlimylimmax=ylimmin+hlim

Il suffit maintenant de vérifier si les coordonnées de notre centre ne sortent pas de ces limites. Et si c'est le cas, on ajuste. Il suffira de 4 if, un pour chacune des 4 directions.

if (cxperso<xlimmin)
xscroll -= (xlimmin-cxperso);
if (cyperso<ylimmin)
yscroll -= (ylimmin-cyperso);
if (cxperso>xlimmax)
xscroll += (cxperso-xlimmax);
if (cyperso>ylimmax)
yscroll += (cyperso-ylimmax);

Une fois cette opération faite, on aura la garantie que notre sprite se trouve bien dans la zone jaune.

Cependant, si jamais notre fenêtre de scrolling est calculée hors du monde, il faut la clamper comme on a vu au-dessus, et là, notre perso pourra donc se coller au bord.

V - Code exemple

Voici maintenant l'heure de voir le code qui illustre ce que nous venons de voir.

Ouvrez et compilez le projet « prog6 ».

Cet exemple ressemble beaucoup à celui de la partie précédente : vous allez déplacer votre personnage vert dans le même monde, à la différence près que la caméra vous suivra.

Utilisez donc uniquement les touches de direction du clavier pour tester cet exemple.

Nous avons vu deux types de scrolling, l'exemple illustre le second.

Nous avions vu que le premier cas pouvait être considéré à partir du premier cas avec un rectangle limite ramené à un point central.

Vous pourrez modifier les paramètres de la fonction FocusScrollBox appelée dans le main pour l'envisager.

Vous pouvez remarquer que j'ai modifié légèrement les fichiers fsprite.h et fmap.h par rapport à l'exemple précédent.

Dans la mesure ou Map aura besoin de Sprite, et Sprite aura besoin de Map, nous pouvons voir :

- en haut de fmap.h

typedef struct Ssprite Sprite;

- en haut de fsprite.h

typedef struct Smap Map;

Regardons le main, dans le fichier prog6.c :

int main(int argc,char** argv)
{
Sprite* perso;
SDL_Surface* screen;
Map* carte;
Input in;
InitEvents(&in);
SDL_Init(SDL_INIT_VIDEO);        // prépare SDL
screen = SDL_SetVideoMode(LARGEUR_FENETRE,HAUTEUR_FENETRE,32,SDL_HWSURFACE|SDL_DOUBLEBUF);
carte = ChargerMap("level2.txt",LARGEUR_FENETRE,HAUTEUR_FENETRE);
perso = InitialiserSprite(150,150,24,32,carte);
FocusScrollBox(carte,perso,200,150,400,300);
while(!in.key[SDLK_ESCAPE])
{
UpdateEvents(&in);
Evolue(&in,perso);
AfficherMap(carte,screen);
AfficherSprite(perso,screen);
SDL_Flip(screen);
SDL_Delay(5);
}
LibererMap(carte);
SDL_Quit();
return 0;
}

Comme dans l'exemple précédent, j'initialise une carte, puis un perso.

Ensuite, j'appelle la ligne suivante :

FocusScrollBox(carte,perso,200,150,400,300);

Cette ligne va simplement dire qu'à partir de maintenant, le scrolling sera automatique sur la carte autour du sprite perso avec un rectangle limite de 200,150,400,300 dans le repère de la fenêtre.

Une fois cette ligne posée, rien d'autre à changer dans le main.

Voyons maintenant la structure Map dans le fichier fmap.h :

struct Smap
{
int LARGEUR_TILE,HAUTEUR_TILE;
int nbtilesX,nbtilesY;
SDL_Surface* tileset;
TileProp* props;
tileindex** schema;
int nbtiles_largeur_monde,nbtiles_hauteur_monde;
int largeur_fenetre,hauteur_fenetre;
// scroll
int xscroll,yscroll;
Sprite* tofocus;
SDL_Rect rectlimitscroll;
};

Les nouveaux paramètres en bas sont un pointeur vers le sprite à focaliser, et le rectangle limite.

Tout ce que va faire la fonction FocusScrollBox, c'est remplir ces paramètres.

int FocusScrollBox(Map* m,Sprite* sp,int x,int y,int w,int h)
{
m->tofocus = sp;
m->rectlimitscroll.x = x;
m->rectlimitscroll.y = y;
m->rectlimitscroll.w = w;
m->rectlimitscroll.h = h;
return 0;
}

C'est finalement la fonction AfficherMap qui va se charger de mettre le scrolling à jour.

int AfficherMap(Map* m,SDL_Surface* screen)
{
int i,j;
SDL_Rect Rect_dest;
int numero_tile;
int minx,maxx,miny,maxy;
UpdateScroll(m);
minx = m->xscroll / m->LARGEUR_TILE-1;
miny = m->yscroll / m->HAUTEUR_TILE-1;
maxx = (m->xscroll + m->largeur_fenetre)/m->LARGEUR_TILE;
maxy = (m->yscroll + m->hauteur_fenetre)/m->HAUTEUR_TILE;
for(i=minx;i<=maxx;i++)
{
for(j=miny;j<=maxy;j++)
{
Rect_dest.x = i*m->LARGEUR_TILE - m->xscroll;
Rect_dest.y = j*m->HAUTEUR_TILE - m->yscroll;
if (i<0 || i>=m->nbtiles_largeur_monde || j<0 || j>=m->nbtiles_hauteur_monde)
numero_tile = 0;
else
numero_tile = m->schema[i][j];<code type="c"><code type="c">
SDL_BlitSurface(m->tileset,&(m->props[numero_tile].R),screen,&Rect_dest);
}
}
return 0;
}

Vous pouvez constater un appel à une fonction UpdateScroll, qui s'en occupera.

Dans cette fonction :

int UpdateScroll(Map* m)
{
int cxperso,cyperso,xlimmin,xlimmax,ylimmin,ylimmax;
if (m->tofocus==NULL)
return -1;
cxperso = m->tofocus->x + m->tofocus->w/2;
cyperso = m->tofocus->y + m->tofocus->h/2;
xlimmin = m->xscroll + m->rectlimitscroll.x;
ylimmin = m->yscroll + m->rectlimitscroll.y;
xlimmax = xlimmin + m->rectlimitscroll.w;
ylimmax = ylimmin + m->rectlimitscroll.h;
if (cxperso<xlimmin)
m->xscroll -= (xlimmin-cxperso);
if (cyperso<ylimmin)
m->yscroll -= (ylimmin-cyperso);
if (cxperso>xlimmax)
m->xscroll += (cxperso-xlimmax);
if (cyperso>ylimmax)
m->yscroll += (cyperso-ylimmax);
ClampScroll(m);
return 0;
}

Nous calculons les nouvelles positions de xscroll et yscroll en fonction du personnage et de la boîte limite, avec les formules vues ci-dessus.

Puis nous appelons la fonction ClampScroll :

void ClampScroll(Map* m)
{
if (m->xscroll<0)
m->xscroll=0;
if (m->yscroll<0)
m->yscroll=0;
if (m->xscroll>m->nbtiles_largeur_monde*m->LARGEUR_TILE-m->largeur_fenetre-1)
m->xscroll=m->nbtiles_largeur_monde*m->LARGEUR_TILE-m->largeur_fenetre-1;
if (m->yscroll>m->nbtiles_hauteur_monde*m->HAUTEUR_TILE-m->hauteur_fenetre-1)
m->yscroll=m->nbtiles_hauteur_monde*m->HAUTEUR_TILE-m->hauteur_fenetre-1;
}

Cette fonction envisage donc le cas où la fenêtre de scrolling dépasserait à l'extérieur du monde, et si c'est le cas, elle la remet dans le monde correctement : elle l'empêche de sortir.

Notre scrolling automatique est maintenant en place.

À l'utilisation, il suffira uniquement d'appeler la fonction FocusScrollBox une fois pour toutes et le scrolling sera automatique !

Plus besoin de se casser la tête, de mettre en place plein de lignes dans le main.

Un seul appel de FocusScrollBox, et le tour est joué !

A la fin de cette partie, les collisions avec le décor sont gérées, le scrolling est automatique, et tout cela avec peu d'appels de fonctions.

Si les techniques ont pu vous paraître compliquées, rappelez-vous qu'une fois en place, l'utilisation est simple.

Après tout, pour le moment, nous n'avons dans notre header fmap.h que six fonctions...

Ceci est la troisième version du tutoriel sur le TileMapping.

Gardez bien en tête que même si la technique vous paraît complexe, une fois les outils en place, l'utilisation, elle, ne l'est pas trop. Il suffit de regarder la taille du main pour le comprendre.

Voici ce qui sera prévu dans les parties suivantes :

- remplacer cet affreux carré vert par un personnage animé ;

- gérer un champ de gravité, pour pouvoir faire des sauts et avancer de façon plus réaliste ;

- faire un fond de stage qui défilera moins vite que les tiles.

Nous pourrons aussi voir :

- comment animer des tuiles (eau, lave qui bouge), comment avoir des tuiles semi-transparentes : une barrière par exemple qui laisserait apparaître le fond entre ses barreaux ;

- comment rajouter des ennemis, leur donner un comportement ;

- comment interagir avec le monde (casser des briques, prendre des bonus…) ;

- comment mettre des tuiles qui masquent en partie notre personnage : par exemple il pourra passer derrière un bosquet et nous ne verrons alors plus ses jambes.

Nous verrons cela avec le temps, en fonction de mes disponibilités, de vos commentaires, de vos demandes par message privé ou sur le topic officiel du tutoriel.

À bientôt pour la suite !

Navigation

Tutoriel précédent : insertion dans un monde de tuiles

Sommaire