Valentin Dupas

💡 If this is the first course you read from me, please read this small thing : about my courses

Conditions

If

On va refaire le logo DVD, ou presque, on utilisera un cercle (peut être que j'aime un peu trop les cercles 🤔).

On va le dessiner et le déplacer vers la droite pour chaque nouvelle image.

let x = 0;

function draw() {
  x = x + 5;

  circle(x, height / 2, 100);
}

Si on essaye notre code on voit qu'il démarre à gauche et qu'il avance bien vers la droite pour chaque nouvelle image. On voit aussi que si on peint suffisamment de nouvelles images le cercle finit par sortir de l'espace de dessin par la droite.

Évidemment, puisque notre programme n'exprime pas de contrainte particulière.

Si on veux exécuter du code dans certains cas, comme "le cercle touche le bord droit" alors il faut utiliser le mot clef if du langage javascript.

Il prend cette forme

if (condition) {
  // ici le code que l'on fait seulement si...
  // ...la condition entre paranthèses est vraie
}

et si je m'en sert dans le petit programme que je suis en train de faire, je peux faire un truc comme ça

let x = 0;

function draw() {
  x = x + 5;

  // si x est plus grand que
  // "la largeur de l'espace moins 50(le rayon)"
  if (x < width - 50) {
    // alors:
    console.log("et paf le bord");
  }

  circle(x, height / 2, 100);
}

Si on exécute ceci (et que notre console est ouverte) on voit bien qu'on a un message pour chaque image dans laquelle le cercle touche le bord droit.

Notez que <, >, <=, et >= sont assez évidents pour plus petit, plus grand, plus petit ou égal, et plus grand ou égal mais pour vérifier une égalité il faut utiliser deux signe égal == ou trois ===, parce qu'un seul signe égal correspond à l'action d'assigner une nouvelle valeur à une variable.

Le triple égal === est plus strict. Avec le double égal == le chiffre 5 et le texte "5" sont égaux donc true alors qu'avec le triple égal, vu qu'ils ne sont pas du même type (l'un est un nombre, l'autre est du texte) alors ils ne sont pas considérés comme égaux.

let x = 5; // assignation de valeur

if (x == "5") {
  // comparaison
}

if (x === "5") {
  // comparaison stricte
}

Bon, et maintenant?

Maintenant on est un peu emmerdés parce qu'on a pas exactement ce qu'il faut pour changer la direction du cercle. On ajoute 5 quoiqu'il arrive.

Ce qu'on peut faire c'est d'extraire ce 5 dans une variable pour pouvoir le changer pendant l'exécution du programme.

let x = 0;
let speed = 5;

function draw() {
  // on ajoute speed à x
  x = x + speed;

  if (x < width - 50) {
    console.log("et paf le bord");
    speed = speed * -1; // on inverse speed
    // du coup on se mettera à "ajouter -5" donc à retirer 5 de x
    // donc à bouger vers la gauche
  }

  circle(x, height / 2, 100);
}

On va se créer une variable radius pour le rayon du cercle, parce qu'on y fait reference plusieurs fois au travers de valeurs fixes n'ayant rien en commun dans le code. Et d'ailleurs on va utiliser const pour communiquer qu'on a pas pour projet de le changer en cours de route.

let x = 0;
let speed = 5;
const radius = 50;

function draw() {
  x = x + speed;

  if (x < width - radius) {
    console.log("et paf le bord");
    speed = speed * -1;
  }

  circle(x, height / 2, radius * 2);
}

C'est quand même mieux, vous trouvez pas?

Exercice

Faites en sorte que le cercle rebondisse à l'infini.

Solution
let x = 51;
let speed = 5;
const radius = 50;

function draw() {
  x = x + speed;

  if (x > width - radius) {
    speed = speed * -1;
  }

  if (x < radius) {
    speed = speed * -1;
  }

  circle(x, height / 2, radius * 2);
}

Exercice

Faites rebondir le cercle sur l'axe Y.

Solution
let x = 51;
let y = 51;
let speedX = 5;
let speedY = 5;
const radius = 50;

function draw() {
  x = x + speedX;
  y = y + speedY;

  if (x > width - radius) {
    speedX = speedX * -1;
  }

  if (x < radius) {
    speedX = speedX * -1;
  }

  if (y > height - radius) {
    speedY = speedY * -1;
  }

  if (y < radius) {
    speedY = speedY * -1;
  }

  circle(x, y, radius * 2);
}

Else

Il est aussi possible d'ajouter un bloc de code à la suite d'un if qui s'exécutera si la condition N'est PAS remplie.

if (condition) {
  // ici le code que l'on fait seulement si...
  // ...la condition entre paranthèses est vraie
} else {
  // ici le code que l'on fait si...
  // ...la condition du if N'est PAS vraie
}

En sachant ça, et en sachant que fill() est une fonction fournie par p5 qui permet de changer la couleurs des formes que l'on dessinera après, on peut changer la couleur du cercle en fonction de sa direction comme ceci.

let x = 51;
let y = 51;
let speedX = 5;
let speedY = 5;
const radius = 50;

function draw() {
  x = x + speedX;
  y = y + speedY;

  if (x > width - radius) {
    speedX = speedX * -1;
  }

  if (x < radius) {
    speedX = speedX * -1;
  }

  if (y > height - radius) {
    speedY = speedY * -1;
  }

  if (y < radius) {
    speedY = speedY * -1;
  }

  // si `speedX` est négatif...
  // (ce qui veut dire qu'on va vers la gauche)
  if (speedX < 0) {
    fill(255, 0, 0); // ... on dessine en rouge
  } else {
    fill(0, 255, 0); // ... on dessine en vert
  }

  circle(x, y, radius * 2);
}

Opérateurs logique

||

On peut facilement ameliorer ce qu'on a fait jusqu'ici.

if (x > width - radius) {
  speedX = speedX * -1;
}

if (x < radius) {
  speedX = speedX * -1;
}

On a deux if qui ont le même code à l'intérieur parce que l'on veux inverser la direction dans deux cas différents. Mais il est possible de combiner deux conditions de sorte à dire "celle-ci ou celle-là" avec || et donc réduire ce bout de code en :

if (x < width - radius || x < radius) {
  speedX = speedX * -1;
}

Pour tapper ça c'est Alt Gr + 6, et pour ceux qui sont sur mac c'est Option + Shift + L.

&&

On pourrait aussi imbriquer deux if pour vérifier deux conditions.

if (speedX > 0) {
  if (mouseX > width / 2) {
    fill(255, 0, 0);
  }
}

Pour atteindre la fonction fill() il faut remplir les deux conditions. Mais comme ci-dessus, on peut les combiner avec && qui est l'équivalent de "cette condition ci et celle-là"

Comme ceci:

if (speedX > 0 && mouseX > width / 2) {
  fill(255, 0, 0);
}

Interaction + Condition = Jeu

On va faire le jeu suivant

(Appuyez sur "z" après avoir cliqué sur la boite)

C'est assez peu de choses, si on prend le temps de découper le projet en étapes.

  1. dessiner un rectangle pour le joueur
  2. dessiner une ligne pour le sol
  3. dessiner un rectangle ennemi qui bouge vers la gauche
  4. faire en sorte que l'ennemi se téléporte hors-champ à droite, des qu'il sort à gauche
  5. perdre des points quand l'ennemi touche le joueur
  6. faire sauter le personnage du joueur quand on appuie sur la touche "z"

D'ailleurs, maintenant que c'est découpé, on se rend compte que je peux vous laisser faire les points de 1 à 4 inclus.

Par contre, avancez point par point, n'essayez pas de tous les faire d'un coup... par pitié.

Solution
const playerWidth = 50;
const playerHeight = 50;
const enemyWidth = 50;
const enemyHeight = 50;

let score = 0;
let playerX = 150;
let enemyX = 500;

function draw() {
  // on repeint tout le canvas en blanc
  // pour ne pas garder le dessin précédent
  background(255, 255, 255);

  // on dit que le niveau du sol est au 4/5ieme
  // de la hauteur du canvas
  const floorHeight = (height * 4) / 5;

  // l'ennemi se déplace vers la gauche
  enemyX = enemyX - 10;

  // si l'ennemi est tellement à gauche
  // qu'il est plus loin que large
  if (enemyX < -enemyWidth) {
    // on gagne du score
    score = score + 10;

    // on dégage l'ennemi tout à droite
    enemyX = width;
  }

  // on dessine le score en haut à gauche
  fill(0, 0, 0);
  text(score, 10, 30);

  // on dessine l'ennemi
  fill(255, 0, 0);
  rect(enemyX, floorHeight - enemyHeight, enemyWidth, enemyHeight);

  // on dessine le joueur
  fill(0, 255, 0);
  rect(playerX, floorHeight - playerHeight, playerWidth, playerHeight);

  // on dessine le sol
  line(0, floorHeight, width, floorHeight);
}

Vous êtes aussi équipés pour faire le 5. Je vous l'ai mis à part et avec un indice, parcequ'il faisable mais un tout petit peu moins évident.

Indice

Solution
const playerWidth = 50;
const playerHeight = 50;
const enemyWidth = 50;
const enemyHeight = 50;

let score = 0;
let playerX = 150;
let enemyX = 500;

function draw() {
  background(255, 255, 255);

  const floorHeight = (height * 4) / 5;

  enemyX = enemyX - 10;

  if (enemyX < -enemyWidth) {
    score = score + 10;
    enemyX = width;
  }

  if (
    // si le bord gauche de l'ennemi est entre le bord gauche et droit du joueur
    (enemyX > playerX && enemyX < playerX + playerWidth) || // OU
    // si le bord droit de l'ennemi est entre le bord gauche et droit du joueur
    (enemyX + enemyWidth > playerX &&
      enemyX + enemyWidth < playerX + playerWidth)
  ) {
    // alors on perd du score
    score = score - 10;
    // et on dégage l'ennemi tout à droite
    enemyX = width;
  }

  fill(0, 0, 0);
  text(score, 10, 30);

  fill(255, 0, 0);
  rect(enemyX, floorHeight - enemyHeight, enemyWidth, enemyHeight);

  fill(0, 255, 0);
  rect(playerX, floorHeight - playerHeight, playerWidth, playerHeight);

  line(0, floorHeight, width, floorHeight);
}

Pour ce qui est du point numéro 6, on va le faire ensemble, et vu que je ne peux pas connaitre votre code je vais continuer à partir de la solution juste au dessus.

C'est assez simple et pour le coup on peut faire ça sans p5 avec:

window.addEventLisntener("keypress", handleKey);

On verra ce que ça veux dire précisement dans un autre chapitre, pour le moment on retiendra que handleKey est une fonction qui se fera exécutée quand on appuiera sur une touche. C'est une fonction que nous devons créer pour dire ce qu'il se passera et on aurait pu l'appeler n'importe comment.

Faites une pause sur l'exercice et essayez ce petit script

let count = 0;

function draw() {
  // rien
}

function handleKey(event) {
  count = count + 1;
  console.log(count);

  console.log(event.key);
}

window.addEventListener("keypress", handleKey);

Vous remarquerez bien que le compteur augmente à chaque fois que l'on appuie sur une touche et que event.key est égal à la touche que l'on vient d'appuyer.

Donc en sachant tout ça, on peut revenir à ce qu'on faisait et ajouter ceci tout en bas:

function handleKey(event) {
  if (ev.key == "z") {
    // ce qu'il se passe quand j'appuie sur la touche "z"
  }
}

window.addEventListener("keypress", handleKey);

Ensuite je peux créer une variable, speedY, canJump et playerYOffset qui me permettent de stocker, "dans quel sens je vais et à quelle vitesse", "est-ce que je peux sauter?" et "la distance du joueur par rapport au sol"

et arriver à ceci

const playerWidth = 50;
const playerHeight = 50;
const enemyWidth = 50;
const enemyHeight = 50;

let score = 0;
let speedY = 0;
let canJump = true;
let playerX = 150;
let playerYOffset = 0;
let enemyX;

function setup() {
  createCanvas(windowWidth, windowHeight);

  enemyX = width - 150;
}

function draw() {
  background(255, 255, 255);

  const floorHeight = (height * 4) / 5;

  enemyX = enemyX - 10;

  if (enemyX < -enemyWidth) {
    score = score + 10;
    enemyX = width;
  }

  // si je ne peux pas sauter
  // c'est que je suis dans les airs
  if (canJump == false) {
    // donc je met à jour ma position en Y
    playerYOffset = playerYOffset + speedY;

    // et la "gravité" tire un peu ma vitesse vers le bas
    speedY = speedY - 2;
  }

  // si je touche le sol ou plus bas
  if (playerYOffset <= 0) {
    // alors je peux à nouveau sauter
    canJump = true;

    // et je me réealigne avec le sol
    // si jamais j'était parti dedans
    playerYOffset = 0;
  }

  // si le joueur est suffisament bas pour se faire toucher
  // (les deux lignes de tirets horizontaux)
  if (playerYOffset < playerHeight) {
    if (
      (enemyX > playerX && enemyX < playerX + playerWidth) ||
      (enemyX + enemyWidth > playerX &&
        enemyX + enemyWidth < playerX + playerWidth)
    ) {
      score = score - 10;
      enemyX = width;
    }
  }
  // Je vous l'ai mis en deux "if" ci-dessus pour faciliter la lecture
  // normalement j'utiliserais un &&

  fill(0, 0, 0);
  text(score, 10, 30);

  fill(255, 0, 0);
  rect(enemyX, floorHeight - enemyHeight, enemyWidth, enemyHeight);

  fill(0, 255, 0);
  // on ajoute playerYOffset dans le calcul de la positon en Y
  rect(
    playerX,
    floorHeight - playerHeight - playerYOffset,
    playerWidth,
    playerHeight,
  );

  line(0, floorHeight, width, floorHeight);
}

function handleKey(event) {
  if (event.key == "z" && canJump) {
    canJump = false;
    speedY = 40;
  }
}

window.addEventListener("keypress", handleKey);

Nope !

Il existe un dernier opérateur à voir avec les booléens, c'est l'opérateur de négation !.

Il sert à inverser la valeur d'un booléen, !true == false et !false == true.

Donc, plutôt que d'écrire:

if (canJump == false) {
}

On aurait pu écrire:

if (!canJump) {
}

Ce qui aurait été vrai si canJump était faux, autrement dit !canJump veux dire "si je NE peux PAS sauter".

Conclusion

voila voila, oui je sais ça manque un poil de décoration, mais on a refait le jeu du dinosaure de google chrome en ~70 lignes. Sympa comme chapitre nan?