Controls: D-Pad: [Arrows / WASD] - A: [J] - B: [K] - Menu: [U] - Home: [I]
Enjoy games at full speed with sound and lights on the Gamebuino META!
Emulator by aoneill
Durée: 30 minutes (à la louche)
Niveau: ancien débutant ayant suivi les premiers tutos
Prérequis
Nous avons vu dans la partie précédente comment charger les données d'un niveau et nous les avons affiché de manière brute: nous avons affiché le caractère qui nous sert dans le tableau "niveau[ligne][colonne]" à suivre l'état d'une "case" du jeu (Si c'est une case Mur, Caisse, Destination, et à savoir si le personnage s'y trouve).
Bien maintenant, des caractères c'est bien mais c'est une peu moche, non ? Alors à la place, on va afficher des sprites. On en tait là dans le dernier tuto. Maintenant il faut réfléchir à la taille de ces sprites. Plus le sprite est gros, plus il est beau (enfin normalement, il peut aussi être gros et moche et là vous avez besoin de quelqu'un qui a du goût et un peu de talent...). Plus il est petit, plus on peut en afficher. On a une solution de 80 pixels de large sur 64 de haut en mode résolution standard. Si on prend un résolution de 12 pixels, on peut afficher 5 sprites de haut : 12 x 5 = 60, c'est court. On aura du mal à appréhender le niveau dans notre cas. Si on prend un sprite qui est 2 fois plus petit: 6 pixels, on peut en mettre: 64/6 soit 10 (2 fois plus c'est assez logique). C'est super mais dessiner un sprite sur 6 pixels dans notre cas, ne nous permet pas de faire ce que je souhaite dessiner. C'est un choix, on pourrait adapter les graphismes pour qu'ils tiennent sur 6 pixels. Je m'en tiendrais donc au choix le plus standard: 8 pixels, ce qui nous laisse 64/8 soit 8 sprites de haut. On pourrait donc en mettre 10 en largeur (80/8) mais dans ce cas il faudrait afficher les informations du jeu sur les sprites. C'est faisable: le jeu Meze le fait très bien mais nous on va s'y prendre autrement: on va garder une partie de l'écran pour afficher ces informations. C'est encore un autre choix et c'est à vous de décider quand vous créez votre jeu quel est le rendu que vous souhaitez. Il n'y a pas de bonnes et de mauvaises solutions (enfin si, si on ne voit rien et que l'on n'arrive pas à lire, c'est un mauvais choix...)
Pour la première partie du job, utilisez le programme que vous souhaitez pour dessiner vos sprites. (regardez ce lien si besoin: http://pixeljoint.com/forum/forum_posts.asp?TID=11299)
Pour mieux comprendre la manipulation des images, vous pouvez lire ce tuto: https://gamebuino.com/creations/images
Bon bref, faites vos images de 8 x 8 pixels et sauvegardez les en PNG ou en BMP 24 bits.
Dans mon cas, j'ai créé ces sprites:
Puis j'utilise cet utilitaire https://gamebuino.com/creations/png-to-code pour les convertir en code mais on pourrait également charger les fichiers tel qu'indiqué dans le tutorial "images". Vous êtes le créateur de vos (nos) jeux alors choisissez la méthode que vous préférez.
Vérifions que nous associons bien une image pour chaque case de notre jeu
Légende :
# : mur => mur.bmp
$ : caisse => caisse.bmp
. : destination => cible.bmp
* : caisse sur une zone de rangement => caisse_sur_cible.bmp
espace (' ') => sol.bmp
On ne dessine pas pour le moment le personnage, donc pour le moment:
@ : personnage => sol.bmp
+ : personnage sur une zone de rangement => cible.bmp
=> OK, ça colle. Nous allons pouvoir ajouter nos images dans notre code.
Nous allons ajouter beaucoup de lignes en intégrant nos données. Afin de ne pas surcharger les parties importantes du code, nous allons ouvrir un nouvel onglet pour y mettre nos images. Hors un programme peut être constitué de centaines voir de milliers de lignes de code et de fonctions. Nous n'atteindrons sans doute jamais cette complexité, mais dès que le code devient complexe, il devient essentiel pour s’y retrouver d’organiser votre code en plusieurs modules. Un module est un couple de fichier source (extension .c pour un fichier c et .ino pour un fichier Arduino ) et un fichier d'en-tête (« header », extension .h).
On regroupe dans un module des fonctions et/ou variables, des types qui traitent d'une sous- partie du programme global. On essaye de les grouper par thèmes. Vous verrez dans les codes des autres Gamebuinistes, souvent, on a une partie Player, une parie Ennemi, une partie son, etc... Dans tous les cas, ce découpage se fait après analyse.
Pour cela, cliquez sur la flèche qui descend qui se trouve en haut à droite de votre fenêtre de code (regardez sur la capture en dessous pour mieux voir où ça se trouve)
Plus tard, on aura sans doute des habitudes ou des conventions, mais en attendant, je vous donne ma méthode que j'ai construit en regardant comment des développeurs plus expérimentés faisaient mais elle peut sans doute être amélioré. En fait j'écris autant ses tutos pour vous que pour moi car j'apprends aussi à programmer sur la Meta... Ca me permet de faire le point sur mon expérience et de mettre me idées au claire.
Bon bref, on va choisir dans le menu "Nouvel onglet".
Vous allez ensuite donner un nom à cet onglet qui correspond à un fichier qui va être créé dans le même répertoire que SOKOBAN.ino (donc le répertoire SOKOBAN). Nous allons l'appeler Graphics.ino (C'est un choix de ma part, vous pouvez l'appeler comme vous voulez ...)
Dans cet onglet, on va rajouter nos graphiques mais pour pouvoir les utiliser depuis n'importe quel onglet, nous allons recommencer la même opération de création d'onglet pour créer le fichier Graphics.h.
Si tout c'est bien passé, le haut de votre fenêtre ressemble désormais à ça:
OK, maintenant utilisons image to code converter (https://gamebuino.com/creations/png-to-code): Hyper simple: on fait glisser son image sur la partie grise avec le texte: "Drop your image into this zone"... On ne peut pas plus intuitif (à part peut être avec des commandes neuronales...) et on récupère le code en dessous en le sélectionnant puis en faisant en clic droit, en choisissant "copier" puis en allant dans "Graphics" en faisant un clic droit pour choisir "coller"...
En effet dans le fichier source (".c" ou ".ino") du module, on insère le code source du module : on initialise des variables globales et on y place toutes les fonctions du module.
C'est ce que nous faisons pour nos 5 images on collant le code généré. A la fin, on doit avoir ça dans "Graphics"
const uint16_t CaisseData[] = {8,8,1, 1, 0, 0, 0x0,0x0,0xbc6a,0xbc6a,0xbc6a,0xbc6a,0x0,0x0,0x0,0xb429,0xbcab,0xbccc,0xbc8a,0xb407,0xabe7,0x0,0xbc6a,0xbc8a,0xc52d,0xcd6f,0xabe7,0xabe7,0xb449,0x8b46,0xbc6a,0xbc8a,0xc52d,0xabe7,0xabe7,0xb48b,0xc4ec,0x8b46,0xbc6a,0xb46a,0xabe7,0xabe7,0xac29,0xc52d,0xbcec,0x9345,0xbc6a,0xac08,0xabe7,0xb48b,0xc52d,0xc54e,0xbccc,0x9325,0x0,0xabe7,0xb46a,0xbcab,0xb46a,0xb48a,0xb449,0x0,0x0,0x0,0x8b46,0x8b46,0x8b46,0x8b46,0x0,0x0}; Image Caisse = Image(CaisseData);const uint16_t Caisse_sur_cibleData[] = {8,8,1, 1, 0, 0, 0xf800,0x0,0xbc6a,0xf800,0xf800,0xbc6a,0x0,0xf800,0x0,0xb429,0xbcab,0xbccc,0xbc8a,0xb407,0xabe7,0x0,0xbc6a,0xbc8a,0xc52d,0xcd6f,0xabe7,0xabe7,0xb449,0x8b46,0xf800,0xbc8a,0xc52d,0xabe7,0xabe7,0xb48b,0xc4ec,0xf800,0xf800,0xb46a,0xabe7,0xabe7,0xac29,0xc52d,0xbcec,0xf800,0xbc6a,0xac08,0xabe7,0xb48b,0xc52d,0xc54e,0xbccc,0x9325,0x0,0xabe7,0xb46a,0xbcab,0xb46a,0xb48a,0xb449,0x0,0xf800,0x0,0x8b46,0xf800,0xf800,0x8b46,0x0,0xf800}; Image Caisse_sur_cible = Image(Caisse_sur_cibleData);
const uint16_t CibleData[] = {8,8,1, 1, 0, 0, 0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0x8410,0x8410,0xf800,0xf800,0x8410,0x8410,0xf800,0xf800,0x8410,0x8410,0xf800,0xf800,0x8410,0x8410,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0x8410,0x8410,0xf800,0xf800,0x8410,0x8410,0xf800,0xf800,0x8410,0x8410,0xf800,0xf800,0x8410,0x8410,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800}; Image Cible = Image(CibleData);
const uint16_t MurData[] = {8,8,1, 1, 0, 0, 0x8984,0x4208,0x4208,0xa1c5,0x4208,0x4208,0x8984,0x4208,0x4208,0xc103,0xc103,0xb143,0xb164,0xc103,0xc103,0x91c5,0x9184,0x9963,0x9943,0xa943,0xa984,0xa1a4,0x99a4,0x4208,0x4208,0xc103,0xc103,0xa164,0xa1a4,0xc103,0xc103,0x8a05,0x9206,0x99a5,0x9184,0x8984,0xa184,0xa184,0x89c5,0x4208,0x4208,0xc103,0xc103,0x8984,0xc103,0xc103,0x89a4,0x79e5,0x9226,0x99c5,0x9964,0xa163,0xa963,0xb143,0x9964,0x4208,0x4208,0x4208,0xc103,0xc0e2,0x4208,0xc0e2,0xc102,0x4208}; Image Mur = Image(MurData);
const uint16_t SolData[] = {8,8,1, 1, 0, 0, 0x4208,0x4208,0x4208,0x4208,0x4208,0x4208,0x4208,0x4208,0x4208,0x8410,0x8410,0x8410,0x8410,0x8410,0x8410,0x8410,0x4208,0x8410,0x8410,0x8410,0x8410,0x8410,0x8410,0x8410,0x4208,0x8410,0x8410,0x8410,0x8410,0x8410,0x8410,0x8410,0x4208,0x8410,0x8410,0x8410,0x8410,0x8410,0x8410,0x8410,0x4208,0x8410,0x8410,0x8410,0x8410,0x8410,0x8410,0x8410,0x4208,0x8410,0x8410,0x8410,0x8410,0x8410,0x8410,0x8410,0x4208,0x8410,0x8410,0x8410,0x8410,0x8410,0x8410,0x8410}; Image Sol = Image(SolData);
Maintenant passons à Graphics.h. Tout d'abord, comprenons l'utilité de la chose. Les fichiers .h contiennent tout ce dont le compilateur doit connaître lorsque l'on compile le code qui utilise les fonctionnalités du module. Le header (fichier d'entête) contient non pas les fonctionnalités du module déjà présentes dans le code, mais uniquement leurs déclarations, c'est à dire le « mode d’emploi » de ces fonctionnalités pour le compilateur mais aussi pour les autres développeurs qui peuvent y retrouver très rapidement les informations importantes. On y met donc les définitions des types publics, les prototypes (ou signatures) des fonctions publiques du module. Oui, on ne définit que ce qui est public, tout ce qui doit pouvoir être vu et manipulé de l'extérieur (depuis le programme principal ou depuis un autre module.
On a déjà fait pas mal de théorie et je suis sûr que quelqu'un pourra être plus précis ou plus clair dans ses explications.
De plus, il existe un système simple qui protège des inclusions multiples, grâce aux ifndef le .h ne peut être inclus qu’une fois. Pour cela, on le met tout au début de notre .h, ce qui donne la structure suivante:
#ifndef GRAPHICS_H #define GRAPHICS_H// On mettra note code ici
#endif
Donc on teste avec ce code si GRAPHICS_H est définit. S'il ne l'est pas on le définit les parties visible du module. Dans notre cas, ça donnera:
#ifndef GRAPHICS_H #define GRAPHICS_Hextern Image Caisse; extern Image Caisse_sur_cible; extern Image Cible; extern Image Mur; extern Image Sol;
#endif
Voilà, pour l'utiliser, il suffira ensuite de rajouter un include du fichier d'entête dans notre programme principal avec la syntaxe suivante: #include "Graphics.h"
Là, on donne accès à nos 5 images, on les expose aux autres modules, mais si l'on réfléchi un peu plus, on se rend compte que l'on ne va les utiliser que dans notre procédure de dessin de notre écran. Il serait plus intelligent de mettre cette procédure dans ce module et de l'exposer elle seulement.
Puisque l'on en parle, commençons à réfléchir à ce que nous voulons dessiner. Nous savons que nous pouvons dessiner 8 sprites de haut mais nous avons décidé de ne pas limiter la taille d'un niveau à 8 lignes. Ce que l'on veut suivre, c'est notre personnage. Il faut donc que l'on affiche les 8 cases qu'il y a autour de lui. Nous avons encore un autre choix à faire: Comme nous avons 8 sprites affichables, et que personnage sera au centre, nous ne tombons pas sur une solution possible en effet, si on met autant de sprites au dessus qu'en dessous, on tombera forcément sur un nombre impair. N spites en haut + Sprite perso + N sprites en bas => 2N Sprites + 1.
Bon on va faire simple et on mettra initialement 4 en haut et 3 sprites en bas et pareil lattéralement: 4 sprites à gauche et 3 sprites à droite.
Autre choix à faire: Soit on déplace toujours le niveau et le personnage reste au centre mais du coup quand le personnage sera au bord de l'écran, on aura toute une partie de l'écran inutile, soit on ne le déplace que quand c'est nécessaire et sinon, c'est le personnage qui bouge à l'écran. Bon ce sera sans doute plus clair après en voyant le code.
On voit donc que l'on doit suivre la position du personnage.
Pour le moment, on n'a pas encore ajouté le personnage, mais on va juste ajouter les coordonnées de sa position que l'on va initialiser avec la valeur de départ indiqué dans le niveau afin de s'en servir avec à la procédure de traçage du niveau avec a règle que l'on a expliqué avant (Si x > 4 => tracer depuis x - 4 sinon tracer depuis 0 et pareil pour y: si y > 4, tracer depuis y - 4 sinon tracer depuis 0).
Allez, on termine cette partie et on l'affiche enfin ce niveau...
Donc, on repart de notre programme de la dernière fois, on y rajoute les variables X et Y.
On les initialise à -1 pour dire au début que l'on ne connait pas la position du joueur et on fait une petite procédure pour la trouver. On appelle cette procédure dans notre boucle principale pour le moment quand X et Y sont à -1, c'est à dire quand ils n'ont pas encore la position du joueur. Bien entendu à terme dans un prochain tuto, on mettra en place les phases du jeu (démarrage, changement de niveau, etc... et on aura quelque chose de plus propre mais chaque chose en son temps. Patience et longueur de temps font plus que force et que rage...). Après dans la boucle principale, le seul truc qui nous reste à faire, c'est d'appeler la demande d'affichage de la partie du niveau situé autour du joueur.
Bon, je ne disserte pas plus et je vous mets le programme ici, dites moi s'il manque des explications sur quelque chose. Perso, ça me semble asse intuitif.
#include #include "Graphics.h"#define NB_LIGNES_NIVEAUX 11 #define NB_COLONNES_NIVEAUX 19
int X; // Position horizontale du personnage int Y; // Position verticale du personnage
char niveau[NB_LIGNES_NIVEAUX][NB_COLONNES_NIVEAUX] = { { ' ', ' ', ' ', ' ', '#', '#', '#', '#', '#', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', }, { ' ', ' ', ' ', ' ', '#', ' ', ' ', ' ', '#', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', }, { ' ', ' ', ' ', ' ', '#', '$', ' ', ' ', '#', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', }, { ' ', ' ', '#', '#', '#', ' ', ' ', '$', '#', '#', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', }, { ' ', ' ', '#', ' ', ' ', '$', ' ', '$', ' ', '#', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', }, { '#', '#', '#', ' ', '#', ' ', '#', '#', ' ', '#', ' ', ' ', ' ', '#', '#', '#', '#', '#', '#', }, { '#', ' ', ' ', ' ', '#', ' ', '#', '#', ' ', '#', '#', '#', '#', '#', ' ', ' ', '.', '.', '#', }, { '#', ' ', '$', ' ', ' ', '$', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '.', '.', '#', }, { '#', '#', '#', '#', '#', ' ', '#', '#', '#', ' ', '#', '@', '#', '#', ' ', ' ', '.', '.', '#', }, { ' ', ' ', ' ', ' ', '#', ' ', ' ', ' ', ' ', ' ', '#', '#', '#', '#', '#', '#', '#', '#', '#', }, { ' ', ' ', ' ', ' ', '#', '#', '#', '#', '#', '#', '#', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', }, };
void trouve_position_perso() { for (int ligne = 0; ligne < NB_LIGNES_NIVEAUX; ligne++) { for (int colonne = 0; colonne < NB_COLONNES_NIVEAUX; colonne++) { if (niveau[ligne][colonne] == '@') { X = colonne; Y = ligne; } } } }
void setup() { gb.begin(); X = -1; Y = -1; }
void loop() { while (!gb.update()); gb.display.clear();
// Initialisation de la position du personnage pour cette étape if ((X == -1) && (Y == -1)) { trouve_position_perso(); }
// On affiche le contenu de chacune des cases DessineNiveau(X, Y); }
Voilà, pour la procédure, je la mets dans Graphics.ino comme nous l'avons décrie plus haut et pour chaque case, elle demandera l'affichage du sprite trouvé, ce qui nous donne le programme suivant:
const uint16_t CaisseData[] = {8, 8, 1, 1, 0, 0, 0x0, 0x0, 0xbc6a, 0xbc6a, 0xbc6a, 0xbc6a, 0x0, 0x0, 0x0, 0xb429, 0xbcab, 0xbccc, 0xbc8a, 0xb407, 0xabe7, 0x0, 0xbc6a, 0xbc8a, 0xc52d, 0xcd6f, 0xabe7, 0xabe7, 0xb449, 0x8b46, 0xbc6a, 0xbc8a, 0xc52d, 0xabe7, 0xabe7, 0xb48b, 0xc4ec, 0x8b46, 0xbc6a, 0xb46a, 0xabe7, 0xabe7, 0xac29, 0xc52d, 0xbcec, 0x9345, 0xbc6a, 0xac08, 0xabe7, 0xb48b, 0xc52d, 0xc54e, 0xbccc, 0x9325, 0x0, 0xabe7, 0xb46a, 0xbcab, 0xb46a, 0xb48a, 0xb449, 0x0, 0x0, 0x0, 0x8b46, 0x8b46, 0x8b46, 0x8b46, 0x0, 0x0}; Image Caisse = Image(CaisseData);const uint16_t Caisse_sur_cibleData[] = {8, 8, 1, 1, 0, 0, 0xf800, 0x0, 0xbc6a, 0xf800, 0xf800, 0xbc6a, 0x0, 0xf800, 0x0, 0xb429, 0xbcab, 0xbccc, 0xbc8a, 0xb407, 0xabe7, 0x0, 0xbc6a, 0xbc8a, 0xc52d, 0xcd6f, 0xabe7, 0xabe7, 0xb449, 0x8b46, 0xf800, 0xbc8a, 0xc52d, 0xabe7, 0xabe7, 0xb48b, 0xc4ec, 0xf800, 0xf800, 0xb46a, 0xabe7, 0xabe7, 0xac29, 0xc52d, 0xbcec, 0xf800, 0xbc6a, 0xac08, 0xabe7, 0xb48b, 0xc52d, 0xc54e, 0xbccc, 0x9325, 0x0, 0xabe7, 0xb46a, 0xbcab, 0xb46a, 0xb48a, 0xb449, 0x0, 0xf800, 0x0, 0x8b46, 0xf800, 0xf800, 0x8b46, 0x0, 0xf800}; Image Caisse_sur_cible = Image(Caisse_sur_cibleData);
const uint16_t CibleData[] = {8, 8, 1, 1, 0, 0, 0xf800, 0xf800, 0xf800, 0xf800, 0xf800, 0xf800, 0xf800, 0xf800, 0xf800, 0x8410, 0x8410, 0xf800, 0xf800, 0x8410, 0x8410, 0xf800, 0xf800, 0x8410, 0x8410, 0xf800, 0xf800, 0x8410, 0x8410, 0xf800, 0xf800, 0xf800, 0xf800, 0xf800, 0xf800, 0xf800, 0xf800, 0xf800, 0xf800, 0xf800, 0xf800, 0xf800, 0xf800, 0xf800, 0xf800, 0xf800, 0xf800, 0x8410, 0x8410, 0xf800, 0xf800, 0x8410, 0x8410, 0xf800, 0xf800, 0x8410, 0x8410, 0xf800, 0xf800, 0x8410, 0x8410, 0xf800, 0xf800, 0xf800, 0xf800, 0xf800, 0xf800, 0xf800, 0xf800, 0xf800}; Image Cible = Image(CibleData);
const uint16_t MurData[] = {8, 8, 1, 1, 0, 0, 0x8984, 0x4208, 0x4208, 0xa1c5, 0x4208, 0x4208, 0x8984, 0x4208, 0x4208, 0xc103, 0xc103, 0xb143, 0xb164, 0xc103, 0xc103, 0x91c5, 0x9184, 0x9963, 0x9943, 0xa943, 0xa984, 0xa1a4, 0x99a4, 0x4208, 0x4208, 0xc103, 0xc103, 0xa164, 0xa1a4, 0xc103, 0xc103, 0x8a05, 0x9206, 0x99a5, 0x9184, 0x8984, 0xa184, 0xa184, 0x89c5, 0x4208, 0x4208, 0xc103, 0xc103, 0x8984, 0xc103, 0xc103, 0x89a4, 0x79e5, 0x9226, 0x99c5, 0x9964, 0xa163, 0xa963, 0xb143, 0x9964, 0x4208, 0x4208, 0x4208, 0xc103, 0xc0e2, 0x4208, 0xc0e2, 0xc102, 0x4208}; Image Mur = Image(MurData);
const uint16_t SolData[] = {8, 8, 1, 1, 0, 0, 0x4208, 0x4208, 0x4208, 0x4208, 0x4208, 0x4208, 0x4208, 0x4208, 0x4208, 0x8410, 0x8410, 0x8410, 0x8410, 0x8410, 0x8410, 0x8410, 0x4208, 0x8410, 0x8410, 0x8410, 0x8410, 0x8410, 0x8410, 0x8410, 0x4208, 0x8410, 0x8410, 0x8410, 0x8410, 0x8410, 0x8410, 0x8410, 0x4208, 0x8410, 0x8410, 0x8410, 0x8410, 0x8410, 0x8410, 0x8410, 0x4208, 0x8410, 0x8410, 0x8410, 0x8410, 0x8410, 0x8410, 0x8410, 0x4208, 0x8410, 0x8410, 0x8410, 0x8410, 0x8410, 0x8410, 0x8410, 0x4208, 0x8410, 0x8410, 0x8410, 0x8410, 0x8410, 0x8410, 0x8410}; Image Sol = Image(SolData);
const char TailleSprite = 8;
void DessineNiveau(uint8_t Xp,uint8_t Yp) { char X_camera; // Position en à gauche de la zone affichée char Y_camera; // Position en haut de la zone affichée char X_max; // Fin de la zone à afficher char Y_max;
// Calcul de la position de la zone à afficher if (Xp > 4) { X_camera = Xp - 4; } else { X_camera = 0; }
if (Yp > 4) { Y_camera = Yp - 4; } else { Y_camera = 0; }
if (Xp <= NB_COLONNES_NIVEAUX - 3) { X_max = Xp + 3; } else { X_max = NB_COLONNES_NIVEAUX; }
if (Yp <= NB_LIGNES_NIVEAUX - 3) { Y_max = Yp + 3; } else { Y_max = NB_LIGNES_NIVEAUX; }
for (int ligne = Y_camera; ligne < Y_max; ligne++) { for (int colonne = X_camera; colonne < X_max; colonne++) { DessineSprite(niveau[ligne][colonne], colonne - X_camera + 1 , ligne - Y_camera + 1); } } }
void DessineSprite(char Type, char Xs, char Ys) { switch (Type) { case ' ' : gb.display.drawImage(Xs * TailleSprite, Ys * TailleSprite, Sol); break; case '#' : gb.display.drawImage(Xs * TailleSprite, Ys * TailleSprite, Mur); break; case '.' : gb.display.drawImage(Xs * TailleSprite, Ys * TailleSprite, Cible); break; case '$' : gb.display.drawImage(Xs * TailleSprite, Ys * TailleSprite, Caisse); break; case '*' : gb.display.drawImage(Xs * TailleSprite, Ys * TailleSprite, Caisse_sur_cible); break; // Pour le moment, on n'affiche pas le personnage (non géré) case '@' : gb.display.drawImage(Xs * TailleSprite, Ys * TailleSprite, Sol); break; case '+' : gb.display.drawImage(Xs * TailleSprite, Ys * TailleSprite, Cible); break; } // end Switch }
Je ne rentre pas plus dans le détail, c'est assez simple mais si vous avez des questions, j'y répondrais avec plaisir.
Bon là vous allez avoir 2 occasions de vous tester. Dans ce premier "A vous de jouer", essayez de mettre à jour le fichier .h comme il convient en rajoutant uniquement ce dont on a besoin.
Bon ce n'était pas sorcier, non ?
Voici le code correspondant à Graphics.h
#ifndef GRAPHICS_H #define GRAPHICS_Hextern const char TailleSprite;
extern void DessineNiveau(uint8_t Xp,uint8_t Yp);
#endif
Élémentaire non.
Bon, on passe la vitesse supérieure. Avec ce que l'on a vu dans les 2 tutos, votre mission si vous l'acceptez et de dessiner un sprite pour le personnage en 8x8 et de les intégrer dans un module dédié que l'on appellera Joueur puis de rajouter une procédure qui affichera la sprite du joueur.
Si vous voulez aller plus loin, vous pouvez rajouter la gestion des touches et changer le sprite du joueur en fonction de son sens de déplacement (vers le haut, le bas, la gauche ou la droite)
La suite au prochain numéro ... mais en attendant j'attends vos commentaires avec impatience, vos propositions d'amélioration, corrections et proposition de code pour le "A vous de jouer n°2"
Retrouvez la partie 1 de ce tutoriel ici: Partie 1
ou continuez avec la partie 3: Partie 3