Développement GameBoy #6 : La couche « Background »

On a vu dans les articles précédents comment encoder des images pour la GameBoy et qu'il y avait plusieurs couches permettant d'afficher ces images. Dans cet article il sera question de la couche Background. Si vous avez lu les deux précédents articles, vous avez déjà vu quelques exemples utilisant cette couche pour afficher des images, mais on va aller un peu plus dans les détails cette fois-ci.

La couche Background est probablement l'une des plus importantes : c'est elle que l'on utilise pour afficher le niveau (pensez à la carte sur laquelle on se déplace dans Pokemon, ou les plateformes dans Super Mario). Certains jeux n'utilisent même que cette couche-là.

Note

Cet article fait partie d'une série sur le développement GameBoy en C avec le compilateur SDCC et la bibliothèque gbdk-n. Cette série est toujours en cours et de nouveaux articles paraissent de temps à autre.

Articles de la série :

Pour rappel, cette couche est l'une des « trois » disponibles, et comme son nom l'indique, il s'agit de celle qui se trouve en dessous :

Position de la couche Background

La couche Background a pour dimensions 32×32 tuiles, ce qui est plus grand que ce que peut afficher l'écran de la GameBoy (qui a pour dimensions 20×18 tuiles). Seule une partie de la couche est donc visible à l'écran à un instant donné :

Comparaison de la taille de la couche Background avec celle de l'écran de la GameBoy

L'intérêt que la couche soit plus grande que l'écran, est qu'il est possible de la faire défiler (on parle de scrolling). Lorsque l'on arrive au bord de la couche et que l'on continue de la faire défiler, elle reboucle par le côté opposé :

Animation du défilement de la couche Background

C'est à peu près tout ce qu'il faut savoir sur la couche Background, on va maintenant voir comment on s'en sert.

APIs

GBDK nous fournit tout un ensemble de fonctions et de macros pour manipuler la couche Background, on va toutes les voir en détail dans cette section.

Afficher / masquer la couche Background

Pour commencer, on va parler des deux macros permettant d'afficher et de masquer la couche Background.

Par défaut, la couche est masquée, il faudra donc la rendre visible si on veut être en mesure de tester les exemples suivants :

SHOW_BKG;

Et on peut la masquer à nouveau via la macro suivante :

HIDE_BKG;

Copier des tiles dans la mémoire vidéo

Pour afficher des tuiles, il faut commencer par les copier dans la mémoire vidéo, cela se fait à l'aide de la fonction suivante :

void set_bkg_data(UINT8 first_tile, UINT8 nb_tiles, unsigned char *data);
  • first_tile : la case mémoire à partir de laquelle on va placer les tuiles (si on met 16 par exemple, la première tuile de notre tileset se trouvera dans la case 16, la seconde dans la case 17,... C'est ce numéro que l'on retrouvera dans la tilemap).
  • nb_tiles : nombre de tuiles à copier dans la mémoire vidéo.
  • *data : pointeur vers les données de nos tuiles.

Je ne remets pas d'exemple pour cette fonction, on en a déjà un de bien détaillé dans l'article « Développement GameBoy #4 : Afficher des images »

Afficher des tiles (et les relire)

Une fois les tuiles dans la mémoire vidéo de la GameBoy, il faut lui indiquer où les afficher. Pour ce faire, il faut lui fournir une tilemap.

Pour envoyer une tilemap dans la mémoire vidéo, il faut utiliser la fonction suivante :

void set_bkg_tiles(UINT8 x, UINT8 y, UINT8 w, UINT8 h, unsigned char *tiles);
  • x, y : Coordonnées de destination de notre tilemap (là où elle sera collée sur la couche Background). Les coordonnées sont exprimées en tuiles (entier entre 0 et 31).
  • w, h : Largeur et hauteur de notre tilemap, exprimées en tuiles (entier entre 0 et 31).
  • *tiles : pointeur vers les données de la tilemap.
Illustration de la copie d'une tilemap dans la couche Background

Cette fois encore, un exemple est disponible dans l'article « Développement GameBoy #4 : Afficher des images ».

En plus de nous permettre de coller des tilesmaps sur la couche Background, GBDK nous fournit une seconde fonction permettant de récupérer ces données (pour savoir à quoi ressemble la couche à un instant donné) :

void get_bkg_tiles(UINT8 x, UINT8 y, UINT8 w, UINT8 h, unsigned char *tiles);
  • x, y : Coordonnées d'origine de la zone à copier, exprimées en tuiles (entier entre 0 et 31).
  • w, h : Largeur et hauteur de la zone à copier, exprimées en tuiles (entier entre 0 et 31).
  • *tiles : pointeur vers un tableau où seront copiées les données. Attention, il faut bien penser à allouer suffisamment de mémoire pour contenir les données (par exemple 56 octets si w = 8 et h = 7).

Par exemple, si on veut dupliquer une partie de la couche Background, on peut écrire le code suivant :

void main(void) {
    // Tableau suffisamment grand pour stocker le contenu que l'on va copier
    UINT8 house[8 * 7];

    // [...] Mettre des trucs dans la couche Background

    // On duplique une partie de la couche Background
    get_bkg_tiles(5, 4, 8, 7, house);
    set_bkg_tiles(5, 12, 8, 7, house);
}
Duplication d'une partie de la couche Background

Déplacer / faire défiler le Background

GBDK nous fournit deux fonctions différentes pour déplacer le Background :

  • scroll_bkg(), qui fait défiler la couche Background relativement à sa position actuelle,
  • et move_bkg(), qui déplace la couche à la position absolue demandée.
void scroll_bkg(INT8 x, INT8 y);
  • x, y : déplacement à appliquer (par exemple +1, -1,...). Attention, cette fois-ci, le déplacement est exprimé en pixels et non en tuiles !
void move_bkg(UINT8 x, UINT8 y);
  • x, y : coordonnées où l'on souhaite déplacer la couche Background (coordonnées du coin en haut à gauche de l'écran). Les coordonnées sont exprimées en pixels.

Exemple

Maintenant qu'on connait toutes les fonctions et macros permettant de manipuler la couche Background, voici un petit exemple histoire de voir tout ça en action :

#include <gb/gb.h>

// Je vous laisse regarder dans l'article précédent pour avoir une
// idée du contenu des fichiers tileset.h et tilemap.h :)
#include "tileset.h"
#include "tilemap.h"

void main(void) {
    UINT8 keys;

    // On copie les tuiles dans la mémoire vidéo
    set_bkg_data(0, TILESET_TILE_COUNT, TILESET);

    // On copie la tilemap dans la mémoire vidéo (dans notre cas,
    // la tilemap contient tout le contenu de la couche background)
    set_bkg_tiles(0, 0, TILEMAP_WIDTH, TILEMAP_HEIGHT, TILEMAP);

    // On affiche la couche Background, sinon on ne verra rien du tout
    SHOW_BKG;

    while (1) {
        keys = joypad();

        // On fait défiler la couche Background lors de l'appui sur l'un
        // des boutons du D-PAD
        if (keys & J_UP) scroll_bkg(0, -1);
        if (keys & J_DOWN) scroll_bkg(0, 1);
        if (keys & J_LEFT) scroll_bkg(-1, 0);
        if (keys & J_RIGHT) scroll_bkg(1, 0);

        // On retourne à la vue d'origine (0, 0) lorsque l'on appuie sur START
        if (keys & J_START) move_bkg(0, 0);

        // On attend que l'écran ait été rafraichi. Si on ne fait pas ça,
        // le déplacement sera beaucoup trop rapide et la vue sera déformée
        // durant le déplacement... Vous pouvez supprimer cette ligne
        // si vous voulez tester ;)
        wait_vbl_done();
    }
}

Cet exemple affiche une carte de 32×32 tuiles dans laquelle il est possible de se déplacer à l'aide de la croix directionnelle. Un appui sur START réinitialise la vue à sa position initiale.

Capture d'écran de l'exemple une fois lancé dans un émilateur

Comme d'habitude, vous retrouverez les sources complètes de l'exemple sur Github :

C'est ici que s'achève cet article, et moi je vais m'efforcer d'écrire le suivant rapidement pour qu'il sorte d'ici 15 jours environ... 🙂️