6 years ago
Dans les parties précédentes nous avons vu les bases de notre programme ainsi que la gestion de la caméra. Nous allons voir dans cette troisième partie la gestion de l'affichage, ainsi à la fin de cette partie notre écran affichera de belles images.
Les pré-requis de ce tutoriel sont :
Je vous invite à télécharger le code qui est le résultat de la deuxième partie, ceci pour partir sur des bases communes.
Nous allons voir dans un premier temps la gestion et l'affichage des différents sprites qui composent notre jeu. Le jeu compte 7 sprites différents qui sont :
Pour chaque sprites le principe est le même, il faut créer une fois l'image et la stocker en mémoire afin de l'utiliser durant toute la vie de notre programme. Je vous montre avec l'image du mur et je vous laisserai faire avec les autres images. Rendez-vous dans SpritesManager.cpp, où nous allons y définir la méthode getWall.
Voici le code :
bool SpritesManager::wallInitialized = false; Image SpritesManager::wall; Image& SpritesManager::getWall() { if(! SpritesManager::wallInitialized) { static const uint16_t wallData[] = { 8, 8, 1, 0, 0, 0, 0x5268,0x5268,0x5268,0x5268,0x5268,0x5268,0x5268,0x5268, 0xacd0,0xacd0,0xacd0,0x5268,0x5268,0xacd0,0xacd0,0xacd0, 0xacd0,0xacd0,0xacd0,0x5268,0x5268,0xacd0,0xacd0,0xacd0, 0x5268,0x5268,0x5268,0x5268,0x5268,0x5268,0x5268,0x5268, 0x5268,0x5268,0x5268,0x5268,0x5268,0x5268,0x5268,0x5268, 0x5268,0xacd0,0xacd0,0xacd0,0xacd0,0xacd0,0xacd0,0x5268, 0x5268,0xacd0,0xacd0,0xacd0,0xacd0,0xacd0,0xacd0,0x5268, 0x5268,0x5268,0x5268,0x5268,0x5268,0x5268,0x5268,0x5268 }; SpritesManager::wall = Image(wallData); SpritesManager::wallInitialized = true; } return SpritesManager::wall; }
Voici les tableaux associés aux autres sprites :
// Une caisse static const uint16_t boxData[] = { 8, 8, 1, 0, 0, 0, 0xacd0,0xacd0,0xacd0,0xacd0,0xacd0,0xacd0,0xacd0,0xacd0, 0xacd0,0x0,0x0,0x0,0x0,0x0,0x0,0xacd0, 0xacd0,0x0,0x0,0xfeb2,0xfeb2,0xfeb2,0x0,0xacd0, 0xacd0,0x0,0xfeb2,0x0,0xfeb2,0xfeb2,0x0,0xacd0, 0xacd0,0x0,0xfeb2,0xfeb2,0x0,0xfeb2,0x0,0xacd0, 0xacd0,0x0,0xfeb2,0xfeb2,0xfeb2,0x0,0x0,0xacd0, 0xacd0,0x0,0x0,0x0,0x0,0x0,0x0,0xacd0, 0xacd0,0xacd0,0xacd0,0xacd0,0xacd0,0xacd0,0xacd0,0xacd0 }; // Une caisse sur la zone de 'chargement' static const uint16_t boxOnAreaData[] = { 8, 8, 1, 0, 0, 0, 0x44a,0x44a,0x44a,0x44a,0x44a,0x44a,0x44a,0x44a, 0x44a,0x0,0x0,0x0,0x0,0x0,0x0,0x44a, 0x44a,0x0,0x0,0xfeb2,0xfeb2,0xfeb2,0x0,0x44a, 0x44a,0x0,0xfeb2,0x0,0xfeb2,0xfeb2,0x0,0x44a, 0x44a,0x0,0xfeb2,0xfeb2,0x0,0xfeb2,0x0,0x44a, 0x44a,0x0,0xfeb2,0xfeb2,0xfeb2,0x0,0x0,0x44a, 0x44a,0x0,0x0,0x0,0x0,0x0,0x0,0x44a, 0x44a,0x44a,0x44a,0x44a,0x44a,0x44a,0x44a,0x44a }; // Une zone de 'chargement' static const uint16_t areaData[] = { 8, 8, 1, 0, 0, 0, 0xd8e4,0xd8e4,0xd8e4,0xd8e4,0xd8e4,0xd8e4,0xd8e4,0xd8e4, 0xd8e4,0xfd42,0xfd42,0xcc68,0xcc68,0xfd42,0xfd42,0xd8e4, 0xd8e4,0xcc68,0xfd42,0xfd42,0xcc68,0xcc68,0xfd42,0xd8e4, 0xd8e4,0xfd42,0xfd42,0xfd42,0xfd42,0xfd42,0xfd42,0xd8e4, 0xd8e4,0xfd42,0xfd42,0xfd42,0xfd42,0xfd42,0xfd42,0xd8e4, 0xd8e4,0xcc68,0xfd42,0xfd42,0xcc68,0xcc68,0xfd42,0xd8e4, 0xd8e4,0xcc68,0xcc68,0xfd42,0xfd42,0xcc68,0xcc68,0xd8e4, 0xd8e4,0xd8e4,0xd8e4,0xd8e4,0xd8e4,0xd8e4,0xd8e4,0xd8e4 }; // Le joueur (en dehors de la zone de 'chargement') static const uint16_t characterData[] = { 8, 8, 1, 0, 0, 0, 0xfd42,0xfd42,0xfd42,0xfd42,0xfd42,0xfd42,0xfd42,0xfd42, 0xcc68,0xacd0,0xacd0,0xacd0,0xacd0,0xacd0,0xacd0,0xcc68, 0xcc68,0xacd0,0x7ddf,0xfeb2,0xfeb2,0x7ddf,0xacd0,0xfd42, 0xfd42,0xfd42,0xfd42,0xfeb2,0xfeb2,0xfd42,0xfd42,0xfd42, 0xfd42,0x210,0x210,0x210,0x210,0x210,0x210,0xfd42, 0xcc68,0xcc68,0x210,0x210,0x210,0x210,0xfd42,0xfd42, 0xfd42,0xcc68,0xcc68,0x210,0x210,0xcc68,0xcc68,0xfd42, 0xfd42,0xfd42,0xfd42,0xfd42,0xfd42,0xfd42,0xfd42,0xfd42 }; // Le joueur sur la zone de chargement static const uint16_t characterOnAreaData[] = { 8, 8, 1, 0, 0, 0, 0xd8e4,0xd8e4,0xd8e4,0xd8e4,0xd8e4,0xd8e4,0xd8e4,0xd8e4, 0xd8e4,0xacd0,0xacd0,0xacd0,0xacd0,0xacd0,0xacd0,0xd8e4, 0xd8e4,0xacd0,0x7ddf,0xfeb2,0xfeb2,0x7ddf,0xacd0,0xd8e4, 0xd8e4,0xfd42,0xfd42,0xfeb2,0xfeb2,0xfd42,0xfd42,0xd8e4, 0xd8e4,0x210,0x210,0x210,0x210,0x210,0x210,0xd8e4, 0xd8e4,0xcc68,0x210,0x210,0x210,0x210,0xfd42,0xd8e4, 0xd8e4,0xcc68,0xcc68,0x210,0x210,0xcc68,0xcc68,0xd8e4, 0xd8e4,0xd8e4,0xd8e4,0xd8e4,0xd8e4,0xd8e4,0xd8e4,0xd8e4 }; // Le sol static const uint16_t floorData[] = { 8, 8, 1, 0, 0, 0, 0xfd42,0xfd42,0xfd42,0xfd42,0xfd42,0xfd42,0xfd42,0xfd42, 0xcc68,0xfd42,0xfd42,0xcc68,0xcc68,0xfd42,0xfd42,0xcc68, 0xcc68,0xcc68,0xfd42,0xfd42,0xcc68,0xcc68,0xfd42,0xfd42, 0xfd42,0xfd42,0xfd42,0xfd42,0xfd42,0xfd42,0xfd42,0xfd42, 0xfd42,0xfd42,0xfd42,0xfd42,0xfd42,0xfd42,0xfd42,0xfd42, 0xcc68,0xcc68,0xfd42,0xfd42,0xcc68,0xcc68,0xfd42,0xfd42, 0xfd42,0xcc68,0xcc68,0xfd42,0xfd42,0xcc68,0xcc68,0xfd42, 0xfd42,0xfd42,0xfd42,0xfd42,0xfd42,0xfd42,0xfd42,0xfd42 };
Je vous fournis des sprites mais libre à vous de personnaliser l'affichage comme vous le souhaitez. Pour ce faire rien de bien compliqué, créez votre image de 8 pixels par 8, passez cette dernière dans le générateur puis modifier le tableau en conséquence.
Dans MapView.cpp nous allons écrire la méthode getSprites, dont voici un extrait du pseudo code :
SELON type de sprites ALORS SI type de sprites = mur ALORS RETOURNER image du mur FIN SI SI type de sprites = caisse ALORS RETOURNER image de la caisse FIN SI // Faire de même pour l'ensemble des sprites FIN SELON
Voici le code complet de la méthode getSprites :
Image& MapView::getSprites(const char typeOfSprites) const { switch(typeOfSprites) { case TypeOfSprites::WALL_TYPE: return SpritesManager::getWall(); break; case TypeOfSprites::BOX_TYPE: return SpritesManager::getBox(); break; case TypeOfSprites::DESTINATION_TYPE: return SpritesManager::getArea(); break; case TypeOfSprites::BOX_ON_ZONE_TYPE: return SpritesManager::getBoxOnArea(); break; case TypeOfSprites::PLAYER_TYPE: return SpritesManager::getCharacter(); break; case TypeOfSprites::PLAYER_ON_ZONE_TYPE: return SpritesManager::getCharacterOnArea(); break; case TypeOfSprites::FLOOR_TYPE: return SpritesManager::getFloorImg(); break; } }
Nous allons tester l'affichage de nos sprites. Dans un premier temps vous pouvez commenter l'ensemble des lignes de debug créées jusqu'à maintenant, rappelez-vous il s'agit de ces lignes qui affichent du texte. Une fois fait, dans la méthode paint de MapView écrivez le code pour afficher les sprites.
Par exemple, pour afficher le mur en x, y voici ce que vous devez écrire :
gb.display.drawImage(x, y, getSprites(TypeOfSprites::WALL_TYPE));
Essayez d'obtenir le résultat suivant :
Voici le code que j'ai écris pour obtenir un tel résultat :
void MapView::paint(const int* aCameraPos) const { //gb.display.printf("Init pos %d,%d", mapModel->getPlayerPositions()[0], mapModel->getPlayerPositions()[1]); gb.display.drawImage(0, 0, getSprites(TypeOfSprites::PLAYER_TYPE)); gb.display.drawImage(16, 0, getSprites(TypeOfSprites::WALL_TYPE)); gb.display.drawImage(32, 0, getSprites(TypeOfSprites::BOX_TYPE)); gb.display.drawImage(48, 0, getSprites(TypeOfSprites::DESTINATION_TYPE)); gb.display.drawImage(0, 16, getSprites(TypeOfSprites::PLAYER_ON_ZONE_TYPE)); gb.display.drawImage(16, 16, getSprites(TypeOfSprites::FLOOR_TYPE)); gb.display.drawImage(32, 16, getSprites(TypeOfSprites::BOX_ON_ZONE_TYPE)); }
Dans un premier temps, nous allons afficher le caractère représentant le sprites, nous verrons ensuite comment afficher l'image correspondante.
Il faut écrire dans MapModel la méthode qui pour les coordonnées X, Y retourne le caractère, il s'agit de la méthode getTypeOfSprites que voici :
const char MapModel::getTypeOfSprites(const int aXSprites, const int aYSprites) { return mapOfGame[aYSprites][aXSprites]; }
Voici le pseudo code qui affiche la partie visible de la carte, à écrire dans la méthode paint de MapView :
POUR y allant de Y0 à Y1 FAIRE POUR x allant de X0 à X1 FAIRE Afficher le caractère ayant pour coordonnées x, y FIN POUR Retourner à la ligne FIN POUR
Voici 2 astuces pour vous guider :
// Afficher le caractère '@' gb.display.printf("%c", '@'); // Retourner à la ligne gb.display.println("");
Voici le code à écrire :
void MapView::paint(const int* aCameraPos) const { for(int y = aCameraPos[1] ; y < aCameraPos[3] ; y++) { for(int x = aCameraPos[0] ; x < aCameraPos[2] ; x++) { gb.display.printf("%c", mapModel->getTypeOfSprites(x, y)); } gb.display.println(""); } }
Dans un second temps, vous pouvez maintenant afficher les images des sprites, voici le code à écrire :
void MapView::paint(const int* aCameraPos) const { int l = 0; for(int y = aCameraPos[1] ; y < aCameraPos[3] ; y++) { int c = 0; for(int x = aCameraPos[0] ; x < aCameraPos[2] ; x++) { gb.display.drawImage(c*SpritesManager::WIDTH_SPRITES, l*SpritesManager::HEIGHT_SPRITES, getSprites(mapModel->getTypeOfSprites(x, y))); c++; } l++; } }
La gestion de l'affichage est désormais écrite, amusez vous à déplacer le joueur sur la carte (le caractère '@') et relancez le programme pour constater que l'affichage est différent.
Si vous avez terminé ou si vous rencontrez des problèmes vous pouvez télécharger la solution ici.
Dans la prochaine partie, c'est-à-dire la quatrième, nous réaliserons la gestion du personnage.
N'hésitez pas à me faire un retour : les améliorations que vous apporteriez (un regard extérieur est toujours bienvenu), les fautes, etc.
NEW 6 years ago
Super. Tiens, une idée à la con comme j'en ai souvent. Aurélien, ce serait possible :
- D'avoir un bouton qui permettrait d'obtenir une version PDF du tuto
- D'ajouter la possibilité d'indiquer qu'un tutoriel fait partie d'une série pour pouvoir passer automatiquement au précédent ou au suivant.
NEW 6 years ago
Merci, je poursuis donc la rédaction de la partie 4 (qui sauf problème devrait arriver dans la semaine).
NEW 6 years ago
Vraiment excellent :). J'aime beaucoup cette approche. J'attend la suite ^^
NEW 6 years ago
Tu peux faire "imprimer" et choisir "pdf" pour sortir une version PDF du tuto.
Pour faire les liens d'un tuto à l'autre, pour l'instant c'est à l'auteur d'ajouter les liens manuellement :)