Avant de t'attaquer à ce défi, assure-toi d'avoir fait ceux d'avant sur la liste.

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

Les classes.

C'est un vaste sujet, dont on pourrait parler des heures! C'est aussi un sujet qui existe dans plusieurs autres langages informatiques que JavaScript comme Python, Ruby, etc.

Un peu comme les fonctions, les classes servent à simplifier notre code. On pourrait écrire TOUT notre code sans fonctions ni classes... mais ça prendrait des tonnes de lignes et ça serait super long. Vraiment pas une perspective réjouissante!

Les classes, c'est un regroupement de variables et de fonctions qui servent de "modèles" réutilisables ailleurs dans notre code.

Prenons cette analogie avec le "vrai" monde. Lorsqu'un architecte veut construire des maisons, il dessine un plan (le modèle). Basé sur son plan, l'architecte peut ensuite créer autant de maisons qu'il veut. Chaque maison pourra avoir quelques variations (ici une porte rouge, ici une porte bleue), mais toutes les maisons vont partager des caractéristiques communes: elles auront des murs, des fenêtres, un toit...

En JavaScript, c'est pareil! Imagine que tu veux dessiner des bulles de savon. Chaque bulle pourrait avoir une couleur différente, mais toutes les bulles auront des caractéristiques communes: elles seront rondes, se déplaceront, auront une lettre...

En JavaScript, le code qui établit le modèle d'une bulle s'appelle une classe. Une fois que la classe est créée, on peut alors créer des instances de cette classe. Ce sont toutes les bulles. Si on veut, on peut créer une seule instance (une seule bulle). Mais on peut également créer 324328 bulles. C'est facile de créer une bulle supplémentaire, car on a déjà un modèle (la classe)!

Résumé: la classe, c'est comme les plans de l'architecte.

Les instances, ce sont les vraies maisons qu'on a construites selon le plan.

Les classes en JavaScript représentent souvent des choses de la vraie vie. Comme des maisons, mais encore des étoiles, des bulles, des joueurs, des élèves, etc.

Voici comment on définirait une classe qui représente le concept d'élève:

              
              class Student {

              }
              
            

Note qu'on a utilisé le mot clé class et que le mot qui suit Student est en UpperCamelCase (en gros, le mot commence avec une majuscule).

Cette classe n'est pas très palpitante. On n'a aucune idée des caractéristiques que chaque élève (student) devrait avoir. Pour ce faire, utilisons la fonction constructor()

              
              class Student {
                constructor() {
                  this.age = random(10, 16);
                  this.name = "Camille";
                }
              }
              
            

Vu que constructor() est une fonction, normalement, on écrirait plutôt:

              
                function constructor() {
                  this.age = random(10, 16);
                  this.name = "Camille";
                }
              
            

mais dans la définition de notre classe, les règles de syntaxe sont un peu différentes!

Tous les étudiants de notre programme auront deux caractéristiques: "age" et "name" (âge et nom).

Maintenant que nous avons notre modèle (la classe) pour créer des étudiants, voyons comment créer un étudiant (une instance). À l'extérieur de la classe, on écrit ceci: new Student()

C'est tout! Pour créer deux étudiants:

              
              new Student();
              new Student();
              
            

Pour créer trois étudiants:

              
              new Student();
              new Student();
              new Student();
              
            

Il se peut qu'on ait besoin de réutiliser nos étudiants plus tard dans notre code. Comment faire pour que l'ordinateur se souvienne de tous les étudiants qu'on a créés?

On les stocke dans des variables!

                  
                  let studentOne = new Student();
                  let studentTwo = new Student();
                  let studentThree = new Student();
                  
                

Ouvre le projet p5.js suivant.

Tu constateras qu'il y a différentes classes qui sont déjà pré-codées. Peux-tu les trouver?

Les classes ne sont pas dans le fichier sketch.js


Une fois que tu as la liste, dans le fichier sketch.js, crée une instance de chaque classe au bon endroit (dans la fonction draw(), là où il y a les commentaires).

Si tu lances ton programme, tu verras qu'il ne se passe pas grand chose sur notre canevas.

Tu peux cependant t'assurer que tes instances ont bien été créées en les stockant dans une variable et en opérant un console.log():

Il est temps de retourner à nos classes! Une classe comporte très souvent une fonction constructor. Mais ce n'est pas la seule fonction possible! À vrai dire, les classes ont généralement plusieurs fonctions. Rappelle-toi: les classes sont souvent une représentation d'objets réels (un étudiant, une étoile, une voiture...). Tous ces objets font des choses. L'étudiante remet un devoir, l'étoile brille dans le ciel, la voiture démarre...

En d'autre termes, les objets ont un comportement (en anglais, on parle de behavior). Et en code, quand on parle d'actions ou de comportement, on pense tout de suite à... des fonctions! Voici comment je pourrais ajouter une fonction à la classe Student:

            
            class Student {
              constructor() {
                this.age = random(10, 16);
                this.name = "Camille";
                this.homework = 0;
              }

              handInHomework() {
                this.homework++;
                console.log(this.name + " a remis un devoir!");
              }
            }
            
          

Ici, j'ai ajouté une fonction, la fonction handInHomework() (remettre un devoir). Note que dans la mesure du possible, il faut trouver des noms de fonctions en anglais. C'est la langue privilégiée par les dévs.

Toutes les instances de la classe Student pourront maintenant... remettre un devoir! Voici comment cela fonctionne:

            
            // Je définis ma classe:
            class Student {
              constructor() {
                this.age = random(10, 16);
                this.name = "Camille";
                this.homework = 0;
              }

              handInHomework() {
                this.homework++;
                console.log(this.name + " a remis un devoir!");
              }
            }

            // Je crée une instance de la classe et je la sauvegarde dans une variable
            let firstStudent = new Student();

            // J'utilise la fonction handInHomework()
            firstStudent.handInHomework();

            
          

À ton avis, est-ce que notre étudiante peut remettre un second devoir? Si oui, comment?

Bien entendu! Il suffit d'utiliser la fonction handInHomework() une deuxième fois:

                
                // Je crée une instance de la classe et je la sauvegarde dans une variable
                let firstStudent = new Student();

                // L'étudiante remet son premier devoir
                firstStudent.handInHomework();

                // L'étudiante remet son deuxième devoir
                firstStudent.handInHomework();
                
              

Retourne voir les classes Star, Building et Moon du projet p5.js. Chacune de ces classes a une fonction en plus de la fonction constructor(). Laquelle?

C'est la fonction display().

Et à ton avis, à quoi sert cette fonction?

Elle sert à montrer l'objet sur le canevas. Display veut dire "montrer" ou "afficher".

Retourne dans le fichier sketch.js et dans la fonction draw(), tente d'ajouter des lignes de code pour que la lune, une étoile et un gratte-ciel apparaissent sur le canevas.

Il faudra utiliser la fonction display().

                
                let moon = new Moon();
                moon.display();
                
              

Ooops... on a un petit problème... l'étoile et le gratte-ciel ne tiennent pas en place! Par contre, aucun problème du côté de la lune... Pourquoi?

Si notre gratte-ciel ne cesse de bouger partout sur le canevas, c'est qu'on a crée notre nouvel objet Building dans notre fonction draw().

En d'autres mots, 30 fois par seconde, l'ordinateur crée un nouveau gratte-ciel. Or examinons les propriétés d'un nouveau gratte-ciel (dans la classe Building):

            
            class Building {
              constructor() {
                this.levels = random(5,7);
                this.gray = random(["DarkGray", "Gray", "dimgray", "LightSlateGray", "SlateGray", "black"]);
                this.position = createVector(random(0,CANVAS_WIDTH), random(SKY_LEVEL, CANVAS_HEIGHT));
              }
              //...
            }
           
         

Combien de propriétés y a-t-il?

Il y a trois propriétés:

  • levels
  • gray
  • position

C'est la propriété position qui nous intéresse. La partie createVector peut sembler étrange, mais ce qui est important de noter c'est l'utilisation de la fonction random(). Cela veut dire qu'à chaque fois qu'un nouvel objet Building est créé, il aura une position aléatoire.

Le problème est le même pour notre étoile. Regarde la fonction constructor() de l'étoile:

            
            class Star {
              constructor() {
                this.position = createVector(random(0,CANVAS_WIDTH-10), random(0,210));
                this.color = 'gold';
              }

              //...
            }
            
          

Là aussi, la propriété position de notre étoile est aléatoire (ici aussi, on utilise la fonction random()).

Par contre, dans la fonction constructor() de la lune, la position est fixe:

            
            class Moon {
              constructor() {
                this.position = createVector(500, 60);
              }

              //...
            }
            
          

Pour remédier au problème du gratte-ciel et de l'étoile, il faut s'assurer qu'on ne les crée qu'une seule fois! Autrement dit, on ne peut pas coder let star = new Star(); dans la fonction draw().

Pense à la fonction qui ne s'exécute qu'une seule fois pendant tout le programme... Et n'oublie pas que si tu déclare une variable dans cette fonction, elle ne sera pas accessible dans la fonction draw()...

Dans ton fichier sketch.js, voici ce que tu peux faire pour l'étoile:

                
                // je déclare ma variable au tout début de mon code pour y avoir accès dans toutes les fonctions
                let star;

                function setup() {
                  // je crée une étoile et je la sauvegarde dans ma variable
                  star = new Star();
                }
                function draw() {
                  // j'utilise la fonction display() pour faire en sorte que l'étoile soit constamment visible à l'écran
                  star.display();
                }
                
              

Tu peux faire quelque chose de semblable pour le gratte-ciel.

Maintenant que tu as une étoile qui reste bien en place, tu peux en créer plusieurs facilement. Il suffit de les sauvegarder dans une liste. En programmation, on appelle des listes des arrays.

Première étape: c'est créer une liste vide et la sauvegarder dans une variable. Voici un exemple:

            
            let students = [];
            
          

Ici, j'ai créé une liste vide [] que j'ai sauvegardée dans la variable students.

Deuxième étape: créer les éléments (objets) qu'on va vouloir ajouter à la liste:

            
            let students = [];

            let firstStudent = new Student();
            let secondStudent = new Student();
            
          

Ici j'ai créé deux objets Student et je les ai sauvegardés dans deux variables (firstStudent et secondStudent.

Troisième étape: ajouter les éléments à la liste. Pour cela, on utilise la fonction push():

            
            let students = [];

            let firstStudent = new Student();
            let secondStudent = new Student();

            students.push(firstStudent);
            students.push(secondStudent);
            
          

Si je fais un console.log sur ma variable students, voici ce que la console me montre:

Les deux étudiantes (qui s'appellent toutes les deux Camille) sont bien dans ma liste.

Et si je voulais 143 étudiantes? Ça serait suuuuuuper long de créer une variable pour chacune des étudiantes...

            
            let students = [];

            let firstStudent = new Student();
            let secondStudent = new Student();
            let thirdStudent = new Student();
            let fourthStudent = new Student();
            let fifthStudent = new Student();
            // ...

            students.push(firstStudent);
            students.push(secondStudent);
            students.push(thirdStudent);
            students.push(fourthStudent);
            students.push(fifthStudent);
            // ...
            
          

Ce que tu peux faire, c'est utiliser une boucle:

            
            for (let i = 0; i < 143; i++) {
              let student = new Student();
              students.push(student);
            }
            
          

Ou, pour être encore plus efficace:

            
            for (let i = 0; i < 143; i++) {
              students.push(new Student());
            }
            
          

Avec ces trois lignes, tu viens de créer 143 étudiantes! Imaginons maintenant qu'elles remettent chacune un devoir. Pour cela, il faut que j'utilise la fonction handInHomework().

À ton avis, est-ce que je peux coder ceci?

            
            let students = [];

            for (let i = 0; i < 143; i++) {
              students.push(new Student());
            }

            students.handInHomework();
            
          

Malheureusement pas! handInHomework() est une fonction qui s'applique pour un seul étudiant, pas pour une liste d'étudiants!

Il va donc falloir passer à travers chacune des étudiantes de la liste et appliquer la fonction handInHomework(). Rassure-toi, il y a un moyen assez facile de faire cela! À ton avis lequel?

Eh oui, on va une fois de plus utiliser une boucle!

Tu peux utiliser la boucle comme ceci:

                
                for (student of students) {
                  student.handInHomework();
                }
                
              

Au début du code de la nuit étoilée, trois listes vides ont été déclarées. À toi maintenant de créer tes milliers (ok, dizaines) d'étoiles et de gratte-ciels. Que c'est beau!

Peux-tu ajouter quelques étoiles filantes? La particularité des étoiles filantes, c'est que la classe ShootingStar "hérite" de la classe Star. Le concept d'héritage est bien complexe (en programmation comme dans la vraie vie😀), mais pour nous, tout ce qu'il suffit de savoir, c'est que pour créer une étoile filante, on fait comme pour les autres classes:

            
            let shootingStar = new ShootingStar();
            
          

Tu peux donc coder plusieurs étoiles filantes sur le même modèle que tes étoiles normales. Utilise la fonction display() pour voir tes étoiles. Tu verras que tes étoiles filantes sont rouges, mais malheureusement statiques (elles ne bougent pas):

Pour les faire bouger, tu devras utiliser à la fois la fonction display() et une autre fonction de la classe ShootingStar.

Il faut utiliser la fonction update(), suivie de la fonction display().

                
                for (shootingStar of shootingStars) {
                 shootingStar.update();
                 shootingStar.display();
                }
                
              

Tes étoiles filantes filent maintenant le parfait bonheur!