Image qui ne s'anime pas

Bonjour,

Je suis nouveau en ce qui concerne la programmation avec la Gamebuino.
Voici mon projet sur GitHub je travaille depuis “develop”
Mon but c’est de pouvoir afficher un personnage que l’on peut changer via des flùches directionnels et en afficher un autre avec son nom inscrit en bas. Pour cela je travail avec deux fichiers principalement :
UndertaleBuino.ino :

#include <Gamebuino-Meta.h>
#include "AfficherSans.h"
#include "AfficherPapyrus.h"
#include "AfficherUndyne.h"
#include <stdio.h>
#include <string.h>

#define MAX 3

// SCREEN 80x64

void setup() {
  gb.begin();
}

void loop() {
  // ACCUEIL
  titleScreen();
  drawFightMenu();
}
void drawFightMenu() {

  while (true) {
    if (gb.update()) {
      // MENU COMBAT
      if (gb.buttons.pressed(BUTTON_A) || gb.buttons.pressed(BUTTON_MENU)) {

        while (true) {
          if (gb.update()) {
            gb.display.clear();
            gb.display.setCursor(4, 0);
            gb.display.println("Choisis ton combat");

            afficherFlecheGauche();

            struct Personnage
            {
              int positionImgX;
              int positionImgY;
              char* nom;
              Image imgP;
            };
            // declaration de ma liste de Personnage
            struct Personnage array_perso[MAX] = {
              {17, 5, "UNDYNE", image_undyne},
              {26, 15, "SANS", image_sans},
              {17, 5, "PAPYRUS", image_papyrus}
            };

       //// CAS SIMPLE UTILISATION TABLEAU PERSONNAGES ////

              // SANS par défaut sera afficher

            int x = array_perso[1].positionImgX;
            int y = array_perso[1].positionImgY;
            Image persoImg = array_perso[1].imgP;
            char* nomP = array_perso[1].nom;

        //// PROTOTYPE BOUCLE MENU POUR AFFICHER PERSO ////
 
            for (int i = 0 ; i < MAX; i++) {

            // Celon le choix "gauche" ou "droit"
            // on remplace les variables qui affiche le perso
              if (gb.buttons.pressed(BUTTON_RIGHT)) {
                x = array_perso[2].positionImgX;
                y = array_perso[2].positionImgY;
                persoImg = array_perso[2].imgP;
                nomP = array_perso[2].nom;
              }
              else if (gb.buttons.pressed(BUTTON_LEFT)) {
                x = array_perso[0].positionImgX;
                y = array_perso[0].positionImgY;
                persoImg = array_perso[0].imgP;
                nomP = array_perso[0].nom;
              }

            }
            
            afficherPerso(x, y, persoImg);
            drawTexteEffetBug(31, 53, nomP);
            afficherFlecheDroite();


          }
        }

      }
    }
  }

}

void drawTexteEffetBug(int customX, int customY, const char* texte) {
  gb.display.cursorX = customX + random(0, 2);
  gb.display.cursorY = customY + random(0, 2);
  gb.display.println(texte);
}

void afficherPerso(int customX, int customY, Image imgP) {
  gb.display.drawImage(customX, customY, imgP);
}    

et AfficherSans.h qui contient la DATA de l’image :

#ifndef AfficherSans.h
#define AfficherSans.h

const uint16_t SANS_DATA[] = {
  26,     // frame width
  32,     // frame height
  9,      // number of frames
  2,      // animation speed
  0xf81f, // transparent color
  0,      // RGB565 color mode
  // frame 1/9
  0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
  // frame 2/9
  0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0xffff, 0x0000, 0x0000, 0x0000, 0xffff, 
};


Image image_sans(SANS_DATA);

// marche mais que si on la declare ici et que on l'appel
// depuis le main
// void afficherSans() {
//  gb.display.drawImage(26, 15, image_sans);
//}
#endif    

Mon problĂšme c’est que la variable et bien prise en compte mais ne s’anime pas, alors que si je crĂ©e la mĂ©thode AfficherSans() et que je l’appel depuis le fichier main sa marche
je comprend pas, et je ne veux pas sa car pour la suite j’aurais besoin de bouger la position du personnage, tout en etant animĂ©.

En vous remerciant de votre aide.

dĂ©jĂ , ça ne devrait pas ĂȘtre dans ta boucle :

            struct Personnage
            {
              int positionImgX;
              int positionImgY;
              char* nom;
              Image imgP;
            };
            // declaration de ma liste de Personnage
            struct Personnage array_perso[MAX] = {
              {17, 5, "UNDYNE", image_undyne},
              {26, 15, "SANS", image_sans},
              {17, 5, "PAPYRUS", image_papyrus}
            };

ensuite ta structure contient une instance d’image, non un pointeur vers une instance,
bref au remplissage de “array_perso” tu copy “image_sans” et ne pointe donc pas sur l’image d’origine,
donc quant tu l’affiche, le code d’avancement de l’anim va toucher la copie et non l’original
pour couronner le tout Ă  chaque itĂ©ration de ta boucle tu recrĂ©e ton tableau et donc recopie l’objet original, avec son index de frame d’origine

donc, dĂ©jĂ  crĂ©Ă© ta structure en dehors du main et ton tableau soit au mĂȘme endroit soit au dĂ©but du code qui le nĂ©cessitera, mais surtout en dehors de ta boucle

modifie ta structure pour avoir un pointeur vers l’image d’origine et assigne l’objet d’origine comme valeur de ce pointeur

            struct Personnage {
...
              Image * imgP;
            };
            // declaration de ma liste de Personnage
            struct Personnage array_perso[MAX] = {
              {17, 5, "UNDYNE", &image_undyne},
              {26, 15, "SANS", &image_sans},
              {17, 5, "PAPYRUS", &image_papyrus}
            };

ensuite modifie tout le code s’y rĂ©fĂ©rent,

            Image persoImg = array_perso[1].imgP;

=>

            Image *persoImg = array_perso[1].imgP;

et :

void afficherPerso(int customX, int customY, Image imgP) {
  gb.display.drawImage(customX, customY, imgP);
}

=>

void afficherPerso(int customX, int customY, Image *imgP) {
  gb.display.drawImage(customX, customY, imgP);
}
2 Likes

J’ai modifier comme demandĂ©, mon struct et mon tableau je le met en dehors de mon loop() et de mon setup(), puis pour le pointeur j’ai fait comme suit :

#define MAX 3
struct Personnage
{
  int positionImgX;
  int positionImgY;
  char* nom;
  Image *imgP;
};
// declaration de ma liste de Personnage
struct Personnage array_perso[MAX] = {
  {17, 5, "UNDYNE", &image_undyne},
  {26, 15, "SANS", &image_sans},
  {17, 5, "PAPYRUS", &image_papyrus}
};

// SCREEN 80x64

void setup() {
  gb.begin();
}

void loop() {
  // ACCUEIL
  titleScreen();
  drawFightMenu();
}
void drawFightMenu() {

  while (true) {
    if (gb.update()) {
      // MENU COMBAT
      if (gb.buttons.pressed(BUTTON_A) || gb.buttons.pressed(BUTTON_MENU)) {

        while (true) {
          if (gb.update()) {
            gb.display.clear();
            gb.display.setCursor(4, 0);
            gb.display.println("Choisis ton combat");

            afficherFlecheGauche();



            //// CAS SIMPLE UTILISATION TABLEAU PERSONNAGES ////

            // SANS par défaut sera afficher

            int x = array_perso[1].positionImgX;
            int y = array_perso[1].positionImgY;
            Image *persoImg = array_perso[1].imgP;
            char* nomP = array_perso[1].nom;

            //// PROTOTYPE BOUCLE MENU POUR AFFICHER PERSO ////

            for (int i = 0 ; i < MAX; i++) {

              // Celon le choix "gauche" ou "droit"
              // on remplace les variables qui affiche le perso
              if (gb.buttons.pressed(BUTTON_RIGHT)) {
                x = array_perso[2].positionImgX;
                y = array_perso[2].positionImgY;
                persoImg = array_perso[2].imgP;
                nomP = array_perso[2].nom;
              }
              else if (gb.buttons.pressed(BUTTON_LEFT)) {
                x = array_perso[0].positionImgX;
                y = array_perso[0].positionImgY;
                persoImg = array_perso[0].imgP;
                nomP = array_perso[0].nom;
              }

            }
           
            afficherPerso(x, y, persoImg);
            drawTexteEffetBug(31, 53, nomP);
            afficherFlecheDroite();


          }
        }

      }
    }
  }

}

void afficherPerso(int customX, int customY, Image *imgP) {
  gb.display.drawImage(customX, customY, imgP);
}

Des erreurs sruviennes sur le typage de l’image et le pointeur que je lui met :



void afficherPerso(int customX, int customY, Image *imgP) {
  gb.display.drawImage(customX, customY, *imgP);
}

visiblement le compilo va de lui mĂȘme rĂ©cupĂ©rer l’adresse par un passage par rĂ©fĂ©rence 


void Display_ST7735::drawImage(int16_t x, int16_t y, Image& img){

le passage par rĂ©fĂ©rence cache pas mal les choses, il est possible qu’il y ai d’autres tricks du genre qui invaliderais des trucs que j’ai dis plus haut, mais bref aucune idĂ©e je me dĂ©merde en C pas C++ :stuck_out_tongue:

Mmmmh la gamebuino permet de codé en C et C++, mais je pense que si la compilation ne fonctionne pense et que le passage en ref est faite pas la console le langage changera probablement pas la donne
bon je suis bloqué, mais merci pour ton aide !

ben je te l’ai donnĂ© la rĂ©ponse, c’est de passer

  gb.display.drawImage(customX, customY, imgP);

en

  gb.display.drawImage(customX, customY, *imgP);

car le “langage” lui mĂȘme va rĂ©cupĂ©rer l’adresse

(hier j’avais prĂ©sumĂ© logiquement qu’il fallait donner un pointeur vers une classe image, pas directement l’objet image 


1 Like

Je pense qu’il faudrait que Steph remette l’explication sur l’usage des pointeurs. Je crois qu’il l’avait fait mais je n’ai pas retrouvĂ© le lien vers le post oĂč c’était expliquĂ© (il me semble, si ma mĂ©moire est bonne).
Ca fait un moment, je ne sais plus si on en a parlĂ© dans l’acadĂ©mie.

1 Like

Merci a toi r043v, tout marche a merveille sa fait 1 semaine que je suis sur le problĂšme et ton explication avec les pointeurs m’a vraiment aidĂ©, mon code compile ! Et mon image s’anime, merci.
Oui jicehel, ce serait vraiment cool un cours sur les pointeurs !

1 Like

Comme en langage C, le langage C++ permet d’utiliser des pointeurs pour manipuler des donnĂ©es, mais il introduit aussi le concept de rĂ©fĂ©rence, trĂšs pratique pour permettre la modification d’une donnĂ©e passĂ©e en paramĂštre d’une fonction.

DĂ©finition d’un pointeur

Un pointeur est une variable contenant l’adresse d’une autre variable d’un type donnĂ©. La notion de pointeur fait souvent peur car il s’agit d’une technique de programmation trĂšs puissante, permettant de dĂ©finir des structures dynamiques, c’est-Ă -dire qui Ă©volue au cours du temps (par opposition aux tableaux par exemple qui sont des structures de donnĂ©es statiques, dont la taille est figĂ©e Ă  la dĂ©finition).

Comprendre la notion d’adresse

Comme nous l’avons vu, un pointeur est une variable qui permet de stocker une adresse, il est donc nĂ©cessaire de comprendre ce qu’est une adresse.

Lorsque l’on exĂ©cute un programme, celui-ci est stockĂ© en mĂ©moire, cela signifie que d’une part le code Ă  exĂ©cuter est stockĂ©, mais aussi que chaque variable que l’on a dĂ©fini Ă  une zone de mĂ©moire qui lui est rĂ©servĂ©e, et la taille de cette zone correspond au type de variable que l’on a dĂ©clarĂ©.
En rĂ©alitĂ© la mĂ©moire est constituĂ©e de plein de petites cases de 8 bits (un octet). Une variable, selon son type (donc sa taille), va ainsi occuper une ou plusieurs de ces cases (une variable de type char occupera une seule case, tandis qu’une variable de type long occupera 4 cases consĂ©cutives).

Chacune de ces « cases » (appelĂ©es blocs ) est identifiĂ©e par un numĂ©ro. Ce numĂ©ro s’appelle adresse .

On peut donc accéder à une variable de 2 façons :

  • grĂące Ă  son nom
  • grĂące Ă  l’adresse du premier bloc allouĂ© Ă  la variable

Il suffit donc de stocker l’adresse de la variable dans un pointeur (il est prĂ©vu pour cela) afin de pouvoir accĂ©der Ă  celle-ci (on dit que l’on « pointe vers la variable »).

Le schĂ©ma ci-dessus montre par exemple par quel mĂ©canisme il est possible de faire pointer une variable (de type pointeur ) vers une autre. Ici le pointeur stockĂ© Ă  l’adresse 24 pointe vers une variable stockĂ©e Ă  l’adresse 253 (les valeurs sont bien Ă©videmment arbitraires).

Comment connaüt-on l’adresse d’une variable ?

En rĂ©alitĂ© vous n’aurez jamais Ă  Ă©crire l’adresse d’une variable, d’autant plus qu’elle change Ă  chaque lancement de programme Ă©tant donnĂ© que le systĂšme d’exploitation alloue les blocs de mĂ©moire qui sont libres, et ceux-ci ne sont pas les mĂȘmes Ă  chaque exĂ©cution.

Ainsi, il existe une syntaxe permettant de connaütre l’adresse d’une variable, connaissant son nom :
il suffit de faire prĂ©cĂ©der le nom de la variable par le caractĂšre & (« ET commercial ») pour dĂ©signer l’adresse de cette variable :

&Nom_de_la_variable

IntĂ©rĂȘt des pointeurs

Les pointeurs ont un grand nombre d’intĂ©rĂȘts :

  • Ils permettent de manipuler de façon simple des donnĂ©es pouvant ĂȘtre importantes (au lieu de passer Ă  une fonction un Ă©lĂ©ment trĂšs grand (en taille) on pourra par exemple lui fournir un pointeur vers cet Ă©lĂ©ment
)
  • Les tableaux ne permettent de stocker qu’un nombre fixĂ© d’élĂ©ments de mĂȘme type. En stockant des pointeurs dans les cases d’un tableau, il sera possible de stocker des Ă©lĂ©ments de taille diverse, et mĂȘme de rajouter des Ă©lĂ©ments au tableau en cours d’utilisation (c’est la notion de tableau dynamique qui est trĂšs Ă©troitement liĂ©e Ă  celle de pointeur)
  • Il est possible de crĂ©er des structures chaĂźnĂ©es, c’est-Ă -dire comportant des maillons

DĂ©claration d’un pointeur

Un pointeur est une variable qui doit ĂȘtre dĂ©finie en prĂ©cisant le type de variable pointĂ©e, de la façon suivante :

type * Nom_du_pointeur

Le type de variable pointĂ©e peut ĂȘtre aussi bien un type primaire (tel que int , char 
) qu’un type complexe (tel que struct 
).

Un pointeur doit OBLIGATOIREMENT ĂȘtre typĂ© !

GrĂące au symbole ‘*’ le compilateur sait qu’il s’agit d’une variable de type pointeur et non d’une variable ordinaire, de plus, Ă©tant donnĂ© que vous prĂ©cisez (obligatoirement) le type de variable, le compilateur saura combien de blocs suivent le bloc situĂ© Ă  l’adresse pointĂ©e.

Initialisation d’un pointeur

AprĂšs avoir dĂ©clarĂ© un pointeur il faut l’intialiser. Cette dĂ©marche est trĂšs importante car lorsque vous dĂ©clarez un pointeur, celui-ci contient ce que la case oĂč il est stockĂ© contenait avant, c’est-Ă -dire n’importe quel nombre. Autrement dit, si vous n’initialisez pas votre pointeur, celui-ci risque de pointer vers une zone hasardeuse de votre mĂ©moire, ce qui peut ĂȘtre un morceau de votre programme ou
 de votre systĂšme d’exploitation !

Un pointeur non initialisé représente un danger !

Pour initialiser un pointeur, il faut utiliser l’opĂ©rateur d’affectation ‘=’ suivi de l’opĂ©rateur d’adresse ‘&’ auquel est accollĂ© un nom de variable (celle-ci doit bien sĂ»r avoir Ă©tĂ© dĂ©finie avant
) :

Nom_du_pointeur = &nom_de_la_variable_pointee;

Par exemple :

int a = 2; char b; int *p1; char *p2; p1 = &a; p2 = &b;

Accéder à une variable pointée

AprĂšs (et seulement aprĂšs) avoir dĂ©clarĂ© et initialisĂ© un pointeur, il est possible d’accĂ©der au contenu de l’adresse mĂ©moire pointĂ©e par le pointeur grĂące Ă  l’opĂ©rateur ‘*’. La syntaxe est la suivante :

*pointeur

Par exemple :

int a = 2; *p1 = 10; *p2 = ‘a’;

AprĂšs ces deux instructions, le contenu des variables p1 et p2 sera respectivement 10 et 97 (61 en hexadĂ©cimal, le code ASCII associĂ© au caractĂšre ‘a’).

Si vous dĂ©sirez utiliser cette notation dans une expression plus complexe, il sera nĂ©cessaire d’employer des parenthĂšses :
Par exemple :

a = (*p) + 2;

Passage d’argument à une fonction par adresse

Lorsque l’on passe une variable en paramĂštre d’une fonction, cette derniĂšre utilise une copie de la variable lorsqu’elle effectue des opĂ©rations sensĂ©es la modifier, c’est-Ă -dire qu’en sortie de la fonction, une variable passĂ©e en paramĂštre n’est pas modifiĂ©e. Cela provient du fait que les variables utilisĂ©es dans la fonction ont comme portĂ©e la portĂ©e de la fonction. Or une variable ne peut ĂȘtre manipulĂ©e que dans la portĂ©e dans laquelle elle est dĂ©finie


Une premiÚre solution consiste à retourner la valeur de la variable modifiée et de la stocker par affectation dans la variable :
Par exemple :

int Ajout2(int a){ a +=2; return a; } int b = 3; b = Ajout2(b);

Toutefois, il se peut que l’on destine le retour de valeur Ă  une autre opĂ©ration, auquel cas l’astuce ci-dessus n’est plus suffisante.

Une solution consiste Ă  utiliser un pointeur vers la variable en paramĂštre, on parle alors de passage de paramĂštres par pointeur ou passage de paramĂštres par adresse . De cette façon la fonction est Ă  mĂȘme d’accĂ©der directement Ă  la variable, donc de la modifier. Pour cela, il s’agit de dĂ©clarer un paramĂštre de type pointeur, et passer l’adresse de la variable au lieu de passer la variable elle-mĂȘme comme dans le cas du passage de paramĂštre par valeur .

L’exemple prĂ©cĂ©dent ressemblerait alors Ă  ceci :

int Ajout2(int * a){ *a +=2; } int b = 3; Ajout2(&b);

Passage d’argument par rĂ©fĂ©rence

Le langage C++ apporte les avantages du passage par pointeur avec la simplicitĂ© du passage par valeur grĂące au concept novateur de rĂ©fĂ©rence. Une rĂ©fĂ©rence (n’ayant aucun sens en langage C) permet de faire « rĂ©fĂ©rence » Ă  des variables existant dans une autre portĂ©e, par exemple manipuler une variable situĂ©e dans une fonction Ă  partir d’une autre fonction.

La dĂ©claration d’une rĂ©fĂ©rence se fait simplement en intercalant une esperluette (le caractĂšre & , appelĂ© aussi ET commercial ) entre le type de la variable et son nom :

type & Nom_de_la_variable = valeur;

* Une rĂ©fĂ©rence doit obligatoirement ĂȘtre initialisĂ©e lors de sa dĂ©claration !

  • Le concept de rĂ©fĂ©rence ne doit en aucun cas ĂȘtre confondu avec celui d’adresse mĂȘme si les deux notions utilisent le caractĂšre &

Le passage par rĂ©fĂ©rence consiste tout simplement Ă  dĂ©finir une rĂ©fĂ©rence pour une variable et de la passer en paramĂštre d’une fonction.

Voici l’exemple prĂ©cĂ©dent mettant en Ɠuvre l’utilisation de rĂ©fĂ©rence :

int Ajout2(int &); int Ajout2(int & a){ a +=2; } int b = 3; Ajout2(b);

1 Like

@Shainee_Khaldi nouveau en Gamebuino mais pas nouveau en programmation apparemment! Les pointeurs c’est dĂ©jĂ  costaud. Personnellement je les ai appris mais jamais intĂ©grĂ© dans mes jeux. Merci Ă  @r043v et Ă  @jicehel d’ĂȘtre aussi actifs pour aider.

2 Likes