thylo.be

La variable

`draw()`, la variable, l'incrémentation `x = x + 1`, les types, la portée. Racontés avec un chevalier en route vers le château.

Raoul se met en route

Raoul le chevalier part du bord gauche de l'écran et marche vers le château, à droite. Les deux sont dessinés par la boîte à outils, à importer dans le sketch avant de commencer.

let positionX = 0;

async function setup() {
  createCanvas(800, 600);
  await chargerPersonnages();
}

function draw() {
  background(20);
  dessinerChateau(650, 500, 260);
  dessinerRaoul(positionX, 500, 60);
  positionX = positionX + 2;
}

draw() est rejoué soixante fois par seconde, comme les images d'une timeline. Tout ce qu'il contient recommence à l'identique, à une exception près : positionX. Ce nombre change entre deux images, donc Raoul avance.

Une variable est une boîte qui porte un nom. let positionX = 0; fabrique la boîte, lui donne son nom et y range 0 : c'est la déclaration de la variable. Ensuite, partout où positionX est écrit, la machine va lire ce qu'il y a dedans.

Prendre, ajouter, remettre

positionX = positionX + 2 serait faux en mathématiques. En programmation, le = range une valeur dans une variable : c'est une affectation. La ligne se lit de droite à gauche : prendre ce qu'il y a dans positionX, ajouter 2, remettre le résultat dans positionX. Cette opération s'appelle une incrémentation, et une décrémentation quand elle retire.

Elle sert partout où quelque chose s'accumule ou s'use.

pieces = pieces + 10;            // Raoul pille un coffre
pointsDeVie = pointsDeVie - 3;   // une flèche le touche
fleches = fleches - 1;           // il en tire une à son tour

Le nombre de droite décide de l'allure. Avec + 2, Raoul marche. Avec + 8, il est à cheval.

Le type

Une variable ne contient pas toujours un nombre. Les trois sortes utilisées dans le cours :

let pointsDeVie = 30;           // un nombre
let nom = "Raoul";              // du texte, entre guillemets
let aLaClef = false;            // un booléen : true ou false, rien d'autre

La sorte de valeur s'appelle le type, et elle change ce que fait le +. Il suffit de deux guillemets autour du 0 de Raoul pour le voir :

let positionX = "0";

async function setup() {
  createCanvas(800, 600);
  await chargerPersonnages();
}

function draw() {
  background(20);
  dessinerChateau(650, 500, 260);
  dessinerRaoul(positionX, 500, 60);
  positionX = positionX + 2;
}

Raoul disparaît presque aussitôt, et aucun message d'erreur ne le signale.

Entre deux nombres, + additionne. Entre un texte et un nombre, il colle l'un au bout de l'autre : "0" + 2 donne "02". La boîte contient donc un texte, qui s'allonge d'un 2 à chaque image.

Mais dessinerRaoul() calcule avec ce texte. Pour centrer l'image, elle retire la moitié de la largeur à x, et le - n'a qu'un sens : il soustrait. JavaScript convertit donc le texte en nombre avant de calculer, et "0222" devient 222.

ImagepositionX contientRaoul est dessiné à
1"0"0
2"02"2
3"022"22
4"0222"222
5"02222"2222, hors du canvas

Raoul traverse l'écran en quatre images, moins d'un dixième de seconde, puis sort du canvas pour ne plus revenir. console.log(positionX), ajouté dans draw(), affiche dans la console ce que la variable contient vraiment, image après image.

La portée

La ligne let positionX = 0; est écrite au-dessus de setup(), en dehors de toute fonction. Descendue en première ligne de draw(), elle donne ce sketch :

async function setup() {
  createCanvas(800, 600);
  await chargerPersonnages();
}

function draw() {
  let positionX = 0;
  background(20);
  dessinerChateau(650, 500, 260);
  dessinerRaoul(positionX, 500, 60);
  positionX = positionX + 2;
}

Raoul ne quitte plus le bord, et aucun message d'erreur ne le signale : le code est valide.

draw() est rejoué à chaque image, let compris. Chaque image fabrique donc une boîte neuve, y range 0, dessine Raoul à 0 et ajoute 2. Puis draw() arrive à son accolade fermante, et la boîte est jetée avec ce qu'elle contient. L'image suivante repart d'une boîte neuve, avec 0 dedans. Le 2 n'est jamais lu.

La position où Raoul est dessiné, image par image :

Imagelet au-dessus de setup()let dans draw()
100
220
340
61, une seconde plus tard1200
Ce qui est déclaré dans draw() ne survit pas à l'image suivante.

La règle vaut pour toutes les accolades. Une variable existe à partir de son let, jusqu'à l'accolade fermante du bloc qui la contient : cette partie du code s'appelle sa portée. Déclarée en dehors de toute fonction, elle n'a pas d'accolade fermante. Elle vit donc tant que le sketch tourne, et toutes les fonctions la voient : c'est une variable globale.

L'erreur inverse est aussi fréquente. setup() prépare le sketch, et la déclaration y semble à sa place :

async function setup() {
  createCanvas(800, 600);
  await chargerPersonnages();
  let positionX = 0;
}

function draw() {
  background(20);
  dessinerChateau(650, 500, 260);
  dessinerRaoul(positionX, 500, 60);
  positionX = positionX + 2;
}

Cette fois le sketch s'arrête, et la console affiche :

🌸 p5.js says: [sketch.js, line 10] "positionX" is not
defined in the current scope. If you have defined it in
your code, you should check its scope, spelling, and
letter-casing (JavaScript is case-sensitive).

La boîte existe, mais seulement entre les accolades de setup(). Elle est jetée avant la première image, et draw() cherche un nom que rien ne déclare dans sa portée. Le message le dit lui-même : « check its scope », vérifier la portée.

Une variable qui doit se souvenir d'une image à l'autre, comme une position, des points de vie ou une bourse, se déclare donc au-dessus de tout. Une variable qui se recalcule à chaque fois se déclare là où elle sert. outils.js en contient une : dans dessinerPersonnage(), let hauteur est calculée à partir de la largeur à chaque appel, puis jetée.

Les variables de p5

p5 fournit des variables qu'on ne déclare pas et qu'on lit seulement. mouseX et mouseY contiennent la position de la souris, width et height la taille du canvas.

async function setup() {
  createCanvas(800, 600);
  await chargerPersonnages();
}

function draw() {
  background(20);
  dessinerChateau(width - 100, 500, 200);  // le château reste collé au bord droit
  dessinerRaoul(mouseX, mouseY, 60);       // Raoul suit la souris
}