Cours:Idees OOP

De troyesGEII
Aller à : navigation, rechercher

Introduction aux objets avec Processing

Programmation orientée-objet (version sympathique) peut être lue en guise de cours.

Exercice 1

Refaire les exemples développés dans la Playlist Youtube de Daniel Shiffman. On commencera par la classe Bubble développée dans le troisième cours.

Préparation à l'exercice suivant

Processing : Les Objets peut être consulté pour un rappel sur les objets. Nous allons continuer avec les tableaux ou listes.

Les listes (ou tableaux) vous propose des tableaux sur types simples (ou presque).

Pour aller plus loin, les listes d'objets sont abordées avec « Programmation orientée-objet (version sympathique) » en y cherchant la section Liste d'objets.

Array Objects montre une autre syntaxe pour les boucles for.

Exercice 2

Question 1

Transformer le code ci-dessous en une classe Processing pour utilisation.

Indications : on vous donne le code permettant un affichage d'un seul digit

int[] val={63,6,91,79,102,109,125,7,127,111,119,124,57,94,121,113};

void segment() {
  beginShape();
  vertex(0,10);
  vertex(10,0);
  vertex(50,0);
  vertex(60,10);
  vertex(50,20);
  vertex(10,20);
 endShape(CLOSE); 
}

void affiche7segs(int val,int rouge,int vert, int bleu) {
  pushMatrix(); // on désire laisser le repère inchangé
  noStroke();
  translate(40,0);
  if ((val & 1) == 1) fill(rouge,vert,bleu); else fill(200);
  segment(); // segment a
  translate(-10,70);
  if ((val & 64) == 64) fill(rouge,vert,bleu); else fill(200);
  segment(); // segment g
  translate(-10,70);
  if ((val & 8) == 8) fill(rouge,vert,bleu); else fill(200);
  segment(); // segment d
  translate(15,-53);
  rotate(radians(100));
  if ((val & 16) == 16) fill(rouge,vert,bleu); else fill(200);
  segment(); // segment e
  translate(-70,0);
  if ((val & 32) == 32) fill(rouge,vert,bleu); else fill(200);
  segment(); // segtment f
  translate(-12,-68);
  if ((val & 2) == 2) fill(rouge,vert,bleu); else fill(200);
  segment(); // segment b
  translate(72,0);
  if ((val & 4) == 4) fill(rouge,vert,bleu); else fill(200);
  segment(); // segment c
  popMatrix(); // on récupère le repère initial
}

Réponse à la question 1 (Exercice 2)

Voici la définition de classe qui est un peu différente de celle présentée en cours.

 
class Digit {
   private int[] val={63,6,91,79,102,109,125,7,127,111,119,124,57,94,121,113};
   int nb1digit;
   int x,y;
   Digit(){
     nb1digit = val[0];
     x=0;y=0;
   }
   Digit(int nb){
     x=0;y=0;
     if (nb < 16)
       nb1digit = val[nb];
     else
       nb1digit=64; // affichage tiret en cas d'erreur
   }
   
   Digit(int xd, int yd){
     nb1digit = val[0];
     x=xd;y=yd;
   }
   
   void put(int nb) {
     if (nb < 16)
       nb1digit = val[nb];
     else
       nb1digit=64; // affichage tiret en cas d'erreur
   }

  private void segment() {
    beginShape();
    vertex(0,10);
    vertex(10,0);
    vertex(50,0);
    vertex(60,10);
    vertex(50,20);
    vertex(10,20);
   endShape(CLOSE); 
  }

void affiche7segs(int rouge,int vert, int bleu) {
  pushMatrix(); // on désire laisser le repère inchangé
  noStroke();
  translate(40+x,0+y);
  if ((nb1digit & 1) == 1) fill(rouge,vert,bleu); else fill(200);
  segment(); // segment a
  translate(-10,70);
  if ((nb1digit & 64) == 64) fill(rouge,vert,bleu); else fill(200);
  segment(); // segment g
  translate(-10,70);
  if ((nb1digit & 8) == 8) fill(rouge,vert,bleu); else fill(200);
  segment(); // segment d
  translate(15,-53);
  rotate(radians(100));
  if ((nb1digit & 16) == 16) fill(rouge,vert,bleu); else fill(200);
  segment(); // segment e
  translate(-70,0);
  if ((nb1digit & 32) == 32) fill(rouge,vert,bleu); else fill(200);
  segment(); // segtment f
  translate(-12,-68);
  if ((nb1digit & 2) == 2) fill(rouge,vert,bleu); else fill(200);
  segment(); // segment b
  translate(72,0);
  if ((nb1digit & 4) == 4) fill(rouge,vert,bleu); else fill(200);
  segment(); // segment c
  popMatrix(); // on récupère le repère initial
}

}

Et voici maintenant un exemple de programme d'utilisation :

Digit digitpoids0;

void setup() {  
  digitpoids0 = new Digit(50,50);
  frameRate(1); 
  size(480, 400);
}

int i=0;

void draw() {  
  i = (++i %16);
  digitpoids0.put(i);
  digitpoids0.affiche7segs(255,0,0);
}

FIN DE LA CORRECTION de la question 1

Question 2

Faire fonctionner la correction de la question 1. Puis réaliser un affichage de compteur binaire sur deux digits.

Correction de la question 2

La classe digit est inchangée par rapport à la question 1. On ne donne donc que le programme principal.

Digit digitpoids0,digitpoids1;

void setup() {  
  digitpoids0 = new Digit(150,50);
  digitpoids1 = new Digit(50,50);
  frameRate(1); 
  size(480, 400);
}

int i=0;

void draw() {  
  i++;
  digitpoids0.put(i%16);
  digitpoids0.affiche7segs(255,0,0);
  digitpoids1.put((i/16)%16);
  digitpoids1.affiche7segs(255,0,0);
}

Question 3

Réaliser un affichage de 4 digits destiné à afficher l'heure et les minutes d'un réveil. A ce stade, on ne vous demande pas de réaliser un compteur heures/minutes. En effet l'arithmétique heures/minutes est très particulière et on vous donnera de l'aide pour la réaliser. Vous pouvez par contre réaliser un compteur décimal qui affichera donc sur 4 digits.

Question 4

Modifier la classe pour pouvoir changer la taille de chaque digit (!!! ANNULE !!!)

Question 5

Réaliser une classe "HHMMClass" de 2x2 digits séparés par deux points.

Indication : voici le début de la classe. Il ne vous reste qu'à positionner les ':' et probablement être capable de positionner cet ensemble de digits si vous voulez un jour afficher l'heure courante et l'heure réveil.

class HHMMClass {
  int posX,posY;
  int hh_mm;
  Digit[] digits = new Digit[4];
  HHMMClass() {
    posX=posY=0;
    hh_mm = 0x2340;
    for(int i=0;i<4;i++)
      digits[i]=new Digit(100*i,50);
  }
  void affiche() {
    for(int i=0;i<4;i++) {
      digits[3-i].put(hh_mm >> (4*i) & 0xF);
      digits[3-i].affiche7segs(255,0,0);
    }
  }
  void incrementHHMM() {
      (hh_mm)++;
      if ((hh_mm & 0x000F) > 0x0009)
        hh_mm += 0x0006;
      if ((hh_mm & 0x00F0) > 0x0050)
        hh_mm += 0x00A0;
      if ((hh_mm & 0x0F00) > 0x0900)
        hh_mm += 0x0600;
      if ((hh_mm & 0xFF00) > 0x2300)
        hh_mm = 0x0000;
  }
  void decrementHHMM() {
      (hh_mm)--;
      if ((hh_mm & 0x000F) == 0x000F)
      hh_mm -= 0x0006;
      if ((hh_mm & 0x00F0) == 0x00F0)
      hh_mm -= 0x00A0;
      if ((hh_mm & 0x0F00) == 0x0F00)
      hh_mm -= 0x0600;
      if ((hh_mm & 0xFFFF) > 0x2359)
      hh_mm = 0x2359;
  }
}

Faire vos propres essais avant de passer à la question suivante. L'utilisation de cette classe nécessite l'utilisation de la classe digit avec laquelle elle possède un lien fort.

Question 6

Utiliser cette classe pour afficher l'heure courante. On utilisera

Le code d'une horloge à aiguilles est disponible sur Internet.

Correction question 6

Voici la nouvelle classe HHMMClass :

class HHMMClass {
  int posX,posY;
  int hh_mm;
  Digit[] digits = new Digit[4];
  HHMMClass() {
    posX=posY=0;
    hh_mm = 0x950;
    for(int i=0;i<4;i++) {
      if (i < 2)
        digits[i]=new Digit(100*i,50);
      else
        digits[i]=new Digit(100*(i+1),50);
    }
  }
  void affiche() {
    for(int i=0;i<4;i++) {
      digits[3-i].put(hh_mm >> (4*i) & 0xF);
      digits[3-i].affiche7segs(255,0,0);
    }
    if ((second() % 2) == 1)
      fill(255,0,0);
    else 
      fill(200);
      
    ellipse(260,100,20,20);
    ellipse(255,150,20,20);
  }
  
  void updateHHMM() {
    int temp;
    temp = hour();
    hh_mm = ((temp/10) << 12)+((temp%10)<<8);
    temp = minute();
    hh_mm += ((temp/10) << 4)+(temp%10);
  }
  void incrementHHMM() {
      (hh_mm)++;
      if ((hh_mm & 0x000F) > 0x0009)
        hh_mm += 0x0006;
      if ((hh_mm & 0x00F0) > 0x0050)
        hh_mm += 0x00A0;
      if ((hh_mm & 0x0F00) > 0x0900)
        hh_mm += 0x0600;
      if ((hh_mm & 0xFF00) > 0x2300)
        hh_mm = 0x0000;
  }
  void decrementHHMM() {
      (hh_mm)--;
      if ((hh_mm & 0x000F) == 0x000F)
      hh_mm -= 0x0006;
      if ((hh_mm & 0x00F0) == 0x00F0)
      hh_mm -= 0x00A0;
      if ((hh_mm & 0x0F00) == 0x0F00)
      hh_mm -= 0x0600;
      if ((hh_mm & 0xFFFF) > 0x2359)
      hh_mm = 0x2359;
  }
}

Il serait bon de modifier la méthode affiche() pour qu'elle puisse changer la couleur d'affichage.

La classe Digit n'a pas changée.

Et voici maintenant le programme principal :

HHMMClass heureC;
void setup() {
  heureC = new HHMMClass();
  frameRate(10);
  size(520,400);
}

void draw() {
  heureC.updateHHMM();
  heureC.affiche();
}

Question 7

Modifier la classe HHMMClass pour pouvoir afficher les heures et les minutes à un endroit précis. Cela devrait ainsi vous permettre d'afficher à la fois l'heure courante et l'heure de réveil.

Correction question 7

Nous avons ajouté la possibilité de déplacer l'affichage des 4 digits ainsi que leur grandeur.

Nouvelle classe HHMM :

class HHMMClass {
  int posX,posY;
  int hh_mm;
  int rouge,vert,bleu;
  float zoom;
  Digit[] digits = new Digit[4];
  HHMMClass() {
    posX=posY=0;
    hh_mm = 0x950;
    zoom=1.0;
    for(int i=0;i<4;i++) {
      if (i < 2)
        digits[i]=new Digit(100*i,50);
      else
        digits[i]=new Digit(100*(i+1),50);
    }
  }
  HHMMClass(int x,int y) {
    posX= x;
    posY=y;
    hh_mm = 0x950;
    for(int i=0;i<4;i++) {
      if (i < 2)
        digits[i]=new Digit(100*i+posX,50+posY);
      else
        digits[i]=new Digit(100*(i+1)+posX,50+posY);
    }
    zoom=1.0;
  }
  HHMMClass(int x,int y,int red,int green,int blue) {
    posX= x;
    posY=y;
    rouge = red;
    vert = green;
    bleu = blue;
    hh_mm = 0x950;
    for(int i=0;i<4;i++) {
      if (i < 2)
        digits[i]=new Digit(100*i+posX,50+posY);
      else
        digits[i]=new Digit(100*(i+1)+posX,50+posY);
    }
    zoom=1.0;
  }
  HHMMClass(int x,int y,int red,int green,int blue,float grossissement) {
    posX= x;
    posY=y;
    rouge = red;
    vert = green;
    bleu = blue;
    zoom = grossissement;
    hh_mm = 0x950;
    for(int i=0;i<4;i++) {
      if (i < 2)
        digits[i]=new Digit(100*i+posX,50+posY,zoom);
      else
        digits[i]=new Digit(100*(i+1)+posX,50+posY,zoom);
    }
  }
  void affiche() {
    for(int i=0;i<4;i++) {
      digits[3-i].put(hh_mm >> (4*i) & 0xF);
      digits[3-i].affiche7segs(rouge,vert,bleu);
    }
    if ((second() % 2) == 1)
      fill(rouge,vert,bleu);
    else 
      fill(200);
      
    ellipse((260+posX)*zoom,(100+posY)*zoom,20*zoom,20*zoom);
    ellipse((255+posX)*zoom,(150+posY)*zoom,20*zoom,20*zoom);
  }
  
  void updateHHMM() {
    int temp;
    temp = hour();
    hh_mm = ((temp/10) << 12)+((temp%10)<<8);
    temp = minute();
    hh_mm += ((temp/10) << 4)+(temp%10);
  }
  void incrementHHMM() {
      (hh_mm)++;
      if ((hh_mm & 0x000F) > 0x0009)
        hh_mm += 0x0006;
      if ((hh_mm & 0x00F0) > 0x0050)
        hh_mm += 0x00A0;
      if ((hh_mm & 0x0F00) > 0x0900)
        hh_mm += 0x0600;
      if ((hh_mm & 0xFF00) > 0x2300)
        hh_mm = 0x0000;
  }
  void decrementHHMM() {
      (hh_mm)--;
      if ((hh_mm & 0x000F) == 0x000F)
      hh_mm -= 0x0006;
      if ((hh_mm & 0x00F0) == 0x00F0)
      hh_mm -= 0x00A0;
      if ((hh_mm & 0x0F00) == 0x0F00)
      hh_mm -= 0x0600;
      if ((hh_mm & 0xFFFF) > 0x2359)
      hh_mm = 0x2359;
  }
}

La classe Digit a du être elle aussi modifiée :

class Digit {
   private int[] val={63,6,91,79,102,109,125,7,127,111,119,124,57,94,121,113};
   int nb1digit;
   int x,y;
   float zoom;
   Digit(){
     nb1digit = val[0];
     x=0;y=0;
     zoom=1.0;
   }
   Digit(int nb){
     x=0;y=0;
     zoom=1.0;
     if (nb < 16)
       nb1digit = val[nb];
     else
       nb1digit=64; // affichage tiret en cas d'erreur
   }
   
   Digit(int xd, int yd){
     nb1digit = val[0];
     x=xd;y=yd;
     zoom=1.0;
   }
   
   Digit(int xd, int yd, float grossissement){
     nb1digit = val[0];
     x=xd;y=yd;
     zoom=grossissement;
   }
   
   void put(int nb) {
     if (nb < 16)
       nb1digit = val[nb];
     else
       nb1digit=64; // affichage tiret en cas d'erreur
   }

  private void segment() {
    beginShape();
    vertex(0*zoom,10*zoom);
    vertex(10*zoom,0*zoom);
    vertex(50*zoom,0*zoom);
    vertex(60*zoom,10*zoom);
    vertex(50*zoom,20*zoom);
    vertex(10*zoom,20*zoom);
   endShape(CLOSE); 
  }

void affiche7segs(int rouge,int vert, int bleu) {
  pushMatrix(); // on désire laisser le repère inchangé
  noStroke();
  translate((40+x)*zoom,(0+y)*zoom);
  if ((nb1digit & 1) == 1) fill(rouge,vert,bleu); else fill(200);
  segment(); // segment a
  translate((-10)*zoom,70*zoom);
  if ((nb1digit & 64) == 64) fill(rouge,vert,bleu); else fill(200);
  segment(); // segment g
  translate(-10*zoom,70*zoom);
  if ((nb1digit & 8) == 8) fill(rouge,vert,bleu); else fill(200);
  segment(); // segment d
  translate(15*zoom,-53*zoom);
  rotate(radians(100));
  if ((nb1digit & 16) == 16) fill(rouge,vert,bleu); else fill(200);
  segment(); // segment e
  translate(-70*zoom,0*zoom);
  if ((nb1digit & 32) == 32) fill(rouge,vert,bleu); else fill(200);
  segment(); // segtment f
  translate(-12*zoom,-68*zoom);
  if ((nb1digit & 2) == 2) fill(rouge,vert,bleu); else fill(200);
  segment(); // segment b
  translate(72*zoom,0*zoom);
  if ((nb1digit & 4) == 4) fill(rouge,vert,bleu); else fill(200);
  segment(); // segment c
  popMatrix(); // on récupère le repère initial
}

}

Et le programme principal :

HHMMClass heureC;
void setup() {
  heureC = new HHMMClass(100,50,255,0,0,1.4);
  frameRate(10);
  size(900,400);
}

void draw() {
  heureC.updateHHMM();
  heureC.affiche();
}

Question 8

1°) Réaliser un affichage heure courante heure réveil

2°) Vous réaliserez ensuite un mécanisme de mise à jour de l'heure réveil. On pourra utiliser pour cela les fonctions souris :

et réaliser deux boutons UP et DOWN avec des rectangles.

Indications : La gestion des boutons serait mieux réalisée avec une classe Bouton. Nous vous en proposons une (assez simplifiée) :

class Bouton
{
  int etat;
  String label;
  int couleur; // en gris
  //int r,v,b; // couleur allumé
  int posX,posY,dX,dY;

  Bouton() {
    couleur = 84;
    posX=0; posY=0; dX=40; dY=40;
  }

  Bouton(int posx,int posy,String txt) {
    couleur = 84;
    posX=posx; posY=posy; dX=40; dY=40;
    label = txt;
  }

  void showOff() {
    fill(couleur);
    rect(posX,posY,dX,dY);
    textSize(24);
    fill(0,255,0);
    text(label,posX,posY+dY+24);
  }

  void showOn() {
    fill(couleur);
    rect(posX,posY,dX,dY);
    fill(0,192,0);
    ellipseMode(CORNER);
    ellipse(posX,posY,dX,dY);
    fill(0,255,0);
    ellipse(posX+5,posY+5,dX-10,dY-10);   
  }
  
  Boolean mouseIn() {
    if ((mouseX>posX) && (mouseX < posX+dX)
      && (mouseY>posY) && (mouseY < posY+dY)) 
         return true;
    else return false;
  }
  
}

Question 9

Pour finir, il ne vous reste qu'à faire le mécanisme qui fait sonner le réveil quand l'heure courante est égale à l'heure réveil. Vous pourrez vous inspirer de la correction trouvée dans la Wikiversité.

Indications :

  • Le bouton d'armement est un peu particulier puisqu'il mémorise son état. Il faut donc spécialiser la classe bouton de la question précédente.
  • Il existe une classe Sound pour processing, mais vous devez vérifier si elle est installée ou pas.


Question 10

Utiliser l'héritage pour fabriquer un bouton de type switch.

Indications :

  • Cours Association et héritage appliqué à notre problématique. Il y a la réponse dans l'exercice 6 de ce cours.
  • Et le code ressource :
    • La classe BoutonSuper
class BoutonSuper {
  String label;
  int couleur; // en gris
  int posX,posY,dX,dY;
  BoutonSuper() {
    couleur = 84;
    posX=0; posY=0; dX=40; dY=40;
  }
  BoutonSuper(int posx,int posy,String txt) {
    couleur = 84;
    posX=posx; posY=posy; dX=40; dY=40;
    label = txt;
  }
  void showOff() {
    fill(couleur);
    rect(posX,posY,dX,dY);
    strokeWeight(2);
    stroke(0);
    line(posX,posY+dY,posX+dX,posY+dY);
    line(posX+dX,posY,posX+dX,posY+dY);
    stroke(255);
    line(posX,posY,posX+dX,posY);
    line(posX,posY,posX+dX,posY);
    strokeWeight(1);
    noStroke();
    textSize(24);
    fill(0,255,0);
    text(label,posX,posY+dY+24);
  }
  void showOn() {    
  }
  Boolean mouseIn() {
    if ((mouseX>posX) && (mouseX < posX+dX)
      && (mouseY>posY) && (mouseY < posY+dY)) 
         return true;
    else return false;
  }
}
    • La classe Bouton
class Bouton extends BoutonSuper
{
    Bouton(int posx,int posy,String txt) {
    couleur = 84;
    posX=posx; posY=posy; dX=40; dY=40;
    label = txt;
  }
  void showOn() {
    fill(couleur);
    rect(posX,posY,dX,dY);
    fill(0,192,0);
    ellipseMode(CORNER);
    ellipse(posX,posY,dX,dY);
    fill(0,255,0);
    ellipse(posX+5,posY+5,dX-10,dY-10);
  }
}
    • La classe Switch
class Switch extends BoutonSuper
{ 
  int etat;
  Switch(int posx,int posy,String txt) {
    couleur = 84;
    posX=posx; posY=posy; dX=30; dY=40;
    label = txt;
    etat=0;
  }
  void showOff() {
    fill(couleur);
    rect(posX,posY,dX,dY);
    strokeWeight(2);
    stroke(0);
    line(posX,posY+dY,posX+dX,posY+dY);
    line(posX+dX,posY,posX+dX,posY+dY);
    stroke(255);
    line(posX,posY,posX+dX,posY);
    line(posX,posY,posX+dX,posY);
    strokeWeight(1);
    noStroke();
    textSize(24);
    fill(0,255,0);
    text(label,posX,posY+dY+24);
  }
  void switchEtat() {
    etat ^= 1;
  }
  void showOn() {
    fill(couleur);
    rect(posX,posY,dX,dY);
    fill(0,192,0);
    ellipseMode(CORNER);
    ellipse(posX,posY,dX,dY);
    fill(0,255,0);
    ellipse(posX+5,posY+5,dX-10,dY-10);
  }
  void show() {
    if (etat == 0) showOff(); else showOn();
  }
}
    • et un exemple d'utilisation :
Bouton bouton1 = new Bouton(50,50,"UP");
Switch switch1 = new Switch(150,50,"SW0");
void setup() {
  size(300,300);
  frameRate(10);
}
void draw() {
    bouton1.showOff();
    switch1.show();
}
void mousePressed() {
  if (bouton1.mouseIn()) {
      bouton1.showOn();
    } else if (switch1.mouseIn()) {
      switch1.switchEtat();
    }
}

Poursuivez avec l'exercice 7 du cours ci-dessus qui change seulement l'apparence du bouton.

Corrections de l'exercice 2

Éléments de correction

Projet Coronavirus

Suite à l'arrêt complet de l'enseignement le lundi 16 Mars 2020 pour une durée indéterminée, nous vous proposons le travail personnel suivant.

Pour ce projet, Travail 1 et Travail 2 ne représentent qu'une promenade de santé. Le vrai travail commence avec Travail 3 et Travail 4.

Code de départ

Voici le code de départ d'un jeu snake pratiquement fonctionnel mais comportant un certain nombre de défauts. Ce code est tiré du Forum Réalisation d'un jeu snake en 3D mais il s'agit naturellement d'un jeu 2D.

int colonnes=100;
int lignes=60;
 
ArrayList<TeteOuCorps> serpent;
 
int posX=0;
int posY=0;
Pomme pomme;
 
long  tpbouger=0;
long  tpbouger2=0;
 
int temps=150;
boolean commencer;
 
TeteOuCorps TETE ;
TeteOuCorps elmt;
 
int dir;
String mesag="";
int score=0;
int serpentTaille=2;
int pas=11;
 
// -----------------------------------------------------------
 
void setup()
{
  size(1000, 600);
  background(0, 0, 255);
 
  textAlign(CENTER);
  int posX=width/2;
  int posY=height/2;
 
  serpent=new ArrayList();
  serpent.add(new TeteOuCorps(posX, posY));
  serpent.add(new TeteOuCorps(posX-1*pas, posY));
  serpent.add(new TeteOuCorps(posX-2*pas, posY));
 
  AjoutPomme();
  mesag="Pour commencer, cliquez à l'intérieur de la grille.\n\nUtilisez les flèches du clavier pour diriger le serpent.";
 
  tpbouger=millis();
  tpbouger2=millis();
 
  DessinerGrille();
 
  posX=1;
  posY=0;
}
 
void draw()
{
  background(0, 0, 255);
 
  if (commencer)
  { 
    DessinerGrille();
    pomme.draw();
    DeplacerEtManger();
  }
  else
  {
    text(mesag, 500, 100);
  }
}
 
// -------------------------------------------------------------------
 
void DeplacerEtManger()
{
 
  //  TeteOuCorps elmt2 = serpent.get(serpent.size()-1);
  //  if ((elmt2.x<0)||(elmt2.x>=colonnes)||(elmt2.y<0)||(elmt2.y>=lignes))
  //  {
  //    serpent.clear(); // on vide le tableau
  //    posX = colonnes/2; // et on repositionne la tête du serpent au centre
  //    posY = lignes/2;
  //
  //    temps = 250; // on remet le temps à 250
  //  }
 
  println  (serpent.size());
 
 
  for (int i = serpent.size()-1; i >= 0; i--)
  {
    TeteOuCorps elmt = serpent.get(i);
    elmt.draw();
    //      if (elmt.touche(posX, posY)) 
    //      { 
    //        // serpent.clear(); // on vide le tableau
    //        posX = colonnes/2; // on repositionne le serpent au centre
    //        posY = lignes/2;
    //
    //        temps = 250; // on remet le tempsàa 250
    //        break; //  sortir prématurement de la boucle
    //      }
  } // for 
 
  TeteOuCorps elmt = serpent.get(0);
  if (pomme.touche(elmt.x, elmt.y))
  {
    score ++;         // un point de plus
    serpentTaille ++; // une taille en plus 
    temps -=1;        // on accelere la cadence
 
 
    //obtenir les deux dernier elmt
    TeteOuCorps elmt2 = serpent.get(serpent.size()-1);
    serpent.add(new TeteOuCorps(elmt2.x-2*pas, elmt2.y));
 
    AjoutPomme();
  }
 
  // move
  if (tpbouger2+400 < millis()) {
 
    if (posX!=0||posY!=0) {
      for (int i = serpent.size()-1; i > 0; i--) {
        elmt = serpent.get(i);
        TeteOuCorps elmt2 = serpent.get(i-1);
        elmt.x = elmt2.x;
        elmt.y = elmt2.y;
      } // for
    } // if
 
    elmt   =  serpent.get(0);
    elmt.x += posX * pas;
    elmt.y += posY * pas;
 
    tpbouger2 = millis();
  } // if 
 
  //score
  fill(255, 0, 0);
 
  textAlign(LEFT);
  text("Score : ", width -120, 25);
  text(score, width -50, 25);
} // func
 
void keyPressed()
{
  // reset 
  posX=0;
  posY=0;
  // get key 
  if (key==CODED)
  {
    if (keyCode==UP)
    {
      dir=1;
    }
    if (keyCode==DOWN)
    {
      dir=2;
    }
    if (keyCode==RIGHT)
    {
      dir=3;
    }
    if (keyCode==LEFT)
    {
      dir=4;
    }
    // on determine la position de la tête en fonction de la direction du serpent
    switch(dir)
    {
    case 1:
      posY=-1;
      break;
    case 2:
      posY=1;
      break;
    case 3:
      posX=1;
      break;
    case 4:
      posX=-1;
      break;
    }
  }
}
 
void DessinerGrille()
{
  background(0, 0, 255);
  stroke(255);
  for (int x =0; x < width; x+=10) {
    line(x, 0, x, height);
  }
  for (int y =0; y < height; y+=10) {
    line(0, y, width, y);
  }
}
 
void AjoutPomme()
{
  pomme=new Pomme(pas/2*(int)random(colonnes), pas/2*(int)random(lignes));
}
 
void mousePressed()
{
  mesag="";
  commencer=true;
}
 
// ==========================================================
 
class Pomme {
  int x; // position x
  int y; // et y
  //
  // le constructeur de la classe
  Pomme(int x_, int y_) 
  {
    x=x_;
    y=y_;
  }
 
  void draw() {
    // draw
    fill(0, 255, 0);
    noStroke();
 
    ellipse(x, y, 10, 10);
  }
 
  Boolean touche(int a, int b) {
    // return ((a==x)&&(b==y));
    return dist(a, b, x, y)<20;
  }
} //class
// ==========================================================
class TeteOuCorps
{
  int x;
  int y;
 
  //le constructeur de la class TeteOuCorps
  TeteOuCorps(int x_, int y_)
  {
    x=x_;
    y=y_;
  } // constr
 
  void draw () {
    // draw
    fill(255, 0, 0);
    noStroke();
    ellipseMode(CENTER);
    ellipse(x, y, 20, 20);
  }
 
  Boolean touche(int a, int b) {
    return false;
    // return (((a==x)&&(b==(y+5)))||(a==x&&(b==(y-5)))||((a==(x+5)&&(b==y)))||((a==(x-5)&&(b==y))));
  }
} // class
// =========================================================

Votre travail va consister à modifier ce code. Il vous faudra réaliser un compte-rendu au fur et à mesure de l'avancement de votre travail et l'envoyer à mon adresse mail "serge.moutou at univ-reims".

Travail 1

Dessiner (sur une feuille de papier) en utilisant la technique présentée dans les documents fournis en cours les classes utilisées dans ce code en précisant leurs attributs et méthodes.

Si vous avez perdu vos documents, nous les avons ajouté en cours 6 du document Processing de mon site perso.

Remarque

Il est très possible que ce code :

ArrayList<TeteOuCorps> serpent;

vous pose des problèmes de compréhension. L'interprétation de cette ligne est :

  • serpent est une variable de type ArrayList
  • une ArrayList est quelque chose qui ressemble à un tableau mais sa taille n'est pas forcément déclarée au départ
    • par contre l'ajout d'une case au tableau se fait par serpent.add();
  • <TeteOuCorps> veut dire deux choses :
    • une classe nommée "TeteOuCorps" apparaîtra plus loin dans votre code
    • chacune des cases du tableau contient un objet qui est du type de cette classe "TeteOuCorps".
  • L’accès à une case du tableau se fait par "serpent.get(i)" où i est l'index de la case.

Travail 2

On vous demande d'organiser le code de manière un peu plus traditionnelle avec le code principal d'un côté dans lequel on ajoutera deux onglets (un par classe).

Nous vous rappelons que le nom des onglets doit forcément être celui de la classe.

Il n'y a aucune modification de code à ce stade, seulement quelques couper/coller pour remplir les onglets !!!

Travail 3

Vous devez à tout prix personnaliser ce jeu d'une manière ou d'une autre :

  • diminuer la taille du jeu (ou pas)
  • éventuellement afficher le score en dehors de l'aire de jeu pour une plus grande lisibilité
  • afficher votre nom en dehors de l'aire de jeu.
  • changer les couleurs

La personnalisation sera pour moi la seule manière de distinguer le travail des uns et des autres : éviter trop d'échanges de code entre vous. Il n'est pas interdit d'échanger entre vous des idées mais recopier le code d'un autre NON.

A ce stade aucun des projets ne doit se ressembler :

* ni par les couleurs

* ni par la taille de jeu

* ni par vos noms qui doivent apparaître quelque part

* ni par l'affichage du score

Travail 4

Maintenant vous allez améliorer le code pour résoudre un certain nombre de problèmes :

  • le serpent mange la pomme alors qu'il est encore un peu loin
  • l'ajout d'une ellipse quand le serpent mange la pomme doit être amélioré elle est mal positionnée parfois même si cela ne dure pas très longtemps
  • ajout de la possibilité de perdre et donc de remettre le score à 0 si
    • le serpent sort de l'aire de jeu
    • le serpent se traverse lui-même
  • apparition de temps en temps de "pomme bonus" qui sont d'une autre couleur et comptent plus de points

Je ne veux pas retrouver des choses trop avancées venant du forum originel Réalisation d'un jeu snake en 3D comme des menus par exemple !!!! Restez modeste, vous êtes sensés ne passer que 12 heures sur ce projet.

TD : Visualisation des données d'un accéléromètre

Notre but est d'utiliser la programmation objet pour réaliser une visualisation 3D des données venant d'un Gyroscope/Accéléromètre/Magnétomètre.

Code de départ

Voici un code complet capable de visualiser les données provenant d'un Arduino couplé avec un MPU9250.

La classe Solid

On rappelle que traditionnellement ce code est mis dans un onglet spécifique qui porte le nom de la classe.

class Solid {
  // attributs de position
  int posX, posY;
  // angles d'Euler
  float angleX, angleY, angleZ;
  // couleurs
  int red, green, blue;
  // constructeurs
  Solid() {
    angleX=angleY=angleZ=0;
    posX=width/2; 
    posY=height/2;
  }
  // angles en degrés
  Solid(int posx,int posy,float anglex,float angley,float anglez){
    posX=posx;
    posY=posy;
    //posZ=-50;
    angleX=anglex;
    angleY=angley;
    angleZ=anglez;
  }
  // methodes
  void setColor(int rouge,int vert,int bleu){
    red=rouge;
    green=vert;
    bleu=blue;
  }
  void upDateAngles(float anglex,float angley,float anglez) {
    angleX = anglex; 
    angleY = angley;
    angleZ = anglez;
  }
  void draw_Solid() {
    //Show angles data
    pushMatrix(); 
    
    translate(posX, posY, -50); 
    // dans le repère de l'écran pour Euler123 de l'accéléromètre
    rotateX(radians(-angleX-90));
    rotateZ(radians(-angleZ));
    rotateY(radians(-angleY));
    
    scale(90);
    beginShape(QUADS);

    fill(red, green, blue);
    vertex(-1, 1.5, 0.25);
    vertex( 1, 1.5, 0.25);
    vertex( 1, -1.5, 0.25);
    vertex(-1, -1.5, 0.25);

    vertex( 1, 1.5, 0.25);
    vertex( 1, 1.5, -0.25);
    vertex( 1, -1.5, -0.25);
    vertex( 1, -1.5, 0.25);

    vertex( 1, 1.5, -0.25);
    vertex(-1, 1.5, -0.25);
    vertex(-1, -1.5, -0.25);
    vertex( 1, -1.5, -0.25);

    vertex(-1, 1.5, -0.25);
    vertex(-1, 1.5, 0.25);
    vertex(-1, -1.5, 0.25);
    vertex(-1, -1.5, -0.25);

    vertex(-1, 1.5, -0.25);
    vertex( 1, 1.5, -0.25);
    vertex( 1, 1.5, 0.25);
    vertex(-1, 1.5, 0.25);

    vertex(-1, -1.5, -0.25);
    vertex( 1, -1.5, -0.25);
    vertex( 1, -1.5, 0.25);
    vertex(-1, -1.5, 0.25);

    endShape();
    popMatrix();
  }
}

Le code principal

Le code donné ci-dessous gère une liaison série pour recevoir des données d'un Arduino couplé à un MPU9250. Le code Arduino sera donné un peu plus loin.

Cette liaison série est capable d'analyser des donnée séparées par des marqueurs. Nous la laisson telle quelle pour le moment.

import processing.serial.*;

Serial  myPort;
short   portIndex = 1;
int     lf = 10;       //ASCII linefeed
String  inString;      //String for testing serial communication
int     calibrating;
 
float   dt;
float   x_gyr;  //Gyroscope data
float   y_gyr;
float   z_gyr;
float   x_acc;  //Accelerometer data
float   y_acc;
float   z_acc;
float   x_fil;  //Filtered data
float   y_fil;
float   z_fil;

Solid solide;
 
void setup()  { 
//  size(640, 360, P3D); 
  size(1400, 800, P3D);
  noStroke();
  colorMode(RGB, 256); 
  solide = new Solid(width/2,height/2,0,0,0);
//  println("in setup");
//  String portName = Serial.list()[portIndex];
  String portName = "/dev/ttyACM0";
//  println(Serial.list());
//  println(" Connecting to -> " + Serial.list()[portIndex]);
  myPort = new Serial(this, portName, 38400);
  myPort.clear();
  myPort.bufferUntil(lf);
} 

void draw()  { 
  
  background(0);
  lights();

  solide.setColor(56, 140, 206);
  solide.draw_Solid();
} 

void serialEvent(Serial p) {

  inString = (myPort.readString());
  
  try {
    // Parse the data
    String[] dataStrings = split(inString, '#');
    for (int i = 0; i < dataStrings.length; i++) {
      String type = dataStrings[i].substring(0, 4);
      String dataval = dataStrings[i].substring(4);
      if (type.equals("DEL:")) {
        dt = float(dataval);
      } else if (type.equals("ACC:")) {
        String data[] = split(dataval, ',');
        x_acc = float(data[0]);
        y_acc = float(data[1]);
        z_acc = float(data[2]);
      } else if (type.equals("GYR:")) {
        String data[] = split(dataval, ',');
        x_gyr = float(data[0]);
        y_gyr = float(data[1]);
        z_gyr = float(data[2]);
      } else if (type.equals("FIL:")) {
        String data[] = split(dataval, ',');
        x_fil = float(data[0]);
        y_fil = float(data[1]);
        z_fil = float(data[2]);
      }
    }
    solide.upDateAngles(x_acc,y_acc,z_acc); 
  } catch (Exception e) {
      println("Caught Exception");
  }
}

Le code Arduino

Le code Arduino est disponible dans la Wikiversité ICI : Application 6 : fusion de données par filtre complémentaire. On ne vous demande pas de comprendre ce code dans le détail.

Exercice 4

Faire fonctionner correctement cet ensemble de code.

Le moins que l'on puisse dire c'est que la visualisation fonctionne correctement mais avec un accéléromètre un peu poussif. Ce code ne pourra donc n'être utilisé pour un jeu vidéo que dans certains cas. Nous allons donc essayer de l'améliorer.

Exercice 5

Nous allons utiliser le processeur DMP (Data Movement Processor) du MPU9250. Ceci fonctionne très bien si on installe la librairie qui va bien comme expliqué ICI.

  1. Installer la bibliothèque de Jeff Rowberg en faisant fonctionner l'exemple de la librairie
  2. Modifier le code de l'exemple pour être compatible avec notre logiciel de visualisation
  3. Faire fonctionner correctement l'ensemble

Exercice 6

Vous allez modifier le code de visualisation pour réaliser un jeu vidéo de votre choix.

Il ne s'agit pas pour vous de trouver un jeu tout fait mais de l'adapter à votre problématique. S'il n'est pas en POO (Programmation Orientée Objet) vous le modifierez pour qu'il contienne au moins une classe.

Si vous manquez d'idée, cela peut être :

  • un Pong avec le mouvement de la raquette réalisé par l'accéléromètre. Vous pouvez vous aider de Créer un Pong avec Processing
  • un plateau horizontal avec des trous et des billes à mettre dans les trous. La modification de la position de l'accéléromètre permettra de faire bouger correctement les billes avec comme objectif de les mettre chacune dans un trou.
  • voyage galactique ... avec des obstacles à éviter...
  • Réalisation d'un jeu snake en 3D
  • Ensemble de jeux ici

TP programmation Objet

L'objectif de ce TP sur deux séances de trois heures est de réaliser un clavier de Piano capable de d'envoyer les changements d'états de ses touches par une liaison série. Les changements d'états des touches seront réalisés par la souris. Vous devrez donc être capable de gérer les deux événement souris "mousePressed()" et "mouseReleased()". Nous aurons en bout de la liaison série, un Arduino Leonardo qui lui-même sera relié à un amplificateur sonore

Code de départ

Pour vous aider, quelques ressources vous sont fournies.

Un clavier piano fonctionnel sur une octave

Un code de départ vous est fourni pour vous éviter de chercher les tailles et la façon de représenter votre clavier. Ce code n'envoie absolument rien sur la liaison série mais affiche dans le moniteur de Processing la touche appuyée ou relachée.

int spazio = 50;
int value;

void setup(){
size(400,400);
  //minim = new Minim(this); 
}

void draw(){
background(0);
stroke(0);
for (int x = 0; x <= width; x = x + spazio ){
  fill(255); // white
  rect(x - 50,0,x,height);
}
fill(0); // black
rect(35, 0, 30, 250);
rect(85, 0, 30, 250);
rect(185, 0, 30, 250);
rect(235, 0, 30, 250);
rect(285, 0, 30, 250);
rect(385, 0, 30, 250);
}

void mouseReleased() {
    value = 0;
    println(value);
}

void mousePressed(){
  value=0;
 //do 
if (mouseX < 35 && mouseY < 250 || mouseX < 50 && mouseY > 250){
  //song = minim.loadFile("Do1.mp3");
  //song.play();
  value = 1;
  //println(value);
} 
//re
if (mouseX > 65 && mouseX < 85 && mouseY < 250){
  //song = minim.loadFile("Re1.mp3");
  //song.play();
  value = 3;
  //println(value);
} 
if (mouseY > 250 && mouseX > 50 && mouseX < 100 || value == 3){
  //song = minim.loadFile("Re1.mp3");
  //song.play();
  value = 3;
  //println(value);
} 
//mi
if (mouseX > 115 && mouseX < 150 && mouseY < 250){
  //song = minim.loadFile("Mi1.mp3");
  //song.play();
  value = 5;
  //println(value);
} 
if ( mouseY > 250 && mouseX > 100 && mouseX < 150 || value == 5){
  //song = minim.loadFile("Mi1.mp3");
  //song.play();
  value = 5;
  //println(value);
} 
//fa
if (mouseX > 150 && mouseX < 185 && mouseY < 250){
  //song = minim.loadFile("Fa1.mp3");
  //song.play();
  value = 6;
  //println(value);
} 
if (mouseY > 250 && mouseX > 150 && mouseX < 200 || value == 6){
  //song = minim.loadFile("Fa1.mp3");
  //song.play();
  value = 6;
  //println(value);
} 
//sol
if (mouseX > 215 && mouseX < 235 && mouseY < 250){
  //song = minim.loadFile("Sol1.mp3");
  //song.play();
  value = 8;
  //println(value);
} 
if (mouseY > 250 && mouseX > 200 && mouseX < 250 || value == 8){
  //song = minim.loadFile("Sol1.mp3");
  //song.play();
  value = 8;
  //println(value);
} 

//la
if (mouseX > 265 && mouseX < 285 && mouseY < 250){
  //song = minim.loadFile("La1.mp3");
  //song.play();
  value = 10;
  //println(value);
} 
if (mouseY > 250 && mouseX > 250 && mouseX < 300 || value == 10){
  //song = minim.loadFile("La1.mp3");
  //song.play();
  value = 10;
  //println(value);
} else


//si 
if (mouseX > 315 && mouseX < 350 && mouseY < 250){
  //song = minim.loadFile("Si1.mp3");
  //song.play();
  value = 12;
  //println(value);
} 
if (mouseY > 250 && mouseX > 300 && mouseX < 350 || value == 12){
  //song = minim.loadFile("Si1.mp3");
  //song.play();
  value = 12;
  //println(value);
} 

//do2
if (mouseX > 350 && mouseX < 385 && mouseY < 250){
//  song = minim.loadFile("Do2.mp3");
  //song.play();
  value = 13;
  //println(value);
} 
if (mouseY > 250 && mouseX > 350 && mouseX < 400 || value == 13){
  //song = minim.loadFile("Do2.mp3");
  //song.play();
  value = 13;
  //println(value);
} 

// I bemolli
//do#
if ( mouseX > 35 && mouseX < 65 && mouseY < 250){
  //song = minim.loadFile("Do#1.mp3");
  //song.play();
  value = 2;
} 
//re#

if ( mouseX > 85 && mouseX < 115 && mouseY < 250){
  //song = minim.loadFile("Re#1.mp3");
  //song.play();
  value = 4;
} 
// fa#
if ( mouseX > 185 && mouseX < 215 && mouseY < 250){
  //song = minim.loadFile("Fa#1.mp3");
  //song.play();
  value = 7;
} 
// sol#
if ( mouseX > 235 && mouseX < 265 && mouseY < 250){
  //song = minim.loadFile("Sol#1.mp3");
  //song.play();
  value = 9;
} 
//la#
if ( mouseX > 285 && mouseX < 315 && mouseY < 250){
  //song = minim.loadFile("La#1.mp3");
  //song.play();
  value = 11;
}
  println(value);
}

Ce code est un bon point de départ mais il comporte plusieurs défauts :

  1. si le graphisme n'est pas trop mal, son principe est de dessiner d'abord les touches blanches puis, par dessus, les touches noires.
  2. les touches ne sont pas des objets et donc il est difficile de gérer une couleur appuyée et une couleur relachée

La classe Bouton

La question 8 de l'exercice 2 introduit la notion de classe pour un bouton et vous seriez bien inspiré de relire le code correspondant.

Travail à réaliser

  1. Pour éviter de travailler avec de l'héritage, vous allez définir 4 types de touches : 3 blanches et 1 noire. Par défaut ces touches seront considérées comme dessinées en coordonnée (0,0) en haut à gauche. Le constructeur devra contenir la définition du type de touche qui déterminera complètement le graphisme de la touche. Pévoir aussi une couleur pour touche non appuyée (blanc ou noir) et une couleur pour touche appuyée.
  2. Décomposer chacune des touches blanches en deux rectangles dont on déterminera les coordonnées à partir de l'exemple donné. Évidemment, au moment du dessin il ne faudra pas dessiner de bords aux rectangles sauf sur le côté gauche et le côté droit (line et le stroke associé).
  3. Traiter le cas des touches noires
  4. Décider de la taille et de la forme la zone dans laquelle il faudra appuyer pour considérer un appui de touche. La mise en place de cette zone sera faite par le constructeur en fonction du type de touche.
  5. A jouter une possibilité de transfert vers la droite pour chacun des types de touche
  6. Gérer la mise de l'ensemble des touches dans un tableau (Les listes (ou tableaux))
  7. Tester avec des print et/ou des println
  8. Gérer la liaison série (Serial) en lieu et place de l'affichage dans la console processing.

Correction partielle sans liaison série

class ClassPianoKey
{
  int _note;
  int _type; // type d la touche
  int couleurOff, couleurOn; // noir, blanc ou  gris
  int offsetX;
  int clickAreaPosX,clickAreaPosY,clickAreaDX,clickAreaDY;
  
 ClassPianoKey(int deltaX,int type,int note) {
    _note = note;
    _type = type;
    offsetX = deltaX;
    if (type < 4) { // touche blanche
      couleurOff = 255;
      couleurOn = 200;
      clickAreaPosX=offsetX;
      clickAreaPosY = 250; 
      clickAreaDX= 50;
      clickAreaDY = 150;
    } else {
      couleurOff = 0;
      couleurOn = 50;
      clickAreaPosX=offsetX;
      clickAreaPosY = 0; 
      clickAreaDX= 30;
      clickAreaDY = 250;
    }
    
  }

  void showOff() {
    if (_type == 1) {
      fill(couleurOff);
      stroke(couleurOff);
      rect(0+offsetX,0,35,250);
      rect(0+offsetX,250,50,150);
      // black
      stroke(0);
      line(0+offsetX,0,0+offsetX,400);
      line(0+offsetX+50,0+250,0+offsetX+50,400);
    }
    if (_type == 2) {
      fill(couleurOff);
      stroke(couleurOff);
      rect(0+offsetX+15,0,35,250);
      rect(0+offsetX,250,50,150);
      stroke(0);
      line(0+offsetX,0+250,0+offsetX,400);
      line(0+offsetX+50,0,0+offsetX+50,400);
    }
    if (_type == 3) {
      fill(couleurOff);
      stroke(couleurOff);
      rect(0+offsetX+15,0,20,250);
      rect(0+offsetX,250,50,150);
      stroke(0);
      line(0+offsetX,0+250,0+offsetX,400);
      line(0+offsetX+50,0+250,0+offsetX+50,400);
    }
    if (_type == 4) {
      fill(couleurOff);
      stroke(couleurOff);
      rect(0+offsetX,0,30,250);      
    }
    
  }

  void showOn() {
    if (_type == 1) {
      fill(couleurOn);
      stroke(couleurOn);
      rect(0+offsetX,0,35,250);
      rect(0+offsetX,250,50,150);
      // black
      stroke(0);
      line(0+offsetX,0,0+offsetX,400);
      line(0+offsetX+50,0+250,0+offsetX+50,400);
    }
    if (_type == 2) {
      fill(couleurOn);
      stroke(couleurOn);
      rect(0+offsetX+15,0,35,250);
      rect(0+offsetX,250,50,150);
      stroke(0);
      line(0+offsetX,0+250,0+offsetX,400);
      line(0+offsetX+50,0,0+offsetX+50,400);
    }
    if (_type == 3) {
      fill(couleurOn);
      stroke(couleurOn);
      rect(0+offsetX+15,0,20,250);
      rect(0+offsetX,250,50,150);
      stroke(0);
      line(0+offsetX,0+250,0+offsetX,400);
      line(0+offsetX+50,0+250,0+offsetX+50,400);
    }
    if (_type == 4) {
      fill(couleurOn);
      stroke(couleurOn);
      rect(0+offsetX,0,30,250);      
    }
    
  }
  
  Boolean mouseIn() {
    if ((mouseX>clickAreaPosX) && (mouseX < clickAreaPosX+clickAreaDX)
      && (mouseY>clickAreaPosY) && (mouseY < clickAreaPosY+clickAreaDY)) 
         return true;
    else return false;
  }
  
}

Et enfin le programme principal :

ClassPianoKey[] pianokeys = new ClassPianoKey[15];

void setup() {
  size(400,400);
  frameRate(1); 
  pianokeys[0]= new ClassPianoKey(0,1,1);  //do
  pianokeys[1]= new ClassPianoKey(35,4,2);  //do#
  pianokeys[2]= new ClassPianoKey(50,3,3);  //re
  pianokeys[3]= new ClassPianoKey(85,4,4);
  pianokeys[4]= new ClassPianoKey(100,2,5);
  pianokeys[5]= new ClassPianoKey(150,1,6);
  pianokeys[6]= new ClassPianoKey(185,4,7);
  pianokeys[7]= new ClassPianoKey(200,3,8);
  pianokeys[8]= new ClassPianoKey(235,4,9);
  pianokeys[9]= new ClassPianoKey(250,3,10);
  pianokeys[10]= new ClassPianoKey(235,4,11);
  pianokeys[11]= new ClassPianoKey(250,3,12);
  pianokeys[12]= new ClassPianoKey(285,4,13);
  pianokeys[13]= new ClassPianoKey(300,2,14);
  pianokeys[14]= new ClassPianoKey(350,1,15);
  for (int i=0;i<15;i++) {
    pianokeys[i].showOff();
  }
}

void draw() {
  
}

void mousePressed() {
  for (int i=0;i<15;i++) {
    if (pianokeys[i].mouseIn())
      pianokeys[i].showOn();
  }  
}

void mouseReleased() {
  for (int i=0;i<15;i++) {
    if (pianokeys[i].mouseIn())
      pianokeys[i].showOff();
  }  
}

Malheureusement l'ensemble est un peu lent pour être utilisé pour réaliser de la musique !!!

Voir aussi