Skip to content

cours08

sbalev edited this page Oct 13, 2022 · 8 revisions

Cours 8

Mouvement

Les variables et les conditionnelles nous permettent de réaliser des mouvements plus intéressants que le simple déplacement en ligne droite à vitesse constante.

On va où ?

Source Jacques Rouxel

Balle rebondissante

Dans l'exemple 4.4 nous avons appris comment faire bouger une balle sur la fenêtre à l'aide d'une variable. Lorsque la balle atteignait le bord de la fenêtre, elle disparaissait pour toujours. Heureusement les conditionnelles nous permettent de poser la question :

Est-ce que la balle a atteint le bord de la fenêtre ?

Si oui, on peut la faire rebondir (changer la direction).

On sait déjà qu'il faut déclarer une variable pour tenir à jour la position de la balle :

int x = 0;

Dans l'exemple 4.4 la balle bougeait toujours à droite :

x = x + 1;

Et si on voulait qu'elle bouge à gauche ? Ce n'est pas très difficile non plus :

x = x - 1;

Autrement dit, par fois la balle va bouger à vitesse 1, parfois à vitesse -1. La vitesse de la balle varie. Cela doit nous mettre la puce à l'oreille. Oui, nous avons besoin d'une deuxième variable pour la vitesse :

int x = 0;
int v = 1;

Maintenant pour faire bouger la balle, il nous suffit de faire

x = x + v;

et changer la direction quand la balle dépasse les bords :

if (x > width) {
  v = -1;
} else if (x < 0) {
  v = 1;
}

ou écrit de façon plus élégante :

if (x > width || x < 0) {
  v = -v;
}

Voici l'exemple complet. Nous avons ajusté les bornes pour qu'on voie la balle tout le temps.


Exemple 8.1. Une balle rebondissante

int r = 25;
int x;
int y;
int v = 1;

void setup() {
  size(200, 200);
  x = r;
  y = height / 2;
  noStroke();
  fill(127);
  ellipseMode(RADIUS);
}

void draw() {
  background(255);
  ellipse(x, y, r, r);
  x += v;
  if (x < r || x > width - r) {
    v = -v;
  }
}

Bien sûr, la balle peut bouger également sur y. Voici un autre exemple ou la balle part d'une position aléatoire dans une direction aléatoire.


Exemple 8.2. Encore une balle rebondissante

int r = 25;
float x;
float y;
float v = 2;
float alpha = random(TWO_PI);
float vx = v * cos(alpha);
float vy = v * sin(alpha);

void setup() {
  size(200, 200);
  x = random(r, width - r);
  y = random(r, height - r);
  noStroke();
  fill(127);
  ellipseMode(RADIUS);
}

void draw() {
  background(255);
  ellipse(x, y, r, r);
  x += vx;
  y += vy;
  if (x < r || x > width - r) {
    vx = -vx;
  }
  if (y < r || y > height - r) {
    vy = -vy;
  }
}

Exercice 8.1. Faire varier une couleur en utilisant la technique de la balle rebondissante. Lorsqu'une des composantes atteint les « bords » 0 et 255, on la fait partir dans la direction opposée.


La présence des conditionnelles dans notre boîte à outils de programmation nous permet de réaliser des mouvements plus complexes. Considérons une balle qui suit les bords de la fenêtre. Une façon de résoudre ce problème est de penser que la balle peut être dans quatre états possibles :

  • état 0 : mouvement de gauche à droite
  • état 1 : mouvement du haut vers le bas
  • état 2 : mouvement de droite à gauche
  • état 3 : mouvement du bas vers le haut

On peut utiliser une variable pour garder l'état courant et lorsque la balle dépasse le bord de l'état courant, passer à l'état suivant. L'exemple suivant implante cette logique.


Exemple 8.3. Une balle borderline

int r = 25;
float x = r;
float y = r;
float v = 2;
int etat = 0;

void setup() {
  size(200, 200);
  noStroke();
  fill(127);
  ellipseMode(RADIUS);
}

void draw() {
  background(255);
  ellipse(x, y, r, r);

  if (etat == 0) {
    x += v;
    if (x > width - r) {
      x = width - r;
      etat = 1;
    }
  } else if (etat == 1) {
    y += v;
    if (y > height - r) {
      y = height - r;
      etat = 2;
    }
  } else if (etat == 2) {
    x -= v;
    if (x < r) {
      x = r;
      etat = 3;
    }
  } else {
    y -= v;
    if (y < r) {
      y = r;
      etat = 0;
    }
  }
}

Exercice 8.2. Modifier l'exemple précédent pour que la balle tourne dans l'autre sens.


Gravité

La balle rebondissante nous a appris qu'un objet bouge en changeant sa position en fonction de sa vitesse :

position = position + vitesse

Autrement dit, la vitesse est le taux de variation de la position.

La gravité est la force d'attraction de la Terre. C'est elle qui fait accélérer les objets qui tombent vers le sol. Pour coder la gravité, il nous faut ajouter à notre balle le concept d'accélération (qui peut être causé par la gravité ou par d'autres forces). C'est elle qui fait augmenter ou baisser la vitesse. Autrement dit, l'accélération est le taux de variation de la vitesse (qui est le taux de variation de la position). Nous avons juste besoin d'une ligne de code supplémentaire :

vitesse = vitesse + accélération

et nous avons notre simulation de la gravité !

Exemple 8.4. Gravité

int r = 25;
float x;
float y;
float vitesse = 0;
float acceleration = 0.1;

void setup() {
  size(400, 400);
  x = width / 2;
  y = r;
  noStroke();
  fill(127);
  ellipseMode(RADIUS);
}

void draw() {
  background(255);
  ellipse(x, y, r, r);

  y += vitesse;
  vitesse += acceleration;

  if (y > height - r) {
    // la balle perd un peu de vitesse quand elle touche le sol
    vitesse *= -0.95;
  }
}

Mouvement périodique

Le mouvement d'un pendule (comme celui à la BU) est décrit par l'équation

θ(t) = θ0 cos(ωt)

  • θ est l'angle entre le fil du pendule et l'axe vertical ;
  • θ0 est l'angle initial ;
  • ω est une constante qui dépend de la longueur du fil et de la gravité.

L'exemple suivant simule un pendule en appliquant l'équation ci-dessus. À noter l'utilisation du mot-clef final permettant de définir des constantes symboliques. L'utilisation de constantes symboliques est une bonne pratique car elle rend le code plus facile à modifier. Il faut privilégier les constantes symboliques (telles que PI) aux constantes « magiques » (telles que 3.141592). Par convention, les noms des constantes symboliques sont tout en majuscules.

int(PI)

Source xkcd


Exemple 8.5 Pendule

final float THETA0 = QUARTER_PI;
final float OMEGA = 0.02;
final int L = 200;

float theta, x, y;
float t = 0;

void setup() {
 size(400, 300);
}

void draw() {
 background(255);

 theta = THETA0 * cos(OMEGA * t);
 x = width / 2 + L * sin(theta);
 y = L * cos(theta);

 stroke(0);
 line(width / 2, 0, x, y);
 noStroke();
 fill(127);
 ellipse(x, y, 50, 50);
 t++;
}

Exercice 8.3. Modifiez la longueur du fil et/ou l'angle initial. Grâce aux constantes symboliques, vous n'aurez qu'un seul changement à faire !


Un jeu débile

Nous avons suffisamment de bagage pour programmer notre premier jeu vidéo en Processing ! L'idée est de faire bouger l'antenne de Chip pour crever des ballons avec. Comme d'habitude, nous avons utilisé l'approche du développent incrémental, mais cette fois nous n'expliquerons pas les étapes intermédiaires et nous ne donnerons que le résultat final. À vous de décrypter le code et de comprendre son fonctionnement. Après tout, le métier de développeur consiste autant à écrire du code qu'à lire et comprendre le code écrit par d'autres (d'où l'importance des commentaires).

On utilise quelques fonctions pas encore vues en cours, n'hésitez pas à consulter leur documentation et à poser des questions à votre chargé de TP si les choses ne sont pas claires.


Exemple 8.6. Notre premier jeu

// Variables de Chip
float chipX, chipY;
float antenneLongueur = 10;
float antenneAngle = -HALF_PI;

// Variables du ballon
float ballonRayon = 20;
float ballonX, ballonY;
color ballonCouleur = color(random(256), random(256), random(256), 127);
float ballonVitesse = 0.5;

// Variables du jeu
int score = 0;

// Variables utilsées pour stocker des résultats intermédiaires
float antenneX, antenneY;
boolean ballonCreve;

void setup() {
  size(400, 400);

  // initialisation des variables qui dépendent de width et height
  chipX = width / 2;
  chipY = height - 100;
  ballonX = random(ballonRayon, width - ballonRayon);
  ballonY = height;
}

void draw() {
  background(255);

  // Dessiner Chip
  // le corps
  stroke(0);
  strokeWeight(1);
  fill(191);
  rect(chipX - 35, chipY, 70, 70);

  // bouton UP
  fill(65, 105, 255);
  rect(chipX - 10, chipY + 5, 20, 20);
  if (chipX - 10 < mouseX && mouseX < chipX + 10 && chipY + 5 < mouseY && mouseY < chipY + 25) {
    antenneLongueur++;
  }

  // bouton DOWN
  fill(255, 215, 0);
  rect(chipX - 10, chipY + 45, 20, 20);
  if (chipX - 10 < mouseX && mouseX < chipX + 10 && chipY + 45 < mouseY && mouseY < chipY + 65) {
    antenneLongueur--;
  }

  // bouton LEFT
  fill(50, 205, 50);
  rect(chipX - 30, chipY + 25, 20, 20);
  if (chipX - 30 < mouseX && mouseX < chipX - 10 && chipY + 25 < mouseY && mouseY < chipY + 45) {
    antenneAngle -= radians(0.5);
  }

  // bouton RIGHT
  fill(220, 20, 60);
  rect(chipX + 10, chipY + 25, 20, 20);
  if (chipX + 10 < mouseX && mouseX < chipX + 30 && chipY + 25 < mouseY && mouseY < chipY + 45) {
    antenneAngle += radians(0.5);
  }

  // les roues
  ellipseMode(CENTER);
  fill(255);
  ellipse(chipX - 20, chipY + 70, 20, 20);
  ellipse(chipX + 20, chipY + 70, 20, 20);

  // la tête
  fill(191);
  arc(chipX, chipY - 5, 70, 70, -PI, 0, CHORD);

  // la caméra
  fill(63, 0, 0);
  ellipse(chipX, chipY - 20, 20, 20);

  // l'antenne
  antenneLongueur = constrain(antenneLongueur, 10, dist(0, 0, chipX, chipY - 40));
  antenneAngle = constrain(antenneAngle, -PI, 0);
  // coordonnées du bout de lantenne
  antenneX = chipX + antenneLongueur * cos(antenneAngle);
  antenneY = chipY - 40 + antenneLongueur * sin(antenneAngle);
  strokeWeight(3);
  line(chipX, chipY - 40, antenneX, antenneY);

  // dessiner le ballon
  ballonY -= ballonVitesse;
  noStroke();
  fill(ballonCouleur);
  ellipseMode(RADIUS);
  ellipse(ballonX, ballonY, ballonRayon, ballonRayon);
  stroke(0);
  strokeWeight(1);
  line(ballonX, ballonY + ballonRayon, ballonX, ballonY + 2 * ballonRayon);

  // Chip a-t-il crevé le ballon ?
  ballonCreve = dist(ballonX, ballonY, antenneX, antenneY) < ballonRayon;
  if (ballonCreve) {
    score++;
  }
  // si le balon est crevé ou hors de portée, on'en fait un nouveau
  if (ballonCreve || ballonY < 0) {
    ballonX = random(ballonRayon, width - ballonRayon);
    ballonY = height;
    ballonCouleur = color(random(256), random(256), random(256), 127);
    // le nouveau ballon est plus rapide
    ballonVitesse += 0.025;
    // ... et plus petit
    ballonRayon -= 0.1;
  }

  // affichage
  fill(0);
  textAlign(LEFT);
  text("Score " + score, 20, 20);
  textAlign(RIGHT);
  // le jeu dure 2 min
  if (millis() < 120000) {
    text("Time " + millis() / 1000, width - 20, 20);
  } else {
    text("Game over", width - 20, 20);
    noLoop();
  }
}

Mise en pratique


Exercice 8.4. En utilisant les variables et conditionnelles, modifiez le comportement de votre dessin. Quelques idées :

  • Faites bouger des objets ou faites les changer de taille. Que se passe-t-il lorsque ces objets arrivent au bord de la fenêtre ou atteignent une certaine taille ?
  • Faites changer l'apparence des éléments lorsque vous passez la souris dessus.
  • Utilisez des boutons pour contrôler des objets.
  • Pourquoi ne pas programmer votre propre jeu vidéo qui met en place tous ces éléments ?

Vous pouvez vous inspirer des exemples vus en cours, mais surtout utilisez votre imagination pour appliquer les techniques acquises jusqu'à maintenant !


Exercices++


Exercice 8.5. Réalisez d'autres mouvements intéressants, par exemple :

  • Mouvement circulaire à vitesse angulaire constante

    circulaire

  • Mouvement en zigzag

    zigzag

  • Descente d'une pente sous l'effet de la gravité

    pente

  • etc.


Exercice 8.6. Simulez un tir de canon. Dans un premier temps, placez une cible à une distance aléatoire du canon et donnez à l'utilisateur la possibilité de régler l'angle et la vitesse du tir en utilisant les flèches du clavier. Visualisez la vitesse et l'angle par une ligne.

visée

Quand l'utilisateur est prêt et appuie sur la touche « Entrée », lancez le tir. Indication : en utilisant l'angle du tir, décomposez la vitesse initiale sur les deux axes. Vous pouvez ignorer la résistance de l'air et considérer que la vitesse horizontale reste constante. En revanche, la gravité tire la balle vers le bas.

Quand la balle touche la terre, vérifiez si la balle a touché la cible. Si c'est le cas, recommencez avec une nouvelle cible, sinon, proposez à l’utilisateur de réessayer avec la même cible.

tir


Clone this wiki locally