Le moment est venu de dresser le décor en utilisant notre tileset :
Chaque tile est défini par une frame dans le tableau TILESET_DATA
. On a 4 tiles, donc 4 frames, que l’on peut représenter par un indice compris entre 0
et 3
. À la différence du sprite, les frames du tileset ne sont pas destinées à être animées. Au contraire, on va avoir besoin de désigner précisément quelle frame afficher à tel endroit sur l’écran.
C’est la raison pour laquelle nous avions défini la propriété frame_loop
avec la valeur 0
:
assets/rgb565.h
const uint16_t TILESET_DATA[] = {
// metadata
16, // frame width
8, // frame height
4, // frames
0, // frame loop
0xf81f, // transparent color
0, // 16-bits color mode
// colormap
Ceci permet de désactiver le processus automatique d’animation géré par gb.display
.
Pour afficher une frame en particulier, nous allons utiliser la méthode setFrame()
de la classe Image
:
void setFrame(uint16_t frame);
Par exemple, pour tapisser tout l’écran avec les tiles d’indice 0
et 2
, on peut faire la chose suivante :
examples/example-10.h
#include <Gamebuino-Meta.h>
#include "../assets/rgb565.h"
const uint8_t SCREEN_WIDTH = 80;
const uint8_t SCREEN_HEIGHT = 64;
const uint8_t TILE_WIDTH = TILESET_DATA[0];
const uint8_t TILE_HEIGHT = TILESET_DATA[1];
const uint8_t TILES_WIDE = SCREEN_WIDTH / TILE_WIDTH;
const uint8_t TILES_HIGH = SCREEN_HEIGHT / TILE_HEIGHT;
Image tileset(TILESET_DATA);
void setup() {
gb.begin();
gb.setFrameRate(32);
}
void loop() {
gb.waitForUpdate();
gb.display.clear();
for (uint8_t j=0; j<TILES_HIGH; ++j) {
for (uint8_t i=0; i<TILES_WIDE; ++i) {
tileset.setFrame(2*(j & 0x1));
gb.display.drawImage(
i*TILE_WIDTH, // x
j*TILE_HEIGHT, // y
tileset // image
);
}
}
}
On détermine le nombre de tiles à afficher en fonction de leurs dimensions horizontale et verticale :
TILES_WIDE
TILES_HIGH
const uint8_t TILE_WIDTH = TILESET_DATA[0];
const uint8_t TILE_HEIGHT = TILESET_DATA[1];
const uint8_t TILES_WIDE = SCREEN_WIDTH / TILE_WIDTH;
const uint8_t TILES_HIGH = SCREEN_HEIGHT / TILE_HEIGHT;
Cela revient à découper l’écran sous la forme d’une grille dont les cellules sont les tiles :
Puis on instancie en mémoire une image contenant notre tileset :
Image tileset(TILESET_DATA);
Ensuite, il ne reste plus qu’à parcourir chaque cellule de la grille :
// the tile grid is crossed in height:
for (uint8_t j=0; j<TILES_HIGH; ++j) {
// and in width:
for (uint8_t i=0; i<TILES_WIDE; ++i) {
// ...
}
}
Et pour chaque coordonnée (i,j)
sur la grille de tiles, on détermine les coordonnées (x,y)
de l’image à afficher à l’écran. Mais juste avant de l’afficher, on sélectionne la bonne frame dans le tileset :
tileset.setFrame(2*(j & 0x1));
gb.display.drawImage(
i*TILE_WIDTH, // x
j*TILE_HEIGHT, // y
tileset // image
);
Si le numéro de ligne j
est pair, on sélectionne la frame d’indice 0
, et si j
est impair, on sélectionne la frame d’indice 2
:
Ici le tilemap, autrement dit la cartographie de l’écran, est obtenu par une procédure de calcul au moment de l’exécution. Ce procédé peut être utile dans certains cas, mais on a coûtume d’établir cette cartographie dans un tableau de données, en indiquant explicitement quelle frame afficher sur quelle cellule de la grille :
const uint8_t TILEMAP[] = {
0, 0, 0, 0, 0,
2, 2, 2, 2, 2,
0, 0, 0, 0, 0,
2, 2, 2, 2, 2,
0, 0, 0, 0, 0,
2, 2, 2, 2, 2,
1, 1, 1, 1, 1,
3, 3, 3, 3, 3
};
Le tableau TILEMAP
établit tout simplement le plan d’agencement des tiles à l’écran.
Intégrons ce nouveau procédé dans notre code :
examples/example-11.h
#include <Gamebuino-Meta.h>
#include "../assets/rgb565.h"
const uint8_t SCREEN_WIDTH = 80;
const uint8_t SCREEN_HEIGHT = 64;
const uint8_t TILE_WIDTH = TILESET_DATA[0];
const uint8_t TILE_HEIGHT = TILESET_DATA[1];
const uint8_t TILES_WIDE = SCREEN_WIDTH / TILE_WIDTH;
const uint8_t TILES_HIGH = SCREEN_HEIGHT / TILE_HEIGHT;
const uint8_t TILEMAP[] = {
0, 0, 0, 0, 0,
2, 2, 2, 2, 2,
0, 0, 0, 0, 0,
2, 2, 2, 2, 2,
0, 0, 0, 0, 0,
2, 2, 2, 2, 2,
1, 1, 1, 1, 1,
3, 3, 3, 3, 3
};
Image tileset(TILESET_DATA);
void setup() {
gb.begin();
gb.setFrameRate(32);
}
void loop() {
gb.waitForUpdate();
gb.display.clear();
for (uint8_t j=0; j<TILES_HIGH; ++j) {
for (uint8_t i=0; i<TILES_WIDE; ++i) {
tileset.setFrame(TILEMAP[i + j * TILES_WIDE]);
gb.display.drawImage(
i*TILE_WIDTH, // x
j*TILE_HEIGHT, // y
tileset // image
);
}
}
}
Et on obtient la scène du jeu définie par le tableau TILEMAP
:
On n’a plus qu’à y intégrer notre avatar et examiner comment contrôler ses mouvements et les animations du sprite qui en découlent. C’est précisément ce qu’on va faire au chapitre suivant.