Willy rovelly alsacien, inventeur fou, IT guy, cinéfilou

Je « débute » en processing : tombe la neige

Avec des guillemets car j’ai un lointain passé de développeur et je continue de faire un peu de programmation par-ci par-là. J’ai donc les bases.

Si j’en crois wikipedia, Processing est un bon langage pour apprendre à coder. Ce qui m’intéresse ici, c’est l’orientation artistique de ce ce langage. Car Processing simplifie les routines de graphisme et d’animation.

Un programme en processing contient deux fonctions de base :

Setup() qui n’est utilisé qu’une seule fois au lancement

Draw() qui est exécuté 60 fois par seconde et en boucle, ce qui nous sera bien utile pour faire de l’animation.

Step 1 : dessine-moi un flocon

Plutôt que de faire un hello world, je vais viser quelque chose d’un peu plus ambitieux. Je suis un peu rouillé mais je vais utiliser le concept de « classe », justement pour me remettre dans le bain.

Une classe va représenter un objet qui aura des caractéristiques et des méthodes (des fonctions associées). Je vais créer une classe Flocon (le F majuscule est important). Pour le moment, je veux juste que ses caractéristiques soient des coordonnées, donc un X et un Y.

Il n’y aura qu’une méthode associée : affiche(). Cette méthode décrira ce qui est nécessaire pour construire graphiquement un flocon en fonction de ses caractéristiques.

Il y a une méthode spéciale, c’est le constructeur. Ca va renseigner les caractéristiques, comme ça on pourra les utiliser.

Voilà le code le plus simple possible et bien commenté !

class Flocon {
    float x;  //Coordonnée X
    float y; // Coordonnée Y
    
      Flocon() {                // constructeur de flocon
      x = random(0, width);    // Donne un X au hasard entre 0 et la largeur de la fenêtre
      y = random(0, height);   // Donne un Y au hasard entre 0 et la hauteur de la fenêtre
     }
    
      void affiche()
    {
      fill(255);  // Remplit mon flocon de neige (du blanc quoi)
      ellipse(x,y,10,10);  // affiche une ellipse "ronde" de taille 10x10 aux coordonnées x et y
    }
    
   
}

void setup()
{
  size(400,400);  // taille de la fenêtre d'affichage

  Flocon monflocon = new Flocon();  // créé un nouveau Flocon et appelle-le "monflocon"
  monflocon.affiche();  // Affiche monflocon !
}

Le résultat est WAOUH :

On remarquera que j’ai utilisé random pour déterminer un X et Y aléatoirement. A chaque exécution, on se retrouve donc avec un flocon à un endroit différent.

Step 2 : dessine-moi plein de flocons

Un flocon tombe rarement tout seul, il est accompagné de plein de copains. Je vais donc créer un tableau de flocons. Je pourrai les créer à la volée sans tableau mais les mettre dedans va me permettre de les garder en mémoire et de les manipuler plus tard pour les animer.

Flocon[] mesflocons = new Flocon[200];  // créer un tableau de 200 flocons

class Flocon {
    float x;  //Coordonnée X
    float y; // Coordonnée Y
    
      Flocon() {                // constructeur de flocon
      x = random(0, width);    // Donne un X au hasard entre 0 et la largeur de la fenêtre
      y = random(0, height);   // Donne un Y au hasard entre 0 et la hauteur de la fenêtre
     }
    
      void affiche()
    {
      fill(255);  // Remplit mon flocon de neige (du blanc quoi)
      ellipse(x,y,10,10);  // affiche une ellipse "ronde" de taille 10x10 aux coordonnées x et y
    }
    
   
}

void setup()
{
  size(400,400);  // taille de la fenêtre d'affichage

  for(int i = 0;  i < mesflocons.length; i++) // On parcourt le tableau, et pour chaque position :
  {
    mesflocons[i] = new Flocon();  // Construit un flocon et stocke le dans le tableau
    mesflocons[i].affiche();  // Affiche le flocon qu'on vient de construire.
  }
  
}


Résultat :

Step 2 : la neige tombe vers le bas

On va passer à l’animation. Dans la classe, on ajoute une méthode qui va décaler vers le bas chaque flocon d’un pixel. Cela revient à faire +1 sur la coordonnée Y.

On va avoir besoin maintenant de la fonction draw, qui on le rappelle, s’exécute 60 fois par seconde.

Flocon[] mesflocons = new Flocon[200];  // créer un tableau de 200 flocons

class Flocon {
    float x;  //Coordonnée X
    float y; // Coordonnée Y
    
      Flocon() {                // constructeur de flocon
      x = random(0, width);    // Donne un X au hasard entre 0 et la largeur de la fenêtre
      y = random(0, height);   // Donne un Y au hasard entre 0 et la hauteur de la fenêtre
     }
    
      void affiche()
    {
      fill(255);  // Remplit mon flocon de neige (du blanc quoi)
      ellipse(x,y,10,10);  // affiche une ellipse "ronde" de taille 10x10 aux coordonnées x et y
    }
    
       void bouge()
    {
      y = y + 1;
    }
    
   
}

void setup()
{
  size(400,400);  // taille de la fenêtre d'affichage

  for(int i = 0;  i < mesflocons.length; i++) // On parcourt le tableau, et pour chaque position :
  {
    mesflocons[i] = new Flocon();  // Construit un flocon et stocke le dans le tableau
    // plus besoin d'afficher, on fera ça dans draw
  }
  
}

void draw()
{
  
   for(int i = 0;  i < mesflocons.length; i++) // On parcourt le tableau créé dans le setup
  {
    mesflocons[i].bouge();  // pour chaque flocon, on va le décaler vers le bas    
    mesflocons[i].affiche();  // on affiche chaque flocon
  }
}

Là on voit qu’on a un problème, ça fait plutôt de l’huile qui coule le long d’un pare-brise…

Step 3 : il est temps de pimper nos flocons

On va mettre un fond noir, pour que les flocons ressortent, c’est la fonction background. Et surtout, je ne veux pas que les flocons laissent une traînée derrière. Je dois donc reremplir de noir à chaque appel de Draw, puis afficher mes flocons.

En regardant bien au-dessus, on voit que mes flocons sont blancs à l’intérieur mais qu’ils ont un contour noir. C’est la fonction noStroke qu’il faut appeler quand on dessine le flocon.

Et puis j’aimerais que les contours soient un peu plus doux. Je vais donc dessiner mes flocons avec un dégradé du blanc à l’intérieur vers le noir à l’extérieur. Après un premier essai, je vois que ça donne un effet de flou. Je vais m’en servir pour simuler une sorte de profondeur de champ.

J’ai aussi fixé un frameRate à 12, ce qui donnera une animation à 12 images par seconde, et ce qui pourra m’aider à générer un gif du résultat.

Step 4 : Plus de réalisme, plus !

On va se dire que la mise au point de faire au lointain. Donc les flocons les plus proches seront gros et flous. Les plus éloignés seront nets et petits

Il faut donc inclure un peu de hasard avec une variable z qui complètera x et y et qui représentera la « profondeur ». On va dire que ça va être compris entre 1 et 20. Du coup je vais tricher un peu et mapper ce Z sur la taille du flocon

Z = 20, ce sera un flocon qui est près de nous, et donc il aura une taille de… Z bien sûr.

En faisant un premier essai, on constate que tous les flocons tombent au même moment. Il faut donc modifier l’animation suivant la valeur du Z. Plus les flocons seront proches, plus ils vont tomber vite. Je dois donc modifier ma méthode bouge de la classe Flocon.

Et pour encore plus de réalisme, on constate qu’une fois que tous les flocons sont tombés.. Eh ben c’est fini. Ce serait pas plus cool de renouveler ces flocons pour que la neige tombe en continu ?

Pour faire ceci, on ajoute une méthode à Flocon pour savoir si flocon a atteint le bord de l’image. Il meurt à ce moment là et donc on va regénérer un flocon avec une coordonnée en haut du cadre.

Et dernière chose, mes flocons spawnent n’importe où sur l’écran au niveau de la hauteur. J’aimerais qu’ils partent tous depuis en haut. Facile, on facile la coordonnée Y à 0. Bon, la première salve est un peu bizarre mais ensuite ça se passe bien.

Laissez un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *