Forums Développement Multimédia

Les formations Mediabox
Les formations Mediabox

Fiche pratique : effets à base de particules

Compatible ActionScript 3. Cliquer pour en savoir plus sur les compatibilités.Par Monsieur Spi, le 17 mai 2013

Bien utile dans de nombreux jeux, la gestion de particule permet de faire énormément de choses allant des effets tels que la fumée, les feux d'artifices, les explosions ou l'écoulement de l'eau, à l'étude de comportements de masses et la gestion de foules. C'est pourquoi il m'a semblé utile de rédiger une petite fiche de prise en main avec quelques exemples concrets. Je n'ai pas cherché à optimiser les calculs et le rendu pour le moment, on verra ça un peu plus tard, l'objectif est déjà de se faire la main sur quelques effets basiques.

Etape 1 - Générateur

La base de tout c'est le générateur, celui qui va émettre les particules, notez qu'il n'est pas obligatoire d'avoir un générateur, vous pouvez tout à fait déterminer un nombre fini de particules que vous placez directement sur votre scène.

Ouvrez un nouveau document Flash AS3 et dessinez une première particule que vous transformez en MovieClip et appelez “Particule”. Cochez également la case “Exporter pour Actionscript”, supprimez les informations situées dans le champ “Classe de base” et remplacez celles qui se trouvent dans le champ “Classe” par “Particule”. De cette manière vous liez l'objet Particule à la classe “Particule” que nous n'avons pas encore écrit mais ça va venir.

Revenez à la racine de votre projet et sur la Keyframe vide qui s'y trouve tapez le code suivant :

var W:int = stage.stageWidth;
var H:int = stage.stageHeight;
var stock:Array = [];
var i:Particule;
 
stage.addEventListener(Event.ENTER_FRAME, main); 
 
function main(e:Event):void{
	i = new Particule(W*.5, H*.5);
	stock.push(i);
	addChild(i);
	while(stock.length>40){
		i = stock.shift();
		removeChild(i);
		i.kill();
		i = null;
	}
}

“W” et “H” nous renvoient les dimensions de la scène.
“stock” est un tableau où on stocke les particules émises.
“i” est un pointeur utilisé dans les boucles.

L'écouteur lance la fonction “main” à la cadence du projet.

La fonction “main” crée une nouvelle particule à chaque frame (nous verrons comment gérer les paramètres plus tard), l'ajoute au stock et vérifie la taille du stock. Si il y a plus de 40 particules dans le stock, on retire la première et on la détruit, cela laisse la place pour la nouvelle.

Voilà pour le fonctionnement de base du générateur, à présent intéressons-nous aux particules.

Chaque particule est un nouvel objet que l'on vient ajouter à notre scène. Afin de bien séparer les choses nous allons utiliser un peu de POO pour définir un moule qui va nous permettre de reproduire autant de particules qu'on le souhaite sur le même modèle, c'est pour cette raison que nous avons exporté le clip de la particule pour une utilisation avec Actionscript et que nous l'avons lié à une classe “Particule” qu'il nous reste encore à écrire.

Pour ceux qui ne sont pas habitués à la POO, allez faire un tour sur le tutoriel de Nataly ici : http://forums.mediabox.fr/wiki/tutoriaux/flashplatform/programmation/poo_bases/classesqqc .

Je vous recommande notamment de lire la différence qu'il y a entre “classe de base” et “classe liée”.

Il y a deux écoles selon vos besoins, soit c'est le générateur qui influe sur les particules, soit les particules sont autonomes, cela dépend de votre projet, pour cet exemple où l'optimisation n'est pas la préocupation essentielle, je choisi de rendre les particules autonomes le plus possible. Ces particules sont donc autonomes, c'est à dire qu'une fois créés elles se débrouillent toute seules pour faire ce qu'elle ont à faire, mais pour cela elles vont avoir besoin de quelques informations, par exemple :

var p:Particule = new Particule(W*.5, H*.5);

Je crée une nouvelle particule et je lui donne une position de départ.

Passons à la classe en elle même, dans le dossier où vous avez créé votre projet (FLA), créez un nouveau document Texte que vous renommez “Particule.as”, éditez-le et écrivez :

package {
 
	import flash.events.Event;
	import flash.display.Sprite;
 
	public class Particule extends Sprite { 
 
		private var _X:Number; 
		private var _Y:Number;
 
		public function Particule (X:Number, Y:Number) {
			x = X;
			y = Y;
			_X = Math.random()*20-10;
			_Y = Math.random()*20-10;
			addEventListener(Event.ENTER_FRAME, update);
		}
 
		private function update(e:Event):void{
			x += _X;
			y += _Y;
		}
 
		public function kill():void {
			removeEventListener(Event.ENTER_FRAME, update);
		}
	}
}

Bien, nous voici donc avec une classe “Particule”, nous avons vus lors de la création du générateur que nous avons besoin de divers paramètres pour donner vie à nos particules, voyons comment traduire ça :

private var _X:Number = 0; 
private var _Y:Number = 0;

Je commence par deux variables privées (accessibles uniquement au sein de la classe), “_X” et “_Y” qui représentent le déplacement sur chaque axe à chaque frame, on appellera cela le “pas” (comme celui d'un marcheur qui à chaque pas se déplace dans une direction).

public function Particule (X:Number, Y:Number) {
	x = X;
	y = Y;
	_X = Math.random()*20-10;
	_Y = Math.random()*20-10;
	addEventListener(Event.ENTER_FRAME, update);
}

Voici le constructeur de la particule, il est appelé une seule fois à la création de l'occurrence que vous souhaitez placer sur la scène. Il accepte deux paramètres qui représentent la position de départ de la particule, on positionne donc la particule en fonction de ces deux paramètres. Puis on tire un pas aléatoire sur chaque axe. Enfin on ajoute un écouteur pour mettre à jour la particule à chaque frame.

private function update(e:Event):void{
	x += _X;
	y += _Y;
}

A chaque frame la particule est mise à jour, ici on souhaite juste la déplacer donc on met à jour sa position en fonction du pas sur chaque axe.

public function kill():void {
	removeEventListener(Event.ENTER_FRAME, update);
}

Lorsque l'on veut détruire une particule, on retire l'écouteur afin de stopper son fonctionnement. Cette méthode est publique puisqu'il faut pouvoir y accéder depuis le générateur, c'est lui qui décide quand une particule doit disparaître. Notez que la particule pourrait se supprimer d'elle même selon certaines conditions, ce qui la rendrait encore plus autonome, mais c'est plus simple de commencer comme ça.

Voilà nous avons terminé notre base de générateur de particules, testez votre projet, si tout s'est bien passé vous devriez voir ceci :

L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.

Etape 2 - Starfield

L'effet le plus simple est celui du starfield, nous avons presque tout avec ce que nous avons fait dans le générateur, mais on va modifier sensiblement le fonctionnement pour vous montrer une autre manière de gérer tout ça.

Deux choses vont changer, on va ajouter un effet de traînée lumineuse pour renforcer l'effet de vitesse et limiter dès le départ le nombre de particules émises, puis les recycler. Le générateur n'interviendra donc qu'une seule fois pour générer les particules utiles, puis chaque particule se débrouillera cette fois toute seule sans intervention extérieure.

On commence par modifier le générateur et ajouter l'effet de traînée lumineuse.

var W:int = stage.stageWidth;
var H:int = stage.stageHeight;
var i:int;
 
var conteneur:Sprite = 		new Sprite();
var texture = 			new BitmapData(W,H,true,0x00000000);
var ecran = 			new Bitmap(texture);
var flou:BlurFilter = 		new BlurFilter(4,4);
var couleur:ColorTransform = 	new ColorTransform(0.999,0.95,0.9,1);
var origine:Point =		new Point(0,0);
 
init();
 
function init():void{
	for (i=0; i<100; i++){
		var p:Particule = new Particule(W*.5, H*.5);
		conteneur.addChild(p);
	}
	stage.addEventListener(Event.ENTER_FRAME, main); 
	addChild(ecran)
}
 
function main(e:Event):void		{
	texture.applyFilter(texture,texture.rect,origine,flou);
	texture.colorTransform(texture.rect, couleur);
	texture.draw(conteneur);
};

Comme vous pouvez le constater, le générateur crée 100 particules à l'aide d'une boucle, chaque particule est placée au sein d'un conteneur qu'on affiche pas (vous allez comprendre pourquoi).

Pour gérer l'effet de traînée lumineuse, on se sert d'un bitmapData (texture), d'un bitmap (ecran), d'un effet (flou), et d'une transformation de couleur (couleur). Le bitmap est affiché comme vous pouvez le voir dans la fonction “init()”, c'est lui qui permet d'afficher le résultat des effets que nous ajoutons. L'objectif est d'appliquer l'effet sur l'ensemble de la texture, et cette texture est tout simplement un dessin de ce que contient le conteneur, ainsi pas besoin d'appliquer un effet par particule, on l'applique sur l'intégralité du conteneur.

Du côté de la particule ça change aussi, voyons le code, éditez la classe Particule de cette manière :

package {
 
	import flash.events.Event;
	import flash.display.Sprite;
 
	public class Particule extends Sprite { 
 
		private var _X:int;
		private var _Y:int;
		private var _V:Number;
		private var _A:Number;
		private var _D:Number;
		private var _O:Number;
 
		public function Particule (X:Number, Y:Number) {
			init(X, Y);
			addEventListener(Event.ENTER_FRAME, update);
		}
 
		private function init(X:Number, Y:Number):void {
			x = _X = X;
			y = _Y = Y;
			_O = Math.random() * 6;
			_D = Math.random() * 150;
			_V = Math.random() * 0.05;
			_A = 1.025;
			alpha = 0;
		}
 
		private function update(e:Event):void {
			_D 	*= _A+(_V*0.25);
			x  	 = _X+Math.cos(_O)*_D*.5;
			y  	 = _Y+Math.sin(_O)*_D*.5;
			alpha 	 = _D/500;
			if (x > _X * 2 || x < 0 || y > _Y * 2 || y < 0) init(_X, _Y);
		}
	}
}

On retrouve à peu près la même chose que dans la première étape de cette fiche, on a un peu plus de variables (on les détaillera après) et cette fois on passe par une méthode privée “init()” pour initialiser la particule.

La fonction “init()” de la particule permet de placer cette dernière, notez que cette fois le pas est identique à la position de départ. “_O” représente l'orientation de la particule (la direction qu'elle va prendre, son angle si vous préférez), “_D” est la distance à laquelle est émise la particule, “_V” est la vitesse de la particule et “_A” est l'accélération de cette particule (identique pour toutes les particules mais vous pouvez changer cela). Toutes ces valeurs sont tirées aléatoirement.

La fonction “update()” met à jour la particule à chaque frame, elle calcule la nouvelle distance par rapport au générateur, met à jour la position sur les deux axes et diminue l'opacité de la particule en fonction de la distance. Enfin, et c'est la chose la plus importante, si la particule sort des limites imposées on la réinitialise en la replaçant à la position du générateur et en retirant aléatoirement toutes les valeurs.

Toutes les particules sont donc réutilisées et on boucle sur uniquement 100 objets.

Testez votre projet, vous devriez obtenir ceci :

L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.

Etape 3 - Fontaine

Maintenant que nous avons vu les deux principales techniques pour générer des particules (en continu ou en boucle), poussons un peu plus loin les choses. Cette fois on va tenter de créer une fontaine sur la base de ce que nous avons vus dans l'étape 1, le code est sensiblement le même.

Editez le code du générateur de la manière suivante :

var W:int = stage.stageWidth;
var H:int = stage.stageHeight;
var stock:Array = [];
var p:Particule
 
stage.addEventListener(Event.ENTER_FRAME, main); 
 
function main(e:Event):void{
	p = new Particule(W*.5, H*.5+100);
	stock.push(p);
	addChild(p);
	while(stock.length>80){
		p = stock.shift();
		removeChild(p);
		p.kill();
		p = null;
	}
}

Le point de départ des particule est modifié (+100 pixels), j'ai également modifié légèrement le look des particules pour qu'elles ressemblent à des gouttes d'eau (ça c'est juste graphique), et j'ai sorti la variable “p” de son contexte afin d'éviter de la recréer à chaque frame (optimisation), dans le même ordre d'idée (optimisation), je supprime la référence à “p” quand je détruit une particule. Pour tout le reste c'est pareil que dans l'étape 1.

Du côté de la classe des particules on a :

package {
 
	import flash.events.Event;
	import flash.display.Sprite;
 
	public class Particule extends Sprite {  
 
		private var _X:Number = 0; 
		private var _Y:Number = 0;
 
		public function Particule (X:Number, Y:Number) {
			x = X;
			y = Y;
			_X = Math.random()*20-10;
			_Y = -20;
			addEventListener(Event.ENTER_FRAME, update);
		}
 
		private function update(e:Event):void {
			x += _X;
			y += _Y;
			alpha -= 0.01;
			scaleX = scaleY *= 1.05;
			_Y += 0.8;
		}
 
		public function kill():void{
			removeEventListener(Event.ENTER_FRAME, update);
		}
	}
}

Cette fois on ne tire pas aléatoirement le pas sur Y, les particules doivent partir vers le haut.

Dans la fonction “update()”, à chaque fois que la particule est mise à jour on décrémente son opacité, on augmente sa taille et on son pas sur Y. Du coup la particule commence par monter, puis redescend lorsque le pas devient positif, sa taille et son opacité change de sorte qu'on ait l'impression que la particule s'approche de nous.

Testez votre projet, vous devriez obtenir ceci :

L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.

Etape 4 - Arrosoir

Imaginez que votre souris soit la pomme d'un arrosoir et que vous souhaitez arroser un jardin, pour cet effet on va repartir de l'effet “fontaine” que l'on va modifier pour ajouter un sol et des interactions à la souris.

Modifiez le générateur de la manière suivante :

var W:int = stage.stageWidth;
var H:int = stage.stageHeight;
var stock:Array = [];
var p:Particule;
var i:int;
var N:int = 1; 
 
stage.addEventListener(Event.ENTER_FRAME, main); 
stage.addEventListener(MouseEvent.MOUSE_DOWN, plus);
stage.addEventListener(MouseEvent.MOUSE_UP, moins);
 
function plus(e:Event)	{N = 10};
function moins(e:Event)	{N = 1};
 
function main(e:Event):void{
 
	for(i=0; i<N; i++){
		p = new Particule(mouseX, mouseY);
		stock.push(p);
		addChild(p);
	}
	while(stock.length>400)	{
		p = stock.shift();
		removeChild(p);
		p.kill();
		p = null;
	}
}

“N” représente le nombre de particules à générer à chaque frame, il change selon que vous appuyiez sur le bouton de la souris ou non. Pour le reste c'est pareil, on augmente juste la limite du nombre de particules affichées.

Les particules se placent à présent à la position de la souris, se dispersent comme nous l'avons fait dans le tout premier exemple, cette fois on ne va pas jouer sur l'alpha mais juste sur l'échelle et la gravité.

Modifiez la classe Particule de cette manière :

package {
 
	import flash.events.Event;
	import flash.display.Sprite;
 
	public class Particule extends Sprite { 
 
		private var _X:Number = 0; 
		private var _Y:Number = 0;
 
		public function Particule (X:Number, Y:Number) {
			x = X;
			y = Y;	
			_X = Math.random()*20-10;
			_Y = Math.random()*20-10;
			addEventListener(Event.ENTER_FRAME, update);
		}
 
		private function update(e:Event):void{
			x += _X;
			y += _Y;
			scaleX = scaleY *= 0.95;
			_Y++;
			if(y>350){
				y = 350;
				_Y = -_Y*.5;
			}
		}
 
		public function kill():void{
			removeEventListener(Event.ENTER_FRAME, update);
		}
	}
}

Pas grand chose ne change si ce n'est que j'ai retiré la modification de l'opacité dont on ne se sert pas, et j'ai ajouté un sol virtuel positionné à 350 pixels du haut de la scène, si la particule dépasse cette position elle s'y recale et inverse temporairement le pas sur l'axe Y, et c'est tout ;-)

Voici ce que vous devriez obtenir :

L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.

Notez que si vous placez votre souris sous le sol et appuyez dessus vous obtiendrez un effet que j'appelle “plaque d'égout”, l'eau semble jaillir du sol en étant bloquée par une plaque, c'est normal puisque les particules sont générées sous le niveau du sol, elles se calent donc dessus et ne peuvent pas tomber plus bas.

Etape 5 - Fumée

Les particules c'est pratique pour faire de l'eau, mais aussi de la fumée, voyons ça avec un petit test rapide sur la même base que les précédents.

Tout d'abord on va modifier un peu le graphisme de la particule, une forme de petit nuage blanc tout simple fera l'affaire, attention cependant si vous utilisez des formes vectorielles, plus la forme est complexe, plus elle est lourde à calculer pour le programme.

On commence par modifier le générateur de la manière suivante :

var W:int = stage.stageWidth;
var H:int = stage.stageHeight;
var stock:Array = [];
var p:Particule;
var i:int;
var N:int = 3; 
 
stage.addEventListener(Event.ENTER_FRAME, main); 
 
function main(e:Event):void{
	for(i=0; i<N; i++){
		p = new Particule(mouseX, mouseY);
		stock.push(p);
		addChild(p);
	}
	while(stock.length>300)	{
		p = stock.shift();
		removeChild(p);
		p.kill();
		p = null;
	}
}

La seule chose qui change c'est que je génère 3 particules à chaque frame et pas une seule.

On modifie ensuite la classe Particule de la manière suivante :

package {
 
	import flash.events.Event;
	import flash.display.Sprite
 
 
	public class Particule extends Sprite { 
 
		private var _X:Number = 0; 
		private var _Y:Number = 0;
 
		public function Particule (X:Number, Y:Number) {
			x = X;
			y = Y;
			scaleX = scaleY = Math.random()*0.3-0.1;
			alpha = 0.4;
			_X = Math.random() * 0.6 - 0.3;
			addEventListener(Event.ENTER_FRAME, update);
		}
 
		private function update(e:Event):void{
			x += _X;
			y += _Y;
			scaleX = scaleY *= 1.03;
			alpha -= 0.00005;
			_Y += -0.05;
		}
 
		public function kill():void{
			removeEventListener(Event.ENTER_FRAME, update);
		}
	}
}

La seule chose qui change c'est l'ajout des deux paramètres dans le constructeur, ils permettent de gérer l'opacité et la taille de la Particule au moment de sa création.

Voici ce que vous devriez obtenir :

L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.

Etape 6 - Formes

Voici un effet simple que l'on peut considérer comme “pyrotechnique”, il s'agit de répandre des particules sur une forme, un peu comme une mèche de dynamite qui brûle.

Commencez par dessiner la forme que vous souhaitez sur votre scène, peu importe qu'il s'agisse d'une forme vectorielle, d'un MovieClip, d'un Sprite ou d'un Bitmap, peu importe en fait de ce que vous voulez dessiner tant que vous ne mettez pas de noir dedans, le noir sera notre couleur de référence.

On modifie le générateur de la manière suivante :

var W:int = stage.stageWidth;
var H:int = stage.stageHeight;
var stock:Array = [];
var p:Particule;
var X:int = 0;
var Y:int = 0;
 
var texture:BitmapData = new BitmapData(W, H,false,0); 
var ecran:Bitmap = new Bitmap(texture); 
texture.draw(this); 
addChild(ecran); 
 
addEventListener(Event.ENTER_FRAME, main); 
 
function main(e:Event):void{
	X += 4; 
	if(X>W) X = 0; 
	for(Y=0; Y<H; Y++)	{
		if(texture.getPixel(X,Y)>0) {
			p = new Particule(X,Y);
			stock.push(p);
			addChild(p); 
		}
	}
	while(stock.length>400)	{
		p = stock.shift();
		removeChild(p);
		p.kill();
		p = null;
	}
}

C'est un mix des choses que nous avons déjà vues.

Tout d'abord on ajoute deux nouvelles variables X et Y, elles serviront à placer les particules sur la forme. Puis on trace dans un nouveau bitmap (ecran) ce qui se trouve sur la scène (c'est pour ça que peu importe ce que vous tracez sur la scène) et on l'affiche par dessus tout le reste, on a déjà utilisé cette manipulation avec le starfield.

La fonction “main” se divise en deux parties, la première va faire avancer X, il s'agit d'une position qui commence à zéro, donc le bord gauche de la scène, et avance jusqu'à l'autre extrémité par pas de quatre pixels puis reviens au début. Puis on fait une petit boucle sur toute la hauteur de la scène et on regarde la couleur des pixels de notre bitmap (ecran). Si on tombe sur autre chose qu'un pixel noir, on ajoute une nouvelle particule, que l'on ajoute également au stock.

La seconde partie de cette fonction est quelque chose que nous utilisons depuis le début, si le stock dépasse 400 particules on détruit la première et on la retire du stock. On modifie ensuite la classe Particule de la manière suivante :

package {
	import flash.display.MovieClip;
	import flash.events.Event;
	public class Particule extends MovieClip{
		private var _X:Number;
		private var _Y:Number;
		public function Particule(X:Number, Y:Number){
			x = X;
			y = Y;
			_X = Math.random() * 10 - 5;
			_Y = Math.random() * 10 - 5;
			addEventListener(Event.ENTER_FRAME, update);
		}
		private function update(e:Event):void{
			x +=  _X;
			y +=  _Y;
			scaleX = scaleY *=  0.8;
		}
		public function kill(){
			removeEventListener(Event.ENTER_FRAME, update);
		}
	}
}

Là aussi rien de bien nouveau, on sélectionne simplement une direction aléatoire sur chaque axe, la particule apparaît donc sur un pixel blanc puis se déplace aléatoirement tout en réduisant son échelle, ce qui nous donne une étincelle.

Voici ce que vous devriez obtenir :

L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.

Etape 7 - Aller plus lion avec Flint

Nous venons de voir quelques effets simples en guise de mise en bouche, à présent sachez que certains développeurs ont été bien plus loin pour vous simplifier la vie. C'est le cas de Richard Lord qui a mis sur pied une petite librairie sympa pour vous aider à faire facilement tout un tas d'effets à base de particules, il s'agit de Flint.

Allez faire un petit tour sur son site pour en savoir plus : http://flintparticles.org/

Pour les non anglophones (ou anglophobes), je vais reprendre son tutorial de prise en main, mais on va aller un peu plus vite que lui puisque vous avez déjà les bases et le modifier légèrement pour retrouver nos petits.

Commencez par aller récupérer la librairie ici : http://flintparticles.org/source-code

Créez un nouveau projet et placez le dossier “org” de la librairie à la racine du dossier contenant votre projet, c'est lui qui contient toutes les classes utiles que nous allons utiliser (utilisez le “classPath” si vous voulez structurer votre projet plus proprement).

Bien que vous n'ayez pas besoin de créer un graphismes particulier (la librairie peut s'en charger), nous allons quand même créer notre propre particule, histoire que vous vous y retrouviez. Créez donc un nouveau MovieClip que vous nommez “Particule” et que vous exportez pour AS (cette fois vous laissez les champs de classe tels quels).

Ok, à présent collez ce code dans la fenêtre d'actions de votre projet :

import org.flintparticles.common.counters.*;
import org.flintparticles.common.initializers.*;
import org.flintparticles.twoD.actions.*;
import org.flintparticles.twoD.emitters.Emitter2D;
import org.flintparticles.twoD.initializers.*;
import org.flintparticles.twoD.renderers.*;
import org.flintparticles.twoD.zones.*;
 
var W:int = stage.stageWidth;
var H:int = stage.stageHeight;
 
var rendu:DisplayObjectRenderer = new DisplayObjectRenderer();
var generateur:Emitter2D = new Emitter2D();
 
generateur.counter = new Steady(100);
generateur.addInitializer(new ImageClass(Particule));
generateur.addInitializer(new Position(new LineZone(new Point(-5,-5), new Point(W+5,-5))));
generateur.addInitializer(new Velocity(new PointZone(new Point(0,65))));
generateur.addInitializer(new ScaleImageInit(0.75,2));
generateur.addAction(new Move());
generateur.addAction(new DeathZone(new RectangleZone(-10,-10,W+10,H+10),true));
generateur.addAction(new RandomDrift(15,15));
 
addChild(rendu);
rendu.addEmitter(generateur);
generateur.start();
generateur.runAhead(10);

C'est assez dense, mais pas très long ni difficile à comprendre, on va s'étudier ça rapidement.

import org.flintparticles.common.counters.*;
import org.flintparticles.common.initializers.*;
import org.flintparticles.twoD.actions.*;
import org.flintparticles.twoD.emitters.Emitter2D;
import org.flintparticles.twoD.initializers.*;
import org.flintparticles.twoD.renderers.*;
import org.flintparticles.twoD.zones.*;

Les imports utiles, c'est à dire toutes les classes que nous allons utiliser pour notre effet. Je vous fais grâce des descriptions précises (il y a une doc fournie avec, ce n'est pas pour les petits oiseaux….), en gros on va avoir besoin d'un compteur pour déterminer le nombre de particules générées et de quelle manière, d'un objet qui initialise les particules, d'actions propres aux particules, d'un générateur, d'un rendu et de zones dans lesquelles restreindre les particules ou les générer.

var W:int = stage.stageWidth;
var H:int = stage.stageHeight;

Un grand classique, la largeur et hauteur de la scène.

var rendu:DisplayObjectRenderer = new DisplayObjectRenderer();
var generateur:Emitter2D = new Emitter2D();

On crée l'objet de rendu, c'est un DisplayObject puisque sont rôle est d'afficher les particules. Et un générateur dont le rôle est d'émettre les particules. Vous noterez ici que l'on a le choix entre 2D et 3D.

Bien, on a tout ce qu'il nous faut, à présent il faut configurer le tout pour générer l'effet souhaité, en l'occurrence ici de la neige qui tombe.

generateur.counter = new Steady(100);

On commence par préciser le nombre de particules a générer, la fréquence est calée sur les secondes, c'est à dire que là on demande au programme de générer 100 particules par seconde. Bien sur il existe différents types de compteurs possibles ( http://flintparticles.org/docs/2d/org/flintparticles/common/counters/package-detail.html ) pour faire un peu ce que l'on veut, à vous de les tester pour trouver celui qui correspond à vos besoins.

generateur.addInitializer(new ImageClass(Particule));

On indique au générateur l'image de référence que l'on souhaite utiliser pour les particules. Ici j'ai un peu modifié le tutorial de départ pour vous permettre de faire facilement vos propres particules, mais la librairie est équipée de classes qui vous permettent de dessiner facilement des primitives simples (cercles, rectangles, ….) via le code.

generateur.addInitializer(new Position(new LineZone(new Point(-5,-5), new Point(W+5,-5))));

La zone où les particules sont générées, ici entre -5 et -5 sur l'axe Y (donc -5 pixels), et entre la largeur de la scène +5 et -5 pour l'axe X. Les particules seront donc générées sur une ligne qui se trouve au dessus de la scène et aléatoirement sur la largeur de la scène.

generateur.addInitializer(new Velocity(new PointZone(new Point(0,65))));

La vitesse des particules sur les deux axes, 0 sur X et 65 sur Y.

generateur.addInitializer(new ScaleImageInit(0.75,2));

La taille initiale de chaque particule, comprise entre 0.75 et 2 (c'est une échelle, “scale”).

generateur.addAction(new Move());

La première action de chaque particule, elle doit se déplacer, ça pourrait être tout à fait autre chose, comme changer de couleur, grossir, disparaître, etc… A chaque nouvelle instruction n'hésitez pas à aller jeter un oeil à la doc fournie pour voir les possibilités qui vous sont offertes, je ne vais pas tout détailler non plus ;-)

generateur.addAction(new DeathZone(new RectangleZone(-10,-10,W+10,H+10),true));

La deuxième action détermine quand détruire les particules. Pour cela on détermine la zone au delà de laquelle les particules ne doivent plus exister ou être actives, ici un rectangle de la taille de la scène auquel on ajoute 10 pixels de chaque côté. Autrement dit si la particule sort de la zone définie, elle doit être détruite.
Vu le nom de l'action (DeathZone) on pourrait s'attendre à ce que les particules meurent quand elles sont dans la zone indiquée, plutôt que l'inverse. C'est justement le rôle du dernier paramètre, ici true, qui permet de préciser qu'on veut que les particules meurent quand elles ne sont pas dans la zone indiquée.

generateur.addAction(new RandomDrift(15,15));

Dernière action à effectuer, donner un mouvement aléatoire sur les deux axes, ici la particule “glisse” donc aléatoirement (RandomDrift) sur chaque axe avec une valeur maximale de 15 pixels pour chaque axe. Ceci à pour effet de rendre la chute des flocons de neige plus réaliste.

Voilà, c'est tout pour l'initialisation du générateur, il ne reste plus qu'à l'afficher et le lancer.

addChild(rendu);
rendu.addEmitter(generateur);
generateur.start();
generateur.runAhead(10);

On ajoute le rendu à la liste d'affichage, on ajoute le générateur au rendu, on lance le générateur et on utilise une petite astuce pour éviter de voir les particules apparaître en haut d'une scène vide, comme si on ouvrait d'un seul coup un sac de farine au dessus de la scène. Pour cela on utilise “runAhead” qui permet de dire au générateur de considérer qu'il s'est déjà passé 10 secondes, ainsi les particules remplissent déjà l'écran au moment où le tout s'affiche.

Et nous avons terminé ce petit tutorial de prise en main de Flint, rien de bien compliqué si vous avez la doc à portée de main, n'hésitez pas à la consulter pour voir l'étendue de ce que vous propose cette librairie et à aller faire un tour sur les exemples (avec code fourni) ici : http://flintparticles.org/examples

Testez votre projet, vous devriez obtenir ceci :

L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.

Conclusion

Je vais m'arrêter là en ce qui concerne les particules, la liste des effets et applications possibles est impressionnante et Richard Lord a déjà effectué un travail remarquable pour nous aider à gérer proprement et facilement un nombre considérable de choses.

Outre les différentes expérimentations que vous pourrez trouver sur le net, je vous recommande d'aller fouiller ce sujet sur le Forum de Mediabox : http://forums.mediabox.fr/topic/160784-optimisation-de-ma-fontaine-de-pixels/ Il y est question de fontaine de particules à plus d'un million de particules, une bonne source d'inspiration pour optimiser ;-)

Vous avez à présent un point de départ pour vos effets de particules, je vous encourage à optimiser tout ça si vous voulez vous en servir dans un projet plus vaste, les particules c'est génial mais cela demande aussi de faire très attention à l'optimisation car vous travaillez avec un grand nombre d'objets. Evitez les formes vectorielles lorsque cela est possible (même si pour ces exemples j'en ai utilisé), pensez à bien typer chaque objet et chaque variable, n'effectuez pas de calculs inutiles, travaillez avec des pixels lorsque cela vous est possible, utilisez des opérateurs binaires, bref sortez toute la panoplie d'astuces d'optimisations que vous connaissez pour que les effets à base de particules ne bouffent pas toutes les ressources de votre projet.

Notons également la possibilité d'utiliser Stage3D afin de confier au GPU le rendu des particules 2D (jetez un œil à ce sujet de Lilive pour en apprendre d'avantage : http://forums.mediabox.fr/topic/167970-moteur-de-rendu-de-sprites-2d-via-stage3d/ ), un gros plus pour les machines pouvant s'en servir, et également l’utilisation de Vector ( voir : http://help.adobe.com/fr_FR/FlashPlatform/reference/actionscript/3/Vector.html ) autrement dit d'un tableau spécialisé qui est largement plus optimisé que les tableaux classiques. Enfin, et vous l'aurez compris, il existe des tas d'astuces et de manipulations possible pour optimiser à mort la gestion des particules, je vous encourage à aller voir le sujet dédié à cette fiche sur le forum afin de voir les astuces et conseils postés par les autres membres.

Ha ! J'aillais oublier, pour aller un peu plus loin et voir comment on peut créer un jeu sur la base des flux de particules, avec des éléments extérieurs qui influent sur leur comportement, je vous ai rédigé un petit exercice à part sur le thème du jeu Auditorium, n'hésitez pas à aller le voir car c'est une bonne mise en application de ce que vous venez de lire ici, c'est par ici que ça se passe : http://forums.mediabox.fr/wiki/tutoriaux/flashplatform/jeux/auditorium

Bon courage à tous, et n'hésitez pas à venir nous montrer vos créations ou poster vos critiques ou questions sur le forum.

Les sources

particules_mb_cs6.zip Fichiers CS6
particules_mb_cs5.zip Fichiers CS5