Parmis les exemples intéressants de ce qu’il est possible de faire avec processing et qui a trait au jeu vidéo il y a les systèmes de particules. Ceci correspond au chapitre 4 de The Nature of Code et s’appuie sur les concepts évoqués de vecteurs et de forces évoqués au chapitre précédent. Cependant, comme celà s’appuie sur des concepts physiques très simples qui remontent à la classe de seconde en France, ceci sera notre introduction à processing car quelle meilleure façon d’introduire un framework de programmation que par un bouquet de particules ? D’abord nous verrons de quelle manière une physique rudimentaire régit le mouvement d’une particule, puis nous créerons un système de particule capable de les générer à l’envie.
- Un objet susceptible à la physique
Avant de s’attaquer directement au problème, comprenons d’abord ce qu’est une particule. Commençons par un problème plus archaïque.
https://github.com/Thibautomaton/CanonBall
Voici un premier exemple qui représente un objet lancé par un canon qui répond donc à une impulsion initiale et tombe sous l’effet de la gravité
- Dessin initial
On commence par créer son espace de jeux. Dans la fonction setup() on indique la taille de la fenêtre avec “size(600, 600)”
Dans la fonction draw(), on commence par “background(125)” même si on serait tenté d’initialiser dans la fonction setup la variable background, il faut se rappeler que l’objet mover que l’on crée est dessiné dans la fenêtre processing à chaque appel de la fonction draw, et que si on ne redéfinit pas la fonction background à chaque impression on perdra la sensation de mouvement.
pushMatrix()
fill(0)
translate(0, height-25)
angle = radians(-45)
rotate(angle)
rect(0, 0, 100, 50)
popMatrix()
Le code ci-dessus n’a que comme fonction de dessiner le rectangle qui représente le canon. Plutôt que de définir les points qui le composent “en direct”, on utilise les fonctions translate et rotate ce qui nous permet de mettre dans la fonction rect simplement les dimensions souhaitées du canon. L’orientation et la position sont déterminés par translate et rotate. Les fonctions pushMatrix() avant puis popMatrix() permettent d’encapsuler ces déplacements permettant par la suite de recréer une origine au début du document.
- Attente d’action utilisateur
Au début du fichier:
global balls
balls = []
On définit la liste “balls” comme une variable globale car elle sera amenée à être modifiée dans la fonction draw() et keypressed(), puisqu’elle contient tous les objets Ball, if faut notamment pouvoir ajouter un nouvel objet Ball à la fin de la liste lorsqu’on appuie sur la touche “s” qui indique qu’on tire avec le canon:
def keyPressed():
print(key)
if str(key)==’s’:
pushMatrix()
fill(255)
balls.append(Ball(100, height-90, random.randint(10, 80)))
force = PVector(random.uniform(1, 2), -1)
force.mult(5)
balls[-1].applyForce(force)
popMatrix()
C’est de la programmation événementielle on définit une action à appeler lorsqu’une touche est enclenchée ce qui émet le signal qui appelle la fonction keyPressed. Ici l’action requise est de créer un objet Ball et de créer une force initiale sous la forme d’un vecteur qui est passé en argument à la fonction applyForce. C’est ce qui va donner l’impulsion initiale à l’objet.
Retour à la fonction draw() :
for ball in balls:
gravity = PVector(0, 0.1)
ball.applyForce(gravity)
ball.update()
ball.display()
- Un objet soumis à des forces
On a trois fonction de la class Ball -> applyForce qu’on a déjà vu, update() et display()
Celà nous donne une idée de la représentation de l’objet Ball. Créons un nouveau fichier python. Il est un peu rapide d’arriver à cette solution sans passer par toutes les étapes du livre “The Nature of Code” mais une force implique un impact sur l’accélération de l’objet. La position de l’objet, la vitesse et l’accélération sont reliés par la formule suivante qui constitue le contenu de notre méthode update() :
self.velocity += self.acceleration
self.location += self.velocity
self.acceleration.mult(0)
De même la physique nous donne la relation entre la force et l’accélération ave F=m*a.
Donc la méthode applyForce:
def applyForce(self, force):
self.acceleration+=force.div(self.mass)
Dans l’exemple donné nous avons également l’accélération angulaire mais nous n’entrerons pas plus dans les détails de son fonctionnement si ce n’est de dire qu’elle influe sur le contenu de la fonction rotate dans la fonction display (on utilise encore pushMatrix, et popMatrix pour dessiner l’objet à la la localisation calculée).

Un dessin d’un canon emettant un objet soumis à des forces
Voilà c’est un premier exemple d’objet sur lequel on applique de la gravité et capable de répondre aux sollicitations de son environnement. Ce cas sera employé à nouveau dans les autres exemples en lien avec The Nature of Code sous la forme d’un objet Mover. Voyons déjà son utilisation dans les systèmes de particules. Les particules fonctionnent de la même manière dans le sens où elles peuvent être soumises à la gravité.
2. Créer un système de particules
Voici un exemple d’un système de particules : https://github.com/Thibautomaton/SimpleParticleSystemWithLifespan
On commence par initialiser la variable globale systems qui répertoriera les systèmes de particules (c’est à dire un objet qui a une position x et y d’où seront émises les particules).
def mousePressed():
global systems
systems.append(ParticleSystem(PVector(mouseX, mouseY)))
les systèmes de particules sont initialisés cette fois quand le signal que le bouton de la souris à été enclenché est reçu.
for ps in systems:
if not ps.isDead():
ps.addParticle()
ps.run()
else:
print(ps.age)
systems = [ps for ps in systems if not ps.isDead()]
On a une condition qui porte sur la méthode isDead d’un système, on imagine qu’il s’agit d’une fonction déterminant si la durée de vie du système est atteinte. Donc en fonctionnement normal le système ajoute une particule (addParticle) et s’update (fonction run qui regroupe les fonctions update et display vues précédemment).
Dans le cas contraire, on imprime l’âge du système.
Alors comment faire en sorte de ne parcourir que les systèmes encore vivants afin de réduire notre consommation de calcul ? La solution est cette instruction synthétique, systems = [ps for ps in systems if not ps.isDead()] qui m’a été soufflée par chatGpt et qui est la bonne manière en python de vérifier que tous les éléments répondent à une condition et les supprimer sinon.
La classe Particle ressemble beaucoup à la classe Ball vue précédemment, à ceci près que l’instruction applyForce est appelée dans la fonction __init__() (car en effet une impulsion seulement à l’apparition de la particule), ensuite on a un attribut “self.lifespan” qui est décrémenté dans la fonction update, et la fonction isDead() qui vérifie si lifespan est supérieur à 0.
La classe ParticleSystem a des attributs un petit peu différents :
class ParticleSystem(object):
def __init__(self, origin):
self.age = 1
self.particles = []
self.origin = origin.get()
self.gravity = PVector(0, 0.1)
Il y a une liste “self.particles” qui contiendra toutes les particules associées au système. Une origine qui est la localisation du système, puis deux attributs, self.gravity qui permet de calculer la gravité sur l’ensemble du système via la fonction applyForce et self.age qui fonctionne un peu comme lifespan pour déterminer la durée de vie du système.
Pour ajouter une particule à la liste on utilise addParticle:
def addParticle(self):
self.particles.append(Particle(self.origin))
Dans la fonction run() on upgrade l’âge, et on parcours la liste des particules pour update, induire la gravité et supprimer les particules expirées.
def run(self):
self.age+=1
for p in self.particles:
p.applyForce(self.gravity)
p.run()
self.particles = [p for p in self.particles if not p.isDead()]

Initialisation d’un système de particule à un endroit x et y avec durée de vie
CONCLUSION:
Voilà ce n’était pas plus compliqué que ça. Nous savons désormais créer des particules et nous pourrons les incorporer à nos futurs programmes. Avec processing on a pu s’expérimenter à la programmation événementielle avec mousePressed() qui est ni plus ni moins que l’attente d’un signal émis par l’action de l’utilisateur. On a aussi expérimenté la programmation objet. Celle-ci étant rappelée dans tous les bouquins de programmation, celà ne m’a pas semblé nécessaire d’en faire un chapitre. J’ai moi-même appris la programmation objet via le cours de C++ d’openclassroom, vous pouvez toujours vous y rendre ou cette notion est mise à jour https://openclassrooms.com/fr/courses/7137751-programmez-en-oriente-objet-avec-c
Dans le prochain article nous continuerons d’explorer les systèmes de particules pour voir comment les jeux les utilisent et si nous pouvons avoir plus d’interaction avec eux.
Je vous y retrouve pour cette exploration des systèmes de particules.
