IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

rocessing

Processing - langage de programmation et environnement de développement libre.


précédentsommairesuivant

V. Animer

V-A. La méthode draw

Jusqu'ici, nous avons créé ce que nous pouvons appeler des programmes linéaires : on démarre le programme, celui-ci exécute notre dessin, et tout s'arrête à la fin des instructions de notre sketch, en bas de la fenêtre d'édition.

Mais Processing n'est pas uniquement un environnement de dessin écrit, c'est également un environnement interactif. Et pour qu'il y ait interactivité, il nous faut du temps, en d'autres termes, un moyen de prolonger notre dessin pour que celui-ci puisse se modifier en suivant chronologiquement certaines étapes, selon différents facteurs et conditions. C'est le rôle de la boucle infinie, appelée en permanence par la machine pour réactualiser notre dessin. Dans Processing, cette boucle infinie s'écrit draw(). Elle est souvent accompagnée par la méthode setup() qui permettra de préparer la fenêtre de visualisation (l'espace de dessin), par exemple en lui donnant une taille au départ.

C'est l'absence de ces deux méthodes qui rend Processing inerte. Si votre code ne dispose pas de méthode draw(), l'exécution du programme s'arrêtera à la fin du code de votre programme.

V-A-1. draw()

Commencez un nouveau programme Processing vide, tapez les lignes suivantes dans la fenêtre d'écriture et appuyez sur le bouton run :

 
Sélectionnez
void draw() {
    background( random(255) );
}

Ça y est, vous avez activé l'animation dans Processing. Vous devriez maintenant voir une fenêtre qui clignote 30 fois par seconde avec une couleur grise aléatoire quelque part entre le noir et le blanc. C'est la méthode random(255) qui donne en retour une valeur aléatoire entre 0 et 255. C'est cette valeur qui est ensuite récupérée par les parenthèses de la méthode background() et appliquée sur le fond de la fenêtre de visualisation de l'espace de dessin. Comme le tout se passe de manière répétée, on a l'impression d'assister à une animation.

Image non disponible

Par défaut, les instructions qui se trouvent entre les deux accolades de la méthode draw() seront appelées 30 fois par seconde. 30 fois par seconde Processing ouvrira cette méthode et regardera ce qui est écrit dedans pour l'exécuter. Nous pouvons mettre autant d'instructions que nous voulons à l'intérieur de cette méthode. Ces instructions seront jouées par Processing de manière cyclique, tel un métronome.

Attention aux erreurs d'écriture, car dès que Processing en rencontrera une, votre programme s'arrêtera net - c'est la fin de votre animation. En revanche, si vous avez écrit une méthode draw() sans erreurs, elle sera appelée en boucle, 30 fois par seconde, jusqu'à ce que l'utilisateur arrête le programme, qu'une panne d'électricité arrive, ou que la fin du monde se produise. C'est donc grâce à cette méthode exécutant du code en répétition que nous allons pouvoir créer des animations.

V-A-2. Fréquence d'exécution

Il est possible de spécifier une valeur différente à notre métronome en utilisant la méthode frameRate().

Si vous modifiez l'exemple précédent comme suit, vous remarquerez que la vitesse de l'animation a été diminuée (divisée par 3).

 
Sélectionnez
void draw() {
   frameRate(10);
   background( random(255) );
}

V-A-3. Nombre de fois où draw() a été appelée

Processing peut aussi compter le nombre de fois que cette méthode draw() a été appelée, depuis le lancement du programme via la variable frameCount. 

Image non disponible

V-A-4. setup()

Souvent, voire la plupart du temps, il est nécessaire de placer certaines instructions au tout début du programme. C'est la nature et la portée de ces instructions sur le programme qui nous incitent à les placer à cet endroit. Par exemple, dans Processing, la taille de la fenêtre de visualisation du dessin ne peut être définie qu'une seule fois dans un sketch ; ce paramétrage de l'espace de dessin ne peut donc être placé à l'intérieur de la méthode draw() car cette méthode s'exécute plusieurs fois durant le déroulement du programme.

À moins de se satisfaire de la dimension par défaut de 100 × 100 pixels, définir la taille de la fenêtre de visualisation peut s'avérer très utile. C'est pour toutes ces raisons qu'une deuxième méthode complémentaire a été créée : la méthode setup().

 
Sélectionnez
void setup() {
 
}

Les subtilités des différents signes qui composent cette méthode sont pour l'instant sans importance. Sachez simplement qu'il faut écrire void setup() tout au début, suivi d'une ouverture d'accolades, saisir ensuite les instructions que vous voulez exécuter, et enfin terminer avec la fermeture des accolades.

 
Sélectionnez
void setup() {
  size(500, 500);
}
 
void draw() {
  background(random(255));
}

Lorsque ce sketch est lancé, Processing exécute tout d'abord les instructions qui se trouvent à l'intérieur de la méthode setup(). Ensuite, la méthode draw() commencera à être appelée de manière répétée, telle un métronome.

Ce principe de fonctionnement est illustré par le schéma suivant :

Image non disponible

V-A-5. Background()

Voici un programme qui remplit progressivement l'écran avec des ellipses.

Image non disponible
 
Sélectionnez
void setup() {
  size(200, 200);
  fill(0);
}
 
void draw() {
   ellipse(random(200), random(200), 20, 20);
}

Comme vous pouvez le constater dans les captures d'écran, cette animation finira par remplir notre espace de dessin complètement par du noir. C'est peut-être l'œuvre conceptuelle ultime de toute notre carrière artistique ; néanmoins il serait bien utile d'apprendre à animer une seule ellipse, la voir évoluer dans l'espace ou changer de forme, sans que ses positions antérieures soient affichées en même temps.

Dans ce cas, il suffit d'ajouter un nettoyage de fond de l'espace de dessin à l'aide de la méthode background(). Celle-ci prend une à trois valeurs de couleur, comme pour la méthode fill() ou stroke().

Voici une modification du programme qui permettra d'afficher maintenant une seule forme animée :

 
Sélectionnez
void setup() {
  size(200, 200);
  fill(0);
}
 
void draw() {
  background(255);
  ellipse( 100, 100, random(100), random(100));
}

En réalité, ce que nous demandons au programme, c'est 30 fois par seconde d'effacer l'intégralité de notre dessin et de tracer une nouvelle forme par-dessus avec un nouveau fond blanc.

V-A-6. Ajouter un fondu

Il existe une astuce, souvent utilisée dans la communauté des utilisateurs de Processing, qui consiste à effacer le fond de l'image à l'aide d'un rectangle semi-transparent plutôt qu'avec l'instruction  background(). Ce procédé permet d'obtenir un effet d'effacement graduel, de fondu.

Image non disponible
 
Sélectionnez
void setup() {
  size(200,200);
  background(0);
  noStroke();
}
 
void draw() {
  fill(0, 0, 0, 20);
  rect(0, 0, 200, 200);
  fill(255);
  ellipse(100 + random(-20,20), 100 + random(-20,20), random(50), random(50));
}

V-B. La ligne de temps

Pour créer une animation, il faut qu'il y ait du mouvement. Le mouvement implique un changement du dessin dans le temps, par exemple une modification de la position ou de la couleur d'un de ces éléments graphiques. Les informations liées à ces changements peuvent être stockées dans des variables.

Pour créer des animations, il faut savoir à quel moment nous nous trouvons par rapport à une ligne de temps. Pour ce faire, nous pouvons soit utiliser l'heure qu'il est, soit compter (par exemple de 1 à 10).

V-B-1. Quelle heure est-il?

Nous allons créer une horloge en appelant les méthodes hour(), minute() et second() de Processing. Nous allons utiliser le résultat de l'appel de ces méthodes pour faire varier la position sur l'axe horizontal de trois minces rectangles.

Image non disponible

Le code pour réaliser cette horloge est très simple :

 
Sélectionnez
void setup() {
  size(60, 60);
  noStroke();
}
 
void draw() {
  background(0);
 
  // Les heures vont de 0 à 23, nous les convertissons à une
  // échelle de 0 à 60
 
  rect((hour() / 24.0) * 60, 0, 1, 20);
  rect(minute(), 20, 1, 20);
  rect(second(), 40, 1, 20);
}

Les images sont dessinées les unes par-dessus les autres. L'appel à l'instruction background(0) remplit l'espace de dessin avec la couleur définie en paramètre (le nombre 0 correspondant à du noir) ce qui revient à chaque fois à effacer l'image précédente.

V-B-2. Mesurer le temps qui passe

On peut obtenir le temps qui s'est écoulé depuis le début de l'exécution d'un sketch en utilisant la méthode millis(). Celle-ci retourne un nombre en millisecondes. Cette valeur peut être exploitée pour créer des animations. Il y a mille millisecondes dans une seconde : une précision suffisante pour animer des formes ou des sons.

Par ailleurs, vous pouvez également avoir besoin de créer des animations cycliques dont la fréquence (le nombre d'animations par cycle) s'adapte en fonction d'autres éléments, par exemple la taille de la fenêtre de visualisation de l'espace de dessin. Pour déterminer à partir d'une valeur donnée ce nombre d'animations par cycle, la solution la plus simple consiste à utiliser l'opérateur modulo %. En mathématique, le modulo permet d'obtenir le reste d'une division par un nombre. Par exemple, l'expression println(109 % 10); affichera 9, car 109 divisé par 10, donne 10 comme quotient et 9 comme reste. De manière plus générale, si on prend deux nombres  x et y, le reste de la division de x par y est strictement inférieur à y. L'opérateur modulo nous permet donc de compter sans jamais dépasser un certain nombre (la base du modulo).

Dans le prochain exemple, nous allons utiliser la méthode millis() et l'opérateur % pour dessiner un cercle qui parcourt très rapidement le sketch selon des trajectoires diagonales. Ce cercle laisse derrière lui une traînée qui s'efface graduellement.

Image non disponible

Le code de cette animation est le suivant :

 
Sélectionnez
void setup() {
  size(320, 240);
  noStroke();
  frameRate(60);
  smooth();
}
 
void draw() {
  fill(0, 0, 0, 10);
  rect(0, 0, width, height);
  fill(255);
  ellipse(millis() % width, millis() % height, 20, 20);
}

L'effacement du dessin précédent s'effectue à l'aide d'un rectangle semi-transparent. Ce procédé permet d'obtenir un effet de flou de mouvement contrairement à l'instruction background(). La présence de l'instruction smooth() au début du programme s'explique par notre amour des courbes lisses.

Un conseil : basez vos animations sur la mesure du temps plutôt que sur le décompte du nombre d'images dessinées. La vitesse de rendu peut très sensiblement se dégrader lorsque les capacités de l'ordinateur sont occupées à gérer un compteur : les animations deviennent alors moins fluides, plus saccadées. Ces limitations de performance n'existent pas lorsque l'on utilise les instructions associées à l'horloge interne de l'ordinateur pour mesurer le temps qui passe.

Pour satisfaire votre curiosité et malgré notre mise en garde sur l'utilisation de la méthode du décomptage du nombre d'images dessinées, nous allons néanmoins voir comment créer des animations à l'aide de compteurs, le principe de mis en œuvre étant très simple.

V-B-3. Animer à l'aide d'un compteur

Nous allons maintenant voir comment créer une animation en comptant, même si, rappelons-le, nous vous déconseillons cette méthode pour des questions de performance. Dans notre exemple, l'animation représentant une simple ligne qui tourne sans cesse.

Image non disponible

Le code est relativement simple, le compteur d'images constituant l'essentiel du programme. À chaque degré de rotation de la ligne correspond le numéro d'une image dessinée.

 
Sélectionnez
int compteur;
 
void setup() {
  size(320, 240);
  frameRate(60);
  fill(0, 0, 0, 10);
  stroke(255);
  smooth();
 
  compteur = 0;
}
 
void draw() {
  compteur = compteur + 1;
 
  rect(0, 0, width, height);
 
  translate(width / 2, height / 2);
  rotate(radians(compteur));
  line(-height, -height, height, height);
}

On déclare un compteur en en-tête de l'application.

 
Sélectionnez
int compteur;

Dans le setup() on initialise notre compteur.

 
Sélectionnez
void setup() {
  size(320, 240);
  frameRate(60);
  fill(0, 0, 0, 10);
  stroke(255);
  smooth();
 
  compteur = 0;
}

À chaque appel de méthode draw() on incrémente notre compte de 1.

 
Sélectionnez
compteur = compteur + 1;

On spécifie ensuite le repère de l'espace de dessin pour dessiner au centre et on génère une rotation qui dépend de compteur.

 
Sélectionnez
  translate(width / 2, height / 2);
  rotate(radians(compteur))

Une traine est visible dans l'animation. La superposition des lignes en s'effaçant graduellement donne un effet moiré.

V-C. L'animation d'un objet

En combinant animation et objet, il devient possible de concevoir des applications plus ambitieuses. Animer un objet revient à ajouter à son modèle des actions de type : se déplacer, rebondir, tester les collisions, etc. Dans ce chapitre, nous allons apprendre comment animer une balle et la faire rebondir sur les quatre bords de l'écran.

Le résultat final se présentera comme ci-dessous. Afin de mieux visualiser la trajectoire de la balle, nous avons intégré dans l'animation un effet de traine.

Image non disponible

V-C-1. Code de base

Comme point de départ, nous allons reprendre le programme de la balle du chapitre « Les objets » qui nous permettait d'afficher une balle à l'écran et lui ajouter progressivement des morceaux de code. Les nouvelles parties du sketch sont signalées en gras avec la mention //AJOUT ou //DEBUT AJOUT et //FIN AJOUT. Pour vous familiariser avec le fonctionnement de ce nouveau programme, vous pouvez également copier tout le bloc du code initial dans votre fenêtre d'édition de Processing et progressivement lui ajouter la classe ou la méthode concernée.

Image non disponible
 
Sélectionnez
//Déclaration et création d'une instance de l'objet Balle
Balle maBalle = new Balle(100, 100, color(255));
 
void setup() {
  smooth(); //Lissage des dessins
  size(400, 200); //Taille de la fenêtre
}
 
void draw() {
  background(0); //On dessine un fond noir
  noStroke(); //On supprime le contour
 
  maBalle.display(); //Affichage de la balle
}
 
class Balle {
  //Déclaration des paramètres de base de la balle
  float x;
  float y;
  color couleur;
 
  //Constructeur de la balle
  Balle (float nouvX, float nouvY, color nouvCouleur) {
    x          = nouvX;
    y          = nouvY;
    couleur    = nouvCouleur;
  }
 
  //Dessin de la balle
  void display() {
    fill(couleur);
    ellipse(x, y, 40, 40);
  }
}

V-C-2. Le déplacement

La balle doit pouvoir se déplacer sur les axes x et y. Nous allons créer deux variables dans le modèle de l'objet qui caractériseront sa vitesse sur les axes x et y. Ensuite nous allons ajouter une nouvelle méthode bouge() dans le modèle de l'objet qui sera appelée depuis la méthode draw() du programme. Cette méthode va, à chaque fois que l'objet est affiché, modifier la position de la balle par rapport à sa vitesse. Il faudra aussi initialiser les variables décrivant la vitesse dans le constructeur. Pour commencer, nous allons leur donner une valeur fixe.

 
Sélectionnez
class Balle {
  //Déclaration des caractéristiques de base de la balle
  float x;
  float y;
  float vitesseX; //AJOUT
  float vitesseY; //AJOUT
  color couleur;
 
  //Constructeur de la balle
  Balle (float nouvX, float nouvY, color nouvCouleur) {
    x      = nouvX;
    y      = nouvY;
    couleur    = nouvCouleur;
 
    vitesseX = 2; //AJOUT
    vitessesY = 2; //AJOUT
  }
 
  //Dessin de la balle
  void display() {
    fill(couleur);
    ellipse(x, y, 40, 40);
  }
 
  //DEBUT AJOUT
  void bouge() {
   x = x + vitesseX;
   y = y + vitesseY;
 
  //FIN AJOUT
}

Il faudra ensuite appeler la méthode bouge() depuis la méthode draw(). Par ailleurs, nous ajoutons un appel à l'instruction background() pour effacer l'écran à chaque nouvelle image.

 
Sélectionnez
void draw() {
  background(0); //On dessine un fond noir
  noStroke(); //On supprime le contour
 
  //Déplacement et affichage de la balle
  maBalle.bouge(); //AJOUT
  maBalle.display();
}

V-C-3. Les collisions

Pour le moment, dès que la balle touche le bord de l'écran, elle continue son chemin. Selon l'exemple de la balle qui rebondit sur les coins de l'écran du chapitre « La ligne de temps », nous allons ajouter une méthode testCollision qui inversera la vitesse de la balle lorsqu'elle touche les bords de l'écran.

 
Sélectionnez
class Balle {
  //Déclaration des caractéristiques de base de la balle
  float x;
  float y;
  float vitesseX;
  float vitesseY;
  color couleur;
 
  //Constructeur de la balle
  Balle (float nouvX, float nouvY, color nouvCouleur) {
    x      = nouvX;
    y      = nouvY;
    couleur    = nouvCouleur;
 
    vitesseX = 2;
    vitesseY = 2;
  }
 
  //Dessin de la balle
  void display() {
    fill(couleur);
    ellipse(x, y, 40, 40);
  }
 
  void move() {
   x = x + vitesseX;
   y = y + vitesseY;
  }
 
  //DEBUT AJOUT
  void testCollision() {
    //Si la balle touche un mur, elle rebondit
    if (x > width-20 || x < 20) {
      vitesseX = vitesseX *-1;
    }
    if (y > height-20 || y < 20) {
      vitesseY = vitesseY * -1;
    }
  }
  //FIN AJOUT
}

Il faut ensuite appeler la méthode testCollision() depuis la méthode draw().

 
Sélectionnez
//ON REMPLACE L'INSTRUCTION BACKGROUND() PAR CES DEUX LIGNES
  fill(0, 0, 0, 1); // Couleur avec transparence.
  rect(0, 0, width, height);   noStroke();
 
  //Déplacement et affichage de la balle
  maBalle.bouge();
  maBalle.testCollision();//AJOUT
  maBalle.display();
}

V-C-4. Code Final

Voici le code final, une fois toutes ces modifications effectuées.

 
Sélectionnez
//Déclaration et création d'une instance de l'objet Balle
Balle maBalle = new Balle(100, 100, color(255));
 
void setup() {
  smooth(); //Lissage des dessins
  size(400, 200); //Taille de la fenêtre
}
 
void draw() {
  fill(0, 0, 0, 1);
  rect(0, 0, width, height);
 
  noStroke();
 
  //Déplacement et affichage de la balle
  maBalle.bouge();
  maBalle.testCollision();
  maBalle.display();
}
 
class Balle {
  //Déclaration des paramètres de base de la balle
  float x;
  float y;
  float vitesseX; //AJOUT
  float vitesseY; //AJOUT
  color couleur;
 
  //Constructeur de la balle
  Balle (float nouvX, float nouvY, color nouvCouleur) {
    x          = nouvX;
    y          = nouvY;
    couleur    = nouvCouleur;
 
    vitesseX = 2; //AJOUT
    vitesseY = 2; //AJOUT
 
  }
 
  //Dessin de la balle
  void display() {
    fill(couleur);
    ellipse(x, y, 40, 40);
  }
 
  void bouge() {
   x = x + vitesseX;
   y = y + vitesseY;
  }
 
  void testCollision() {
    //Si la balle touche un mur, elle rebondit
    if (x > width-20 || x < 20) {
      vitesseX = vitesseX * -1;
    }
    if (y > height-20 || y < 20) {
      vitesseY = vitesseY * -1;
    }
  }
}

N'hésitez pas à modifier certains paramètres du programme pour vous approprier davantage son fonctionnement.

V-D. L'animation de plusieurs objets

Dès lors qu'un objet est créé dans un programme, il est possible de le multiplier facilement et rapidement. Deux solutions sont possibles :

  • Dans le chapitre d'introduction aux objets, nous avons vu qu'on pouvait obtenir deux balles à l'écran en déclarant une seconde copie de la balle et en l'affichant à son tour dans la méthode draw(). Ce procédé devient toutefois lourd lorsque le nombre d'objets à reproduire est supérieur à deux.
  • Lorsqu'il y a plus de deux objets à animer, il est préférable d'utiliser des listes. Petit rappel du chapitre consacré à cette notion : les listes permettent de gérer facilement un ensemble d'éléments semblables que ce soit des chiffres, des images et même des objets.

Dans ce chapitre, nous allons poursuivre l'exemple de la balle rebondissante. Nous allons ajouter plusieurs balles en utilisant des listes puis ajouter une méthode permettant de gérer les collisions entre les balles.

Image non disponible

V-D-1. Code de base

Comme point de départ, nous allons reprendre le code de la balle du chapitre « Animer un objet » qui nous permettait d'afficher une balle rebondissante à l'écran. Tout au long du chapitre, nous allons ajouter des portions de code à l'exemple de base. Les nouvelles parties sont signalées en gras avec la mention //AJOUT ou //DEBUT AJOUT et //FIN AJOUT. Pour vous familiariser avec le fonctionnement de ce nouveau programme, vous pouvez également copier tout le bloc du code initial dans votre fenêtre d'édition de Processing et progressivement lui ajouter la classe ou la méthode concernée.

Image non disponible
 
Sélectionnez
//Déclaration et création d'une instance de l'objet Balle
Balle maBalle = new Balle(100, 100, color(255));
 
void setup() {
  smooth(); //Lissage des dessins
  size(400, 200); //Taille de la fenêtre
}
 
void draw() {
  fill(0, 0, 0, 1);
  rect(0, 0, width, height);
 
  noStroke();
 
  //Déplacement et affichage de la balle
  maBalle.bouge();
  maBalle.testCollision();
  maBalle.display();
}
 
class Balle {
  //Déclaration des paramètres de base de la balle
  float x;
  float y;
  float vitesseX; //AJOUT
  float vitesseY; //AJOUT
  color couleur;
 
  //Constructeur de la balle
  Balle (float nouvX, float nouvY, color nouvCouleur) {
    x          = nouvX;
    y          = nouvY;
    couleur    = nouvCouleur;
 
    vitesseX = 2; //AJOUT
    vitesseY = 2; //AJOUT
 
  }
 
  //Dessin de la balle
  void display() {
    fill(couleur);
    ellipse(x, y, 40, 40);
  }
 
  void bouge() {
   x = x + vitesseX;
   y = y + vitesseY;
  }
 
  void testCollision() {
    //Si la balle touche un mur, elle rebondit
    if (x > width-20 || x < 20) {
      vitesseX = vitesseX * -1;
    }
    if (y > height-20 || y < 20) {
      vitesseY = vitesseY * -1;
    }
  }
}

V-D-2. Lister les balles

Nous allons maintenant appliquer le concept de listes à notre balle rebondissante. Cela nous permettra d'avoir plusieurs balles à l'écran, sans dupliquer le code !

En premier lieu, nous allons déclarer une liste de balles et non plus une seule balle. Pour cela, nous allons utiliser une variable nbreBalle pour stoker le nombre de balles utilisées dans le programme.

Nous allons remplacer la déclaration en en-tête suivant

 
Sélectionnez
Balle maBalle = new Balle(100, 100, color(255));

par 

 
Sélectionnez
//Déclaration d'une variable contenant le nombre de balles
int nbreBalle = 3;
 
//Déclaration d'une liste d'instances de l'objet Ball
Balle[] balles = new Balle[nbreBalle];

Comme dans l'exemple des nombres entiers, nous venons uniquement de déclarer des copies du modèle de balle. Il faut maintenant les créer dans le setup(). Nous allons dessiner trois balles au centre de l'écran. Toutes les trois seront blanches.

 
Sélectionnez
void setup() {
  smooth(); //Lissage des dessins
  size(400, 200); //Taille de la fenêtre
 
  //DEBUT AJOUT
  //Cette boucle va créer trois balles
  //blanches au centre de l'écran
  for (int i = 0; i < nbreBalle; i++) {
    balles[i] = new Balle(width/2, height/2,  color(255));
  }
  //FIN AJOUT
}

Dans la méthode draw() , nous allons aussi créer une boucle qui va parcourir tous les éléments de la liste pour les déplacer, tester leurs collisions et les afficher. Nous allons remplacer

 
Sélectionnez
  //Déplacement et affichage de la balle
  maBalle.bouge();
  maBalle.testCollision();
  maBalle.display();

par

 
Sélectionnez
  //Cette boucle va déplacer et afficher les trois balles
  for (int i = 0; i < nbreBalle; i++) {
    balles[i].bouge();
    balles[i].testCollision();
    balles[i].display();
  }

Une dernière opération va consister à modifier le constructeur du modèle de la balle afin que chaque balle ait une vitesse et une direction spécifiques. Pour ce faire nous allons utiliser la fonction random() qui permet de générer des nombres aléatoires. Nous allons remplacer le constructeur ci-dessous :

 
Sélectionnez
  //Constructeur de la balle
  Balle (float nouvX, float nouvY, color nouvCouleur) {
    x      = nouvX;
    y      = nouvY;
    couleur    = nouvCouleur;
 
    vitesseX = 2;
    vitesseY = 2;
}

par celui-ci 

 
Sélectionnez
//Constructeur de la balle
  Balle (float nouvX, float nouvY, color nouvCouleur) {
    x      = nouvX;
    y      = nouvY;
    couleur    = nouvCouleur;
 
    vitesseX = 2 + random(-1,1);
    vitessesY = 2 + random(-1,1);
}

V-D-3. Code final

Voici le programme complet :

 
Sélectionnez
//Déclaration d'une variable contenant le nombre de balles
int nbreBalle = 3;
 
//Déclaration d'une liste d'instances de l'objet Balle
Balle[] balles = new Balle[nbreBalle];
 
void setup() {
  smooth(); //Lissage des dessins
  size(400, 200); //Taille de la fenêtre
 
  //Cette boucle va créer trois balles blanches
  //au centre de l'écran
  for (int i = 0; i < nbreBalle; i++) {
    balles[i] = new Balle(width/2, height/2,  color(255));
  }
}
 
void draw() {
  fill(0, 0, 0, 1); // Couleur avec transparence.
  rect(0, 0, width, height);
 
  noStroke();
 
  //Cette boucle va déplacer et afficher les trois balles
  for (int i = 0; i < nbreBalle; i++) {
    balles[i].bouge();
    balles[i].testCollision();
    balles[i].display();
  }
}
 
class Balle {
  //Déclaration des paramètres de base de la balle
  float x;
  float y;
  float vitesseX;
  float vitesseY;
  color couleur;
 
  //Constructeur de la balle
  Balle (float nouvX, float nouvY, color nouvCouleur) {
    x      = nouvX;
    y      = nouvY;
    couleur    = nouvCouleur;
 
    vitesseX = 2 + random(-1,1);
    vitesseY = 2 + random(-1,1);
  }
 
  //Dessin de la balle
  void display() {
    fill(couleur);
    ellipse(x, y, 40, 40);
  }
 
  //Déplacement de la balle
  void bouge() {
    x = x + vitesseX;
    y = y + vitesseY;
  }
 
  void testCollision() {
    //Si la balle touche un mur, elle rebondit
    if (x > width-20 || x < 20) {
       vitesseX = vitesseX * -1;
    }
    if (y > height-20 || y < 20) {
       vitesseY = vitesseY * -1;
    }
  }
}

N'hésitez pas à modifier certains paramètres du programme pour vous approprier davantage son fonctionnement.

V-E. La lecture du son

Processing est un environnement principalement dédié à la création visuelle. Il n'a pas été conçu au départ pour jouer du son, et encore moins pour générer directement des ondes audio, tel un logiciel comme Pure Data. Par la suite, de nombreuses bibliothèques externes ont été créées pour étendre les capacités sonores de Processing. Ainsi vous pouvez à n'importe quel moment intégrer ces bibliothèques externes dans vos programmes : des synthétiseurs, des sampleurs audio, des interfaces MIDI, des séquenceurs, etc. Sachez toutefois que ce n'est pas la mission de base de Processing davantage centrée sur la génération de formes visuelles.

Dans ce chapitre, nous allons nous familiariser avec un minim de fonctionnalités audio disponibles par défaut dans Processing.

V-E-1. Minim

Il a été décidé à un moment donné d'intégrer dans toute distribution Processing la bibliothèque audio Minim pour pouvoir par défaut jouer des fichiers audio ou capter le son entrant par le microphone. A priori cette bibliothèque est déjà installée sur votre machine. Pour vérifier sa présence et l'intégrer dans votre programme, il suffit d'aller dans le menu Sketch > Import Library… > Minim audio.

Image non disponible

Selon votre plateforme et votre version, à la suite de cette action, Processing ajoutera des lignes de code plus ou moins nombreuses en haut de votre programme. Par défaut, il faut au moins voir affichée l'instruction suivante :

Image non disponible
 
Sélectionnez
import ddf.minim.*;

C'est cette instruction (que vous pouvez saisir à la main si vous le voulez) qui importera l'ensemble des fonctionnalités de la bibliothèque Minim pour les rendre accessibles à notre programme. C'est à partir de cette instruction import ddf.minim.*; que notre sketch va pouvoir jouer du son.

V-E-2. Configurer Minim

Le lancement de notre sketch via le bouton Run ne suffit pas pour jouer un son. Au préalable, il faut prévoir d'activer les fonctionnalités de gestion audio de Minim (son moteur audio en jargon informatique). Pour jouer un son, Minim doit en effet auparavant demander un accès à la carte son de votre ordinateur. Ne vous inquiétez pas, Minim s'occupe de toute cette cuisine interne pour vous, il suffit de lui dire de s'activer pour cela. Le fait d'avoir activé Minim suppose de prévoir de le désactiver à la fin de notre programme.

Si vous ne comprenez pas tout ce que nous venons de dire, ce n'est pas trop grave. Sachez simplement qu'il faut saisir le code suivant au début de chaque programme utilisant Minim :

 
Sélectionnez
import ddf.minim.*;
 
Minim minim;
 
void setup() {
  minim = new Minim(this);
}

Et qu'il faut également écrire la fin du programme les instructions suivantes :

 
Sélectionnez
void stop() {
  minim.stop();
  super.stop();
}

Si par curiosité vous voulez savoir à quoi sert le mot « this » dans ce programme, sachez qu'il sert à indiquer à Processing que nos instructions s'appliquent à « ce » programme. Minim a besoin de connaître l'adresse de « ce » programme (le nôtre) pour le faire communiquer avec la carte son de notre ordinateur. Encore une fois, si vous ne comprenez pas ce que nous venons de dire, ce n'est pas grave. Sachez seulement que ces mots doivent être écrits au début et à la fin de chaque programme lorsque nous voulons produire du son.

V-E-3. Importer un son

Tout comme les images photographiques ou la typographie, nous allons importer un fichier son dans Processing pour pouvoir le jouer dans notre programme. Pour bien réussir cette étape, nous vous recommandons de sauvegarder d'abord votre sketch.

Nos sons peuvent venir de plusieurs sources : des sons téléchargés, des sons que vous avez créés vous-même via un logiciel comme Audacity, voire des sons que vous pouvez trouver par défaut dans votre système d'exploitation.

Identifiez et sélectionnez à présent le fichier son que vous souhaitez utiliser, et glissez-le directement sur la fenêtre Processing :

Image non disponible

Cette action placera le son de ce fichier dans le dossier « data » de votre sketch (un message s'affichant au niveau de la console de Processing vous le confirmera). Si vous voulez voir ce que contient ce dossier, taper sur ctrl-k (Windows/Linux) ou cmd-k (Mac), sinon choisissez dans le menu Sketch > Show Sketch Folder.

V-E-4. Formats audio

Vous pouvez utiliser trois formats audio avec Minim : WAV, AIFF, et MP3. Les deux premiers formats peuvent être exportés par défaut via des logiciels libres comme Audacity. Le troisième format nécessite à ce jour un plug-in pour Audacity, mais celui-ci est relativement facile à installer.

Comme pour les images, les différents formats ont leurs avantages et désavantages.

  • Les formats WAV et AIFF sont quasi identiques et sont deux formats non compressés. Ils peuvent être lus sans trop de difficultés par Minim, car ils ne requièrent pas de décompression. Ils sont souvent utilisés pour des sons courts, de type bruitage. Pour un morceau de musique de même durée, ces deux formats occupent beaucoup plus de place mémoire que les fichiers au format MP3.
  • Le format MP3 est un format compressé. Il est souvent beaucoup moins volumineux que les fichiers WAV et AIFF. Par contre, la lecture du format MP3 nécessite plus de ressources de calcul de votre ordinateur, car le fichier doit être décompressé lors de sa lecture. 

V-E-5. Une horloge

Voici un programme simple, qui anime une ellipse avec les secondes de l'horloge de votre ordinateur (l'horloge système). Nous allons utiliser ce programme comme base pour créer une horloge qui sonne toutes les minutes. Pour mieux anticiper cet événement qui se déroule toutes les soixante secondes - car il nécessite une certaine patience - nous allons représenter les secondes par une ellipse qui s'agrandit progressivement depuis le centre de l'espace de dessin.

Image non disponible
 
Sélectionnez
void draw() {
  background(255);
  ellipse(50,50,second(),second());
}

Nous utiliserons la valeur des secondes de l'horloge système pour définir les paramètres {largeur,hauteur} de l'ellipse. Si les secondes sont égales à 42, nous aurons une ellipse de 42 × 42 pixels. Si les secondes sont égales à 2, nous aurons une ellipse de 2 × 2 pixels.

Nous pouvons également ajouter une condition pour signaler davantage le changement des minutes : à chaque fois que les secondes reviendront à zéro, nous marquerons l'événement avec un écran rouge.

Image non disponible
 
Sélectionnez
void draw() {
 
  if (second() == 0) {
    background(255,0,0);
  } else {
    background(255);
  }
 
  ellipse(50,50,second(),second());
}

La condition if(second() == 0) {} compare les secondes avec la valeur 0. Si les deux sont égales, le fond sera rouge, sinon (else) le fond sera blanc.

V-F. Une horloge sonore

Nous allons maintenant sonoriser ce programme en jouant un son à chaque fois que les secondes de notre horloge reviennent à zéro.

Pour commencer, nous allons ajouter autour de notre méthode draw() de l'horloge, toutes les méthodes nécessaires pour la sonoriser (lignes de code mentionnées en gras).

 
Sélectionnez
import ddf.minim.*;
 
Minim minim;
AudioSnippet ping;
 
void setup() {
  minim = new Minim(this);
  ping = minim.loadSnippet("ping.wav");
}
 
void draw() {
 
  if (second() == 0) {
 
    if (ping.isPlaying() == false){
      ping.play();
    }
 
    background(255,0,0);
 
  } else {
    background(255);
  }
 
  ellipse(50,50,second(),second());
}
 
void stop() {
  ping.close();
  minim.stop();
  super.stop();
}

Attardons-nous sur la structuration de ce sketch :

Image non disponible

Ce programme nécessite de nombreuses étapes pour fonctionner :

  • Importer les fonctionnalités Minim dans notre sketch.
  • Créer deux variables dont l'une qui contiendra le moteur Minim et l'autre les données audio.
  • Démarrer le moteur Minim au début du programme.
  • Copier les données du fichier audio dans notre variable.
  • Actions conditionnelles : jouer le son et afficher un écran rouge lorsque les secondes sont égales à zéro (vérifier que ce son n'est pas déjà en train de jouer).
  • Dessiner une ellipse en fonction de la valeur des secondes de l'horloge.
  • À la fin du programme, arrêter le son.
  • À la fin du programme, arrêter Minim.

Nous avons déjà expliqué le démarrage et l'extinction du moteur Minim. Nous n'y revenons pas. Par contre, nous attirons votre attention sur les parties 2, 4 et 5 du programme qui concernent l'importation et la lecture du son.

Tout d'abord, nous avons créé une variable de type AudioSnippet. Le type AudioSnippet est un type de variable défini à l'intérieur du code de Minim. Nous vous rappelons que dans le cas de n'importe quelle variable, la formule pour la déclarer est de la forme {type de la variable} {nom de la variable} = {les valeurs de la variable}. Par exemple, si par un tour de force il était possible d'importer un petit oiseau tout mignon dans Processing, il suffirait d'écrire PetitOiseau pioupiou = loadBird("pioupiou.bird");. En d'autres termes, on écrit d'abord le type de la chose, le nom de la chose, et enfin on lui donne sa valeur. Pour revenir à notre exemple d'horloge sonore, cette valeur est donnée par la fonction loadSnippet() qui va aller chercher les valeurs d'ondes dans le fichier audio et les importera dans notre variable nommée « ping ».

Pour plus d'informations sur les variables et les différents types de variables, reportez-vous au chapitre dédié à ce sujet.

Une fois la variable chargée avec le son, il suffit de le jouer. Dans le cas du AudioSnippet la commande pour jouer un son s'appelle play(). Enfin, il peut être important de vérifier à un moment donné si le son n'est pas déjà en train d'être joué. C'est le rôle de la commande isPlaying() de nous dire si oui (true) ou non (false) nous sommes dans cette situation. Dans notre exemple, si nous avions oublié cette condition, le son se serait lancé pendant soixante fois durant la totalité de la seconde où second() est égal à zéro.

V-F-1. AudioSnippet, AudioSample, AudioPlayer

La bibliothèque Minim distingue plusieurs manières de jouer un son dans Processing. En principe, les instructions à utiliser dépendront de la nature et rôle du son dans votre programme. Si l'on se réfère à la documentation décrivant les fonctionnalités offertes par Minim, un AudioSnippet sera employé pour une lecture simple d'un court extrait sonore, un AudioSample pour une lecture rapide et répétée comme une boîte à rythmes, et enfin un AudioPlayer pour jouer des fichiers audio plus longs (souvent de type MP3) situés directement sur le disque dur de votre ordinateur pour ne pas alourdir encore davantage la mémoire interne du programme. Voici un exemple qui utilise les trois méthodes de lecture du son associées respectivement à trois fichiers audio imaginaires dénommés ping, pop et song.

 
Sélectionnez
import ddf.minim.*;
 
Minim minim;
AudioSample ping;
AudioSnippet pop;
AudioPlayer song;
 
void setup() {
  minim = new Minim(this);
  ping = minim.loadSample("ping.aiff");
  pop = minim.loadSnippet("pop.aiff");
  song = minim.loadFile("song.mp3");
}
 
void draw() {
 
}
 
void keyPressed() {
  if (key == 'a') ping.trigger();
  if (key == 'b') {
    pop.rewind();
    pop.play();
  }
  if (key == 'c') song.play();
}
 
void stop() {
  ping.close();
  pop.close();
  song.close();
  minim.stop();
  super.stop();
}

Notez avant tout que les instructions d'importation et de lectures changent selon le type choisi. AudioPlayer et AudioSample utiliseront tous les deux la méthode play() car leur lecture est toujours unique (un seul son à la fois). Par contre, AudioSample nécessite la lecture de nombreux sons simultanés (on peut taper plusieurs fois sur une batterie) et pour cette raison utilise une autre nomenclature,  trigger(), pour décrire le lancement d'un son. En anglais, le mot « trigger » signifie une gâchette.

V-F-2. Documentation

Pour plus d'informations sur les différentes fonctionnalités de Minim, ainsi qu'un grand nombre d'exemples d'utilisation (avec leur code source librement mis à disposition), reportez-vous au site http://code.compartmental.net/tools/minim/ ou à la documentation technique (plus difficile à lire) consultable à l'adresse http://code.compartmental.net/minim/javadoc/ddf/minim/package-tree.html.


précédentsommairesuivant

Licence Creative Commons
Le contenu de cet article est rédigé par Flossmanuals et est mis à disposition selon les termes de la Licence Creative Commons Attribution 3.0 non transposé.
Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright © 2013 Developpez.com.