Fatigué d’avoir à effectuer ces conversions moi-même, j’ai rapidement décidé de coder un petit utilitaire pour faire le boulot à ma place : Image Transcoder. L’arrivée d’une nouvelle équipe aux commandes du vaisseau Gamebuino m’a d’ailleurs donné envie de faire évoluer mon outil et de lui repasser une bonne couche de vernis.
Avant tout, je te propose de télécharger les assets, c’est-à-dire les ressources graphiques du projet, que nous allons devoir convertir en code C++. Ça t’évitera d’avoir à tout redessiner, à moins que ça ne t’amuse pour découvrir Piskel ou un autre outil graphique de ton choix…
Une fois téléchargés, range les dans un dossier nommé artwork
, à la racine de ton projet :
Tu remarqueras que la palette définie dans le fichier palette-1x16.png
comporte exactement 16 pixels, bien que le dernier soit totalement transparent. Ça n’était pas obligatoire, le transcodeur peut s’accomoder d’une palette incomplète. De manière générale, je te conseille de profiter au maximum des 16 couleurs qui te sont offertes avec les modes d’affichage en couleurs indexées.
Les pixels transparents d’une palette de couleur incomplète (comportant moins de 16 couleurs) se verront affecter le code couleur d’indice 0x0
par défaut. Si cette couleur est elle-même transparente alors elle se verra affecter le code 0x0000
du noir.
L’utilisation du transcodeur est très simple. Je suppose qu’une petite démo en images suffira. On va par exemple convertir notre tileset pour un mode d’affichage à couleurs indexées :
C’est simple et rapide. En un tour de main les conversions sont faites !
Dans le cas des modes d’affichage à couleurs indexées, si tu ne fournis pas de fichier palette, c’est la palette Gamebuino qui sera appliquée par défaut :
Par conséquent, si l’image à convertir ne contient aucune de ces couleurs, tous les pixels de l’image se verront affecter la couleur d’indice 0x0
par défaut (donc le noir).
Maintenant à toi de jouer. Convertis les assets du projet pour les modes d’affichage RGB565 et à couleurs indexées, en paramétrant correctement le transcodeur :
On va répartir les codes des assets dans deux fichiers séparés :
rgb565.h
pour le mode d’affichage RGB565indexed.h
pour les modes d’affichage en couleurs indexéesJe te propose de les télécharger :
On va ensuite placer ces fichiers dans un répertoire nommé assets
pour stucturer un peu notre projet et ne pas tout mettre en vrac à la racine. L’inconvénient avec l’IDE Arduino, c’est qu’en isolant les fichiers d’assets dans un dossier spécifique au lieu de les placer à la racine du projet, tout se passe comme si l’éditeur ne les voyait pas. Tu verras que tu ne peux pas ouvrir le dossier assets
. Pour contourner cette bizarrerie idiote, il suffit de créer un ficher vide assets.ino
dans le dossier assets
:
De cette façon, tu peux faire comme si tu ouvrais un autre sketch à côté du sketch principal :
C’est une manière simple de procéder pour avoir accès aux fichiers rgb565.h
ou indexed.h
, depuis l’éditeur de code de l’IDE Arduino, lorsque tu auras besoin d’y apporter des modifications.
Mais, le sketch principal my-stunning-game
reste celui à partir duquel tu dois toujours lancer la compilation et le téléversement sur ta Gamebuino. C’est en effet le point de départ de tout le processus de compilation. L’idée consiste donc à y intégrer le bon fichier d’assets en fonction du mode d’affichage que tu auras choisi et défini dans le fichier config-gamebuino.h
.
DISPLAY_MODE_RGB565
my-stunning-game.ino
#include <Gamebuino-Meta.h>
#include "assets/rgb565.h"
void setup() {
gb.begin();
}
void loop() {
gb.waitForUpdate();
}
DISPLAY_MODE_INDEX ou DISPLAY_MODE_INDEX_HALFRES
my-stunning-game.ino
#include <Gamebuino-Meta.h>
#include "assets/indexed.h"
void setup() {
gb.begin();
}
void loop() {
gb.waitForUpdate();
}
Les assets convertis et prêts à l’emploi pour C++
Voyons maintenant le détail de ces deux fichiers d’assets. Les tableaux de données caractérisant la spritesheet SPRITE_DATA
et le tileset TILESET_DATA
qui ont été générés par le transcodeur y sont rassemblés. Pour éviter les inclusions multiples, il faut également penser à ajouter la directive de précompilation #pragma once
au début de chacun de ces deux fichiers :
assets/rgb565.h
#pragma once
const uint16_t SPRITE_DATA[] = {
// metadata
8, // frame width
8, // frame height
4, // frames
4, // frame loop
0xf81f, // transparent color
0, // 16-bits color mode
// colormap
// frame 1/4
0xf81f, 0xf81f, 0x632c, 0xad55, 0xad55, 0xad55, 0xf81f, 0xf81f,
0xf81f, 0xf81f, 0xee2f, 0xff36, 0xff36, 0xff36, 0xf81f, 0xf81f,
0xf81f, 0xf81f, 0xee2f, 0x0000, 0xff36, 0x0000, 0xf81f, 0xf81f,
0xf81f, 0xf81f, 0xee2f, 0xff36, 0xff36, 0xff36, 0xf81f, 0xf81f,
0xf81f, 0xf81f, 0x7afa, 0xb4df, 0xb4df, 0xb4df, 0xf81f, 0xf81f,
0xf81f, 0xf81f, 0xff36, 0xb4df, 0xb4df, 0xb4df, 0xff36, 0xf81f,
0xf81f, 0xf81f, 0x7afa, 0xb4df, 0xb4df, 0xb4df, 0xf81f, 0xf81f,
0xf81f, 0xf81f, 0x6217, 0xf81f, 0xf81f, 0x6217, 0xf81f, 0xf81f,
// frame 2/4
0xf81f, 0xf81f, 0x632c, 0xad55, 0xad55, 0xad55, 0xf81f, 0xf81f,
0xf81f, 0xf81f, 0xee2f, 0xff36, 0xff36, 0xff36, 0xf81f, 0xf81f,
0xf81f, 0xf81f, 0xee2f, 0x0000, 0xff36, 0x0000, 0xf81f, 0xf81f,
0xf81f, 0xf81f, 0xee2f, 0xff36, 0xff36, 0xff36, 0xf81f, 0xf81f,
0xf81f, 0xf81f, 0x7afa, 0xb4df, 0xb4df, 0xb4df, 0xf81f, 0xf81f,
0xf81f, 0xff36, 0x7afa, 0xb4df, 0xb4df, 0xb4df, 0x7afa, 0xff36,
0xf81f, 0xf81f, 0x7afa, 0xb4df, 0xb4df, 0xb4df, 0xf81f, 0xf81f,
0xf81f, 0xf81f, 0xf81f, 0x7afa, 0x6217, 0xf81f, 0xf81f, 0xf81f,
// frame 3/4
0xf81f, 0xf81f, 0x632c, 0xad55, 0xad55, 0xad55, 0xf81f, 0xf81f,
0xf81f, 0xf81f, 0xee2f, 0xff36, 0xff36, 0xff36, 0xf81f, 0xf81f,
0xf81f, 0xf81f, 0xee2f, 0x0000, 0xff36, 0x0000, 0xf81f, 0xf81f,
0xf81f, 0xf81f, 0xee2f, 0xff36, 0xff36, 0xff36, 0xf81f, 0xf81f,
0xf81f, 0xf81f, 0x7afa, 0xb4df, 0xb4df, 0xb4df, 0xf81f, 0xf81f,
0xf81f, 0xf81f, 0xff36, 0xb4df, 0xb4df, 0xb4df, 0xff36, 0xf81f,
0xf81f, 0xf81f, 0x7afa, 0xb4df, 0xb4df, 0xb4df, 0xf81f, 0xf81f,
0xf81f, 0xf81f, 0x6217, 0xf81f, 0xf81f, 0x6217, 0xf81f, 0xf81f,
// frame 4/4
0xf81f, 0xf81f, 0x632c, 0xad55, 0xad55, 0xad55, 0xf81f, 0xf81f,
0xf81f, 0xf81f, 0xee2f, 0xff36, 0xff36, 0xff36, 0xf81f, 0xf81f,
0xf81f, 0xf81f, 0xee2f, 0x0000, 0xff36, 0x0000, 0xf81f, 0xf81f,
0xf81f, 0xf81f, 0xee2f, 0xff36, 0xff36, 0xff36, 0xf81f, 0xf81f,
0xf81f, 0xf81f, 0x7afa, 0xb4df, 0xb4df, 0xb4df, 0xf81f, 0xf81f,
0xf81f, 0xf81f, 0x7afa, 0x7afa, 0xff36, 0xb4df, 0xf81f, 0xf81f,
0xf81f, 0xf81f, 0x7afa, 0xb4df, 0xb4df, 0xb4df, 0xf81f, 0xf81f,
0xf81f, 0x6217, 0xf81f, 0xf81f, 0xf81f, 0xf81f, 0x7afa, 0xf81f
};
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
// frame 1/4
0x1926, 0x1926, 0x1926, 0x1926, 0x1926, 0x1926, 0x1926, 0x1926, 0x1926, 0x1926, 0x1926, 0x1926, 0x1926, 0x1926, 0x1926, 0x0000,
0x1926, 0x1926, 0x10e4, 0x1926, 0x10e4, 0x1926, 0x10e4, 0x1926, 0x10e4, 0x1926, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x0862,
0x1926, 0x10e4, 0x1926, 0x10e4, 0x1926, 0x10e4, 0x1926, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x0862,
0x1926, 0x1926, 0x10e4, 0x1926, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x0862,
0x1926, 0x10e4, 0x1926, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x0862,
0x1926, 0x1926, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x0862,
0x1926, 0x10e4, 0x1926, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x0862, 0x0862,
0x1926, 0x1926, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x0862,
// frame 2/4
0x4228, 0x632c, 0x632c, 0x632c, 0x632c, 0x632c, 0x632c, 0x632c, 0x632c, 0x632c, 0x632c, 0x632c, 0x632c, 0x632c, 0x632c, 0x632c,
0xad55, 0x4228, 0x4228, 0x4228, 0x4228, 0x4228, 0x4228, 0x4228, 0x4228, 0x4228, 0x4228, 0x4228, 0x4228, 0x4228, 0x4228, 0x4228,
0x632c, 0x4228, 0x4228, 0x4228, 0x4228, 0x4228, 0x4228, 0x4228, 0x4228, 0x4228, 0x4228, 0x4228, 0x4228, 0x4228, 0x4228, 0x4228,
0x0000, 0x3186, 0x3186, 0x3186, 0x3186, 0x3186, 0x3186, 0x3186, 0x3186, 0x3186, 0x3186, 0x3186, 0x3186, 0x3186, 0x3186, 0x3186,
0x18c3, 0x0000, 0x18c3, 0x0000, 0x18c3, 0x0000, 0x18c3, 0x0000, 0x18c3, 0x0000, 0x18c3, 0x0000, 0x18c3, 0x0000, 0x18c3, 0x0000,
0x0000, 0x18c3, 0x0000, 0x3186, 0x0000, 0x18c3, 0x0000, 0x3186, 0x0000, 0x18c3, 0x0000, 0x3186, 0x0000, 0x18c3, 0x0000, 0x3186,
0x3186, 0x0000, 0x3186, 0x0000, 0x3186, 0x0000, 0x3186, 0x0000, 0x3186, 0x0000, 0x3186, 0x0000, 0x3186, 0x0000, 0x3186, 0x0000,
0x0000, 0x18c3, 0x0000, 0x18c3, 0x0000, 0x18c3, 0x0000, 0x18c3, 0x0000, 0x18c3, 0x0000, 0x18c3, 0x0000, 0x18c3, 0x0000, 0x18c3,
// frame 3/4
0x1926, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x0862, 0x0862,
0x1926, 0x1926, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x0862, 0x10e4, 0x0862,
0x1926, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x0862, 0x0862,
0x1926, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x0862, 0x10e4, 0x0862,
0x1926, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x0862, 0x10e4, 0x0862, 0x0862,
0x1926, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x0862, 0x10e4, 0x0862, 0x10e4, 0x0862, 0x10e4, 0x0862,
0x1926, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x10e4, 0x0862, 0x10e4, 0x0862, 0x10e4, 0x0862, 0x10e4, 0x0862, 0x10e4, 0x0862, 0x0862,
0x0000, 0x0862, 0x0862, 0x0862, 0x0862, 0x0862, 0x0862, 0x0862, 0x0862, 0x0862, 0x0862, 0x0862, 0x0862, 0x0862, 0x0862, 0x0000,
// frame 4/4
0x0000, 0x632c, 0x3186, 0x3186, 0x3186, 0x3186, 0x3186, 0x3186, 0x3186, 0x3186, 0x3186, 0x3186, 0x3186, 0x3186, 0x3186, 0x18c3,
0x632c, 0x3186, 0x18c3, 0x3186, 0x18c3, 0x3186, 0x18c3, 0x3186, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x0000,
0x3186, 0x18c3, 0x3186, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x0000,
0x3186, 0x3186, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x0000,
0x3186, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x0000, 0x0000,
0x3186, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x0000, 0x18c3, 0x0000, 0x18c3, 0x0000,
0x3186, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x0000, 0x18c3, 0x0000, 0x18c3, 0x0000, 0x18c3, 0x0000, 0x18c3, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x18c3
};
Dans le cas du fichier d’assets pour les modes d’affichage en couleurs indexées, la définition de la palette des couleurs, qui est commune à SPRITE_DATA
et TILESET_DATA
, a également été ajoutée. Le fichier d’en-tête <Gamebuino-Meta.h>
doit aussi être intégré puisqu’il contient la déclaration de la classe Color
qui est utilisée pour la définition de la palette :
assets/indexed.h
#pragma once
#include <Gamebuino-Meta.h>
const Color PALETTE[] = {
(Color) 0x0000, // 0x0
(Color) 0x18c3, // 0x1
(Color) 0x3186, // 0x2
(Color) 0x4228, // 0x3
(Color) 0x632c, // 0x4
(Color) 0xad55, // 0x5
(Color) 0xee2f, // 0x6
(Color) 0xff36, // 0x7
(Color) 0x0862, // 0x8
(Color) 0x10e4, // 0x9
(Color) 0x1926, // 0xa
(Color) 0x6217, // 0xb
(Color) 0x7afa, // 0xc
(Color) 0xb4df, // 0xd
(Color) 0xf81f, // 0xe
(Color) 0x0000 // 0xf
};
const uint8_t SPRITE_DATA[] = {
// metadata
8, // frame width
8, // frame height
0x04, // frames (lower byte)
0x00, // frames (upper byte)
4, // frame loop
0xe, // transparent color
1, // indexed color mode
// colormap
// frame 1/4
0xee, 0x45, 0x55, 0xee,
0xee, 0x67, 0x77, 0xee,
0xee, 0x60, 0x70, 0xee,
0xee, 0x67, 0x77, 0xee,
0xee, 0xcd, 0xdd, 0xee,
0xee, 0x7d, 0xdd, 0x7e,
0xee, 0xcd, 0xdd, 0xee,
0xee, 0xbe, 0xeb, 0xee,
// frame 2/4
0xee, 0x45, 0x55, 0xee,
0xee, 0x67, 0x77, 0xee,
0xee, 0x60, 0x70, 0xee,
0xee, 0x67, 0x77, 0xee,
0xee, 0xcd, 0xdd, 0xee,
0xe7, 0xcd, 0xdd, 0xc7,
0xee, 0xcd, 0xdd, 0xee,
0xee, 0xec, 0xbe, 0xee,
// frame 3/4
0xee, 0x45, 0x55, 0xee,
0xee, 0x67, 0x77, 0xee,
0xee, 0x60, 0x70, 0xee,
0xee, 0x67, 0x77, 0xee,
0xee, 0xcd, 0xdd, 0xee,
0xee, 0x7d, 0xdd, 0x7e,
0xee, 0xcd, 0xdd, 0xee,
0xee, 0xbe, 0xeb, 0xee,
// frame 4/4
0xee, 0x45, 0x55, 0xee,
0xee, 0x67, 0x77, 0xee,
0xee, 0x60, 0x70, 0xee,
0xee, 0x67, 0x77, 0xee,
0xee, 0xcd, 0xdd, 0xee,
0xee, 0xcc, 0x7d, 0xee,
0xee, 0xcd, 0xdd, 0xee,
0xeb, 0xee, 0xee, 0xce
};
const uint8_t TILESET_DATA[] = {
// metadata
16, // frame width
8, // frame height
0x04, // frames (lower byte)
0x00, // frames (upper byte)
0, // frame loop
0xe, // transparent color
1, // indexed color mode
// colormap
// frame 1/4
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xa0,
0xaa, 0x9a, 0x9a, 0x9a, 0x9a, 0x99, 0x99, 0x98,
0xa9, 0xa9, 0xa9, 0xa9, 0x99, 0x99, 0x99, 0x98,
0xaa, 0x9a, 0x99, 0x99, 0x99, 0x99, 0x99, 0x98,
0xa9, 0xa9, 0x99, 0x99, 0x99, 0x99, 0x99, 0x98,
0xaa, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x98,
0xa9, 0xa9, 0x99, 0x99, 0x99, 0x99, 0x99, 0x88,
0xaa, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x98,
// frame 2/4
0x34, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
0x53, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
0x43, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
0x02, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
// frame 3/4
0xa9, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x88,
0xaa, 0x99, 0x99, 0x99, 0x99, 0x99, 0x98, 0x98,
0xa9, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x88,
0xa9, 0x99, 0x99, 0x99, 0x99, 0x99, 0x98, 0x98,
0xa9, 0x99, 0x99, 0x99, 0x99, 0x99, 0x89, 0x88,
0xa9, 0x99, 0x99, 0x99, 0x98, 0x98, 0x98, 0x98,
0xa9, 0x99, 0x99, 0x89, 0x89, 0x89, 0x89, 0x88,
0x08, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x80,
// frame 4/4
0x04, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x21,
0x42, 0x12, 0x12, 0x12, 0x11, 0x11, 0x11, 0x10,
0x21, 0x21, 0x11, 0x11, 0x11, 0x11, 0x11, 0x10,
0x22, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x10,
0x21, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x00,
0x21, 0x11, 0x11, 0x11, 0x11, 0x10, 0x10, 0x10,
0x21, 0x11, 0x11, 0x01, 0x01, 0x01, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
};
Voilà, désormais tout est prêt pour passer à l’implémentation de l’affichage des assets à l’écran. Y’avait pas mal de choses à dire et à appliquer en préambule de la programmation. Mais j’espère que tout est maintenant plus clair dans ton esprit quant à la manière de préparer tes assets avant de leur donner vie par le code. On se retrouve au prochain chapitre pour la suite.