Avant de t'attaquer à ce défi, on te conseille de compléter les deux défis suivants:

  1. En 2020 je serai...
  2. Ça va bien aller

Première chose avant de commencer à travailler sur n'importe quel projet p5.js: connecte-toi à ton compte en cliquant sur "Log in".

Renomme ton projet en cliquant sur le petit crayon .

Notre but pour la première partie du défi est simplement de faire apparaître deux ballons de différentes couleurs sur notre canvas et de les faire bouger du bas du canvas vers le haut.

🤔 Comment faire pour dessiner des ballons en p5.js? Rappelle-toi qu'il faut toujours commencer par la solution la plus simple! Une fois qu'elle fonctionne, tu peux rendre ton code plus sophistiqué.

Dans le défi Ça va bien aller, on a vu comment dessiner en p5.js des cercles de différentes tailles et couleurs.On peut donc utiliser la fonction circle() pour dessiner un ballon rouge. Ce ne sera pas un ballon très réaliste, mais c'est un début!

Où doit-on utiliser la fonction circle() dans notre code? Dans la fonction setup()? Dans la fonction draw()?

Dans la fonction draw(), vu que les coordonnées de notre ballon vont changer pendant l'exécution du programme.

Ton code pourrait avoir l'air de quelque chose comme ceci:


                  //...
                  function draw() {
                  background(220);

                  fill("red");
                  circle(100, 100, 60);
                }
              

Pour la tige de ton ballon, cherche dans la documentation de p5.js une fonction qui pourrait dessiner une ligne.

En anglais, le mot "ligne" se dit line...

Si tu choisis les mêmes coordoonées d'origine de ton trait que pour le cercle, ça va donner un drôle de résultat:

line(100 , 100, 100, 250);

Tu veux que le trait commence là où finit le cercle rouge. Tu dois donc "baisser" le trait, c'est-à-dire changer sa position sur l'axe des y.

On va maintenant faire bouger notre ballon du bas vers le haut de notre canvas.

C'est la position sur l'axe vertical qui doit constamment changer. À chaque fois que la fonction draw() est appelée, la position en Y doit avoir... diminué ou augmenté?🤔

Diminué... vu que l'origine de ton canvas et en haut à gauche. La position en Y va commencer à 200, puis 199, 198...

Comment faire?

Il faut choisir une position en Y et faire en sorte que l'ordinateur s'en souvienne pour pouvoir la réutiliser. Et qui dit "se souvenir" en programmation, dit...

VARIABLES!

On va stocker la valeur de la position en Y dans une variable réutilisable:


            let positionY= 400;
            circle(200, positionY, 60);
          

Maintenant, diminuons la valeur de cette variable. Pour augmenter la valeur d'une variable de 1, on peut écrire ceci: positionY++ ou ceci: positionY += 1. À ton avis, comment faire pour diminuer la valeur?

Lance ton code pour tester... Notre ballon reste en place 😢. Pourtant, si tu utlises le bon vieux console.log() tu devrais voir que la valeur de positionY change. En fait, elle alterne entre deux valeurs...

Comment faire pour que la valeur diminue constamment?

Tu ne peux pas déclarer ta variable positionY dans la fonction draw(), car cette dernière est appellée plusieurs fois par seconde. Et donc plusieurs fois par seconde, on replace le ballon à sa position verticale initiale.

Tu as sans doute noté qu'il va falloir également modifier le code de la tige du ballon pour qu'elle suive... C'est là où utiliser des variables s'avère très utile!

Notre code de base fonctionne, alors on peut s'amuser à le rendre plus sophistiqué. Ce qui serait chouette, c'est de travailler sur l'apparence du ballon pour qu'il ait une forme plus réaliste.

Au lieu d'utiliser un cercle, tu peux utiliser une ellipse. Consulte la documentation de p5.js pour dessiner des ellipses.

L'idée, c'est d'utiliser deux ellipses: une pour le ballon lui-même, et l'autre (toute petite) pour son noeud.

Quelques pistes pour t'aider:

  • Pour enlever le contour noir du ballon, il faut utiliser la fonction noStroke()
  • Si tu utilises la fonction noStroke() ta tige va probablement disparaître. Tu peux la faire apparaître à nouveau avec la fonction stroke()

Donne une couleur aléatoire à ton ballon à chaque fois que tu déclenches ton animation. Ici tu as deux options:

  1. Laisser l'ordinateur choisir au hasard une couleur parmi toutes les couleurs possibles du web.
  2. Choisir quelques couleurs de ton choix et donner à ton ballon une de ces couleurs.

Il n'y a pas de "meilleure" façon de faire. C'est plutôt selon ce qui te tente pour ce projet. Dans tous les cas, il est utile de savoir coder les deux options.

Dans les deux options, c'est la fonction fill() qui détermine la couleur d'une forme en p5.js. Cette fonction prend un ou plusieurs arguments (entre les parenthèses). Ces arguments peuvent être de différents types:

  • un nombre, comme par exemple: fill(51);
  • une string, comme par exemple: fill("hotpink"); (oui, oui, hotpink est une couleur du web!) ou encore fill('#44344f');
  • un objet color, qu'on obtient avec la fonction color()

A. Couleur complètement au hasard

Pour laisser l'ordinateur choisir complètement au hasard, il faut utiliser un argument avec des nombres. Si on laisse l'ordinateur choisir au hasard des lettres (pour une string, il va former des mots complètement loufoques. L'instruction fill("qwemzi"); n'aura pas beaucoup de succès, comme tu peux te l'imaginer!

On pourrait utiliser la formule fill(51) et remplacer le 51 par un nombre au hasard. Comment faire pour trouver un nombre au hasard en p5.js? Avec la fonction random().

Problème! La couleur est toujours noire et le noeud de mon ballon est toujours rouge...

Pour résoudre le fait que la couleur est toujours noire, il faut voir ce que random() génère comme nombre. Comme faire pour inspecter🔎? Avec un console.log() comme toujours!

Toutes les nombres générés par la fonction random() se trouvent entre 0 et 1. Or pour la fonction fill(), si on code fill(0), c'est la même chose que de coder fill("black"). L'échelle va de 0 à 255. Si tu codes fill(140) tu vas déjà voir une différence de couleur.

Si on veut un nombre au hasard de 0 à 255, on peut donner deux arguments à notre fonction random, comme ceci: random(0, 255)

De nouveaux problèmes!!! YESS!!! Notre ballon change de couleur aléatoirement, mais constamment... et les couleurs semblent être sur une échelle de gris! Et comme ce n'est pas le moment de tester les 50 nuances de gris... 😉

Une chose à la fois!

À ton avis, pourquoi la couleur change constamment (ce qui donne un effet stroboscopique passablement desagréable)?

On utilise (appelle) la fonction random() à l'intérieur de la fonction draw() qui elle-même est appelée plusieurs fois par seconde. La couleur change donc plusieurs fois par seconde.

Pour remédier à ce problème, il faut appeler la fonction random() à l'extérieur de la fonction draw(). Deux hypothèses:

  • Au tout début de ton code (hors de toute fonction);
  • Dans la fonction setup() qui elle n'est appelée qu'une seule fois au début de ton animation.

Tu ne peux pas utiliser la fonction random() au tout début de ton code. Cette fonction est une fonction spécifique de la librairie p5.js. Elle ne peut donc être utilisée qu'à l'intérieur des deux fonctions p5.js que sont setup() et draw(). On sait déjà que ce ne peut être dans la fonction draw() sinon, on retombe dans l'effet stroboscopique.

Par conséquence, on doit coder cela dans la fonction setup():


                function setup() {
                  createCanvas(400, 400);
                  fill(random(0, 255));
                }
              

Assure-toi qu'il ne reste pas de fill("red") dans ta fonction draw() sans quoi le ballon va reprendre sa couleur rouge!

Comment faire pour avoir des couleurs autres que sur une échelle de gris?

Tu peux utiliser la fonction color() avec 3 arguments (le premier représente les rouges, le deuxième les verts et le troisième les bleus). C'est ce qu'on appelle la notation RGB (Red, Green, Blue).

Ce code va te donner un rectangle bleu, par exemple:

En combinant les fonctions fill(), color() et random(), tu pourras obtenir une couleur aléatoire pour ton ballon à chaque fois que tu relances ton animation.

B. Une couleur parmi quelques couleurs choisies

Comme on l'a vu plus haut, la fonction fill() une string comme argument. Cette string sera le "nom" de la couleur en anglais:

Si tu te demandes où j'ai pêché les couleurs peachpuff et seagreen, ce site web va t'intéresser 🎨🖌️

Une fois que tu as choisi quelques couleurs que tu aimes, il faut les stocker dans la mémoire de ton programme p5.js. Quelle est la chose qu'on utilise toujours en programmation pour stocker des données?

Une variable par Toutatis! Et pour stocker plusieurs informations dans une variable, le mieux c'est d'utiliser un tableau (array en anglais):


                let colors = ["yellow", "peachpuff", "seagreen"];
              

Pour aller chercher une couleur au hasard dans cette liste, tu peux utiliser notre bonne vieille méthode random():


                let colors = ["yellow", "peachpuff", "seagreen"];
                fill(random(colors));
              

Note que tu aurais pu utiliser le code hexadécimal des couleurs ('#44344f') plutôt que leur nom.

Code un second ballon! Pour commencer il peut s'envoler en même temps que le premier. Mais il doit ne doit pas être superposé sur le premier.

Tu peux réutiliser le code de ton premier ballon, mais la position en x doit être différente.

Comment coder un troisième ballon? Et un quatrième? C'est toujours le même code qu'on copie et qu'on colle. Quelle est la seule chose qui change? La position de départ sur l'axe des x du ballon.

Les bons devs sont très paresseux! Pour être exact, ils n'aiment pas se répéter... Du coup, faire du copier-coller de plein de lignes, ce n'est pas leur tasse de thé ☕

Lorsque plusieurs instructions se répètent, on peut les "encapsuler" dans une... fonction! Tu as déjà utilisé plein de fonctions: draw(), random(), setup()... Ces fonctions étaient déjà existantes dans la librairie p5.js. On va maintenant coder notre propre fonction!

Nos propres fonctions fonctionnent (haha!) en 2 étapes:

  1. je déclare (définis) ma fonction
  2. j'appelle (utilise) ma fonction

Voici la syntaxe pour déclarer une fonction en JavaScript:

            
              function leNomDeMaFonction() {
                // les instructions de ma fonction vont ici!
              }
            
          

Un peu de vocabulaire en anglais:

Tu pourrais donner n'importe quel nom à ta fonction, mais il vaut mieux que ce soit logique pour que tu puisses t'y retrouver plus tard et pour que tes collègues puissent comprednre ton code! Pas une super idée de coder quelque chose comme ceci (d'autant plus qu'il y a un accent):


            function jaimeLePopcornSalé() {
              noStroke();
              ellipse(100, 400, 65, 80);
            }
          

Ceci a plus de sens, par contre:


            function drawBalloon() {
              noStroke();
              ellipse(100, 400, 65, 80);
            }
          

À la toute fin de ton sketch p5.js, définis ta propre fonction drawBalloon(). Entre les deux accolades {}, insère tout le code nécessaire pour dessiner le premier ballon qui se trouvait jusqu'à maintenant dans la fonction draw().

            
              function draw() {
                background(220);

                // Premier ballon
                // ovale
                noStroke();
                ellipse(100, positionY, 65, 80);

                // corde
                stroke(160);
                strokeWeight(1);
                line(100 , positionY+ 80/2, 100, positionY+80/2+100);

                // noeud
                noStroke();
                ellipse(100, positionY + 80/2, 10, 10);

                // faire monter le premier ballon
                positionY--

                // Deuxième ballon
                // ovale
                noStroke();
                ellipse(300, positionY, 65, 80);

                // corde
                stroke(160);
                strokeWeight(1);
                line(300 , positionY+ 80/2, 300, positionY+80/2+100);

                // noeud
                noStroke();
                ellipse(300, positionY + 80/2, 10, 10);

                // faire monter le deuxième ballon
                positionY--
              }

              function drawBalloon() {

              }
            
          

Va devenir:

            
              function draw() {
                background(220);

                // Premier ballon
                ?????

                // faire monter le premier ballon
                positionY--

                // Deuxième ballon
                // ovale
                noStroke();
                ellipse(300, positionY, 65, 80);

                // corde
                stroke(160);
                strokeWeight(1);
                line(300 , positionY+ 80/2, 300, positionY+80/2+100);

                // noeud
                noStroke();
                ellipse(300, positionY + 80/2, 10, 10);

                // faire monter le deuxième ballon
                positionY--
              }

              function drawBalloon() {
                // ovale
                noStroke();
                ellipse(100, positionY, 65, 80);

                // corde
                stroke(160);
                strokeWeight(1);
                line(100 , positionY+ 80/2, 100, positionY+80/2+100);

                // noeud
                noStroke();
                ellipse(100, positionY + 80/2, 10, 10);
              }
            
          

Chouette, mais si on lance l'animation, on se retrouve avec un seul ballon (le deuxième). C'est qu'on a défini (déclaré) notre fonction drawBalloon() mais on ne l'a pas appelée (utilisée). C'est l'étape 2 des fonctions!

Pour appeler une fonction, il suffit de l'appeler par son nom et de ne pas oublier les parenthèses (où on pourra mettre plus tard des arguments). Dans mon exemple précédent, là où j'ai laissé des ????, je vais donc pouvoir appeler ma fonction:

            
              function draw() {
                background(220);

                // Premier ballon
                drawBalloon();

                // faire monter le premier ballon
                positionY--

                // Deuxième ballon
                // ovale
                noStroke();
                ellipse(300, positionY, 65, 80);

                // corde
                stroke(160);
                strokeWeight(1);
                line(300 , positionY+ 80/2, 300, positionY+80/2+100);

                // noeud
                noStroke();
                ellipse(300, positionY + 80/2, 10, 10);

                // faire monter le deuxième ballon
                positionY--
              }

              function drawBalloon() {
                // ovale
                noStroke();
                ellipse(100, positionY, 65, 80);

                // corde
                stroke(160);
                strokeWeight(1);
                line(100 , positionY+ 80/2, 100, positionY+80/2+100);

                // noeud
                noStroke();
                ellipse(100, positionY + 80/2, 10, 10);
              }
            
          

Ça y est, les deux ballons sont réaparus! Mais... notre code est plus long qu'avant... 🙀 Pourquoi tout ce bazar de fonctions, alors???

C'est qu'on va pouvoir enlever le code du deuxième ballon de notre fonction draw()...

            
              function draw() {
                background(220);

                // Premier ballon
                drawBalloon();

                // faire monter le premier ballon
                positionY--

                // Deuxième ballon
                ????

                // faire monter le deuxième ballon
                positionY--
              }

              function drawBalloon() {
                // ovale
                noStroke();
                ellipse(100, positionY, 65, 80);

                // corde
                stroke(160);
                strokeWeight(1);
                line(100 , positionY+ 80/2, 100, positionY+80/2+100);

                // noeud
                noStroke();
                ellipse(100, positionY + 80/2, 10, 10);
              }
            
          
            
              function draw() {
                background(220);

                // Premier ballon
                drawBalloon();

                // faire monter le premier ballon
                positionY--

                // Deuxième ballon
                ????

                // faire monter le deuxième ballon
                positionY--
              }

              function drawBalloon() {
                // ovale
                noStroke();
                ellipse(100, positionY, 65, 80);

                // corde
                stroke(160);
                strokeWeight(1);
                line(100 , positionY+ 80/2, 100, positionY+80/2+100);

                // noeud
                noStroke();
                ellipse(100, positionY + 80/2, 10, 10);
              }
            
          

Par quoi faut-il remplacer les ????? de mon code?

Il faut que j'appelle à nouveau ma fonction drawBalloon()

Notre code est beaucoup plus court... mais notre deuxième ballon a disparu! En fait, les deux ballons sont superposés car ils ont tous deux la valeur de la position en X déterminée dans la fonction drawBalloon(). Dans mon cas, la position est 100.

Ce que je veux, c'est pouvoir changer la position en x dans ma fonction. La première fois que j'appelle ma fonction, je veux que x soit 100, et la deuxième fois, je veux que x soit 300. Pour ce faire, je vais ajouter le paramètre x dans la définition de ma fonction:

            
              function drawBalloon(x) {

              }
            
          

Partout où j'avais auparavant 100, je peux maintenant utiliser mon paramètre:

            
              function drawBalloon(x) {
                // ovale
                noStroke();
                ellipse(x, positionY, 65, 80);

                // corde
                stroke(160);
                strokeWeight(1);
                line(x , positionY+ 80/2, x, positionY+80/2+100);

                // noeud
                noStroke();
                ellipse(x, positionY + 80/2, 10, 10);
              }
            
          

Si tu lances ton code... catastrophe!!! Plus de ballons du tout! Il y a cependant un message d'erreur dans la console:

En d'autres mots, le paramètre x était vide et du coup, p5.js ne sait plus comment dessiner l'ellipse de notre ballon. Pour donner une valeur à x, il faut le faire à chaque fois qu'on appelle la fonction:

            
              function draw() {
                background(220);

                // Premier ballon
                drawBalloon(100);

                // faire monter le premier ballon
                positionY--

                // Deuxième ballon
                drawBalloon(????);


                // faire monter le deuxième ballon
                positionY--
              }

              function drawBalloon(x) {
                // ovale
                noStroke();
                ellipse(x, positionY, 65, 80);

                // corde
                stroke(160);
                strokeWeight(1);
                line(x , positionY+ 80/2, x, positionY+80/2+100);

                // noeud
                noStroke();
                ellipse(x, positionY + 80/2, 10, 10);
              }
            
          

YIHAAAAAA! Ça fonctionne! BRAVO!!! Et si tu n'es pas déjà écoeuré, tu peux refactoriser encore plus ta fonction draw(). As-tu vraiment besoin de faire diminuer la variable positionY deux fois?...

Maintenant que nos deux ballons s'envolent, essayons de voir comment les... péter 💪 La première chose à apprendre, c'est comment faire pour que l'ordinateur réagisse quand on clique sur le canevas. La librairie p5.js est très utile pour ça, car elle a plein de fonctions (pré-définies) qui servent à détecter les actions de l'utilisateur.

La fonction qu'on va utiliser s'appelle mouseClicked (souris cliquée).

On va définir la fonction mouseClicked après les fonctions setup(), draw() et drawBalloon(). La première instruction qu'on va mettre dans la fonction mouseClicked, c'est une instruction pour afficher quelque chose dans la console.

Chaque fois que tu cliques sur le canevas, tu vois le mot "cliqué!" apparaître dans la console. Bien sûr, notre but n'est pas d'afficher un message dans notre console mais plutôt de faire "disparaître" le ballon qui a été cliqué.

Pour commencer, on va se concentrer sur notre premier ballon. On ne va pas l'effacer mais plutôt en donner l'illusion! En fait, on va repositionner le ballon en bas du canevas et on va changer sa couleur. Ni vu ni connu!

À ton avis, que faut-il coder dans la fonction mouseClicked() pour changer la position et la couleur du ballon?

                
                function mouseClicked() {
                  // pour choisir une nouvelle couleur au hasard parmi la liste des couleurs que j'ai choisies et stockées dans une variable colorsList:
                  fill(random(colorsList));

                  // pour replacer le ballon au bas du canevas
                  positionY = ???;
                }
                
              

Remplace les ??? de mon code par la valeur en Y qui te permettra de positionner ton ballon en bas du canevas.

Dès qu'on clique n'importe où sur notre canevas, le ballon retourne au bas du canevas. Sympa... mais le ballon ne devrait faire cela que lorsqu'on clique sur celui-ci!

C'est le temps de mettre une condition à notre retour au bas du canevas. La syntaxe d'une condition en JavaScript est la suivante:


            if (CONDITION) {
              // CODE À EXÉCUTER
            }
          

On connaît déjà que notre code à exécuter. C'est celui qu'on avait avant!


            function mouseClicked() {
              if (CONDITION) {
                // pour choisir une nouvelle couleur
                balloonColor = random(colors);

                // pour replacer le ballon au bas du canevas
                positionY = 400;
              }
            }
          

Et maintenant, la condition! Comment exprimer en code "quand l'utilisateur clique le ballon"? Rappelle-toi que dans une animation (comme dans beaucoup de jeux vidéo), tout est une question de coordonnées. Du coup, on va vérifier la distance de notre souris par rapport au centre de notre ballon. Et bien entendu, il existe une chouette fonction p5.js pour calculer la distance entre deux points: dist()

Observe les paramètres de la fonction. C'est un peu comme pour la fonction line(), il y a 4 paramètres qui représentent les coordonnées des deux points dont on mesure la distance.

Notre premier point en bleu est le centre du cercle (ou de l'ellipse) qui forme le ballon. Le second point en rouge est l'endroit où le joueur a cliqué. Pour le point rouge, tu peux obtenir les coordonnées avec deux variables spéciales de p5.js appellées mouseX et mouseY.

Pour obtenir la distance, tu peux donc coder ceci: let d = dist(x1, y1, mouseX, mouseY); À toi de remplacer x1 et x2 par les variables appropriées!

Pour la position en Y, tu as déjà déclaré une variable positionY. Pourrais-tu également avoir une variable en X?

Maintenant, utilises console.log(d) pour voir la distance s'afficher à chaque fois que tu cliques:

Combine maintenant la distance avec la condition:

            
            function mouseClicked() {
              let d = dist(positionX, positionY, mouseX, mouseY);
              if (d ????) {
                // pour choisir une nouvelle couleur
                balloonColor = random(colors);

                // pour replacer le ballon au bas du canevas
                positionY = 400;
              }
            }
            
          

Remplace les ???? dans mon exemple et voilà, le tour est joué! Bravo!!!!