classe particule et émetteur de particules

Pour le jeu avec le vaisseau, voir ici : présentation du jeu, j’ai eu besoin d’une classe capable de générer un effet de particules que ce soit lors des différentes explosions ou pour les moteurs de la fusée.
J’ai commencé par chercher ce que je pouvais trouver de sympa du coté des librairies open source, tel que Flint mais aprés quelques tests très simple, j’ai vite remarqué que cette librairie utilisait plus de ressources que tous les autres moteur du jeu (physique/déplacement/…), vu la puissance de cette librairie et ses possibilités c’est normal, mais ca ne pouvait pas me convenir.

Il me restait à créer mes propres classes, en gardant à l’esprit 3 pré-requis :

  • Pouvoir générer les effets visuels recherchés
  • Être performante afin de ne pas ralentir le jeu
  • Pouvoir être réutilisable dans d’autres projets


Voici le code de la classe qui représente une simple particule :

/* ----------------------------------------------------------------------------
*
*                            classe Particule v1.0
*
* Lorenzo le 12/04/2010
*
*
* AS >= 3.0
* package commun Flash et Flex
 ------------------------------------------------------------------------------- */
package lol.dessin {
	import flash.display.Shape;
	import flash.events.Event;
	import flash.filters.DropShadowFilter;
	import flash.filters.BevelFilter;
 
	import lol.base.Nb;
 
	/**
	 * @author Lorenzo
	 *
	 * Représente une particule
	 *
	 * @example
	 *
		var p:Particule = new Particule();
		p.x = 50;
		p.y = 50;
		p.ecartAlpha = Nb.aleatoire(0.005, 0.01, 3);
		p.longueur = Nb.aleatoire(0.6, 1.5, 5);
		p.ecartAngleMax = Math.PI / 10;
		p.angle = -Math.PI / 4 ;
		p.vitesse = Nb.aleatoire(0.5, 1.5, 5);
		this.addChild(p);
	 *
	 */
	public class Particule extends Shape{
		// -----------------------------------------------------------------------------------
		// DECLARATIONS
		// -----------------------------------------------------------------------------------
 
		// ----------------------------------- Paramètres utilisateurs -----------------------------------
 
		public var couleur:uint = 0xFF0000;
		public var longueur:Number = 2;
		public var angle:Number = 0;
		public var ecartAngleMax:Number = Math.PI / 4;
		public var vitesse:Number = 1;
		public var ecartAlpha:Number = 0.01;
		public var type:uint = 1;
 
		// ----------------------------------- Paramètres classe -----------------------------------
 
		// Indique si le composant est sur la scene
		protected var _surScene:Boolean = false;
 
		// le nombre total d'occurrence de cette classe
		private static  var totalOccurence:uint = 0;
 
		private var _ecartAngle:Number = 0;
 
		public static const TRAIT:uint = 1;
		public static const CERCLE:uint = 2;
		public static const RECTANGLE:uint = 4;
		public static const ELLIPSE:uint = 8;
		public static const ALEATOIRE:uint = 16;
 
		// ----------------------------------- Evenements -----------------------------------
 
		// -----------------------------------------------------------------------------------
		// CONSTRUCTEUR
		// -----------------------------------------------------------------------------------
		/**
		* Constructeur de la classe
		*/
		public function Particule() {
			super();
 
			this.addEventListener(Event.ADDED_TO_STAGE, this.evtThisAddedToStage);
			this.addEventListener(Event.REMOVED_FROM_STAGE, this.evtThisRemovedFromStage);
 
			this.name = "spriteParticule_" + totalOccurence;
			totalOccurence++;
		}
 
		// -----------------------------------------------------------------------------------
		// METHODES
		// -----------------------------------------------------------------------------------
		private function dessiner():void {
			if ( !this._surScene ) {
				return;
			}
 
			this._ecartAngle = Nb.aleatoire(-this.ecartAngleMax, this.ecartAngleMax, 7);
			this.angle += this._ecartAngle;
 
			if ( this.ecartAlpha <= 0 ) {
				this.ecartAlpha = 0.1;
			}
 
			var posX:Number = Math.cos(this.angle) * this.longueur;
			var posY:Number = -Math.sin(this.angle) * this.longueur;
 
			with (this.graphics) {
				clear();
				if( this.type & TRAIT ){
					lineStyle(1, couleur, 1);
					lineTo(posX, posY);
				}
				if( this.type & CERCLE ){
					lineStyle(0, 0, 0);
					beginFill(couleur, 1);
					drawCircle(0, 0, longueur / 2);
					endFill();
				}
				if( this.type & ELLIPSE ){
						lineStyle(0, 0, 0);
						beginFill(couleur, 1);
						drawEllipse(0, 0, longueur / 2, longueur);
						endFill();
				}
				if( this.type & RECTANGLE ){
						lineStyle(0, 0, 0);
						beginFill(couleur, 1);
						drawRect(0, 0, longueur, longueur);
						endFill();
				}
				if ( this.type & ALEATOIRE ) {
					switch (Nb.aleatoire(1, 4, 0)) {
						case 1 :
							lineStyle(0, 0, 0);
							beginFill(couleur, 1);
							drawCircle(0, 0, longueur / 2);
							endFill();
							break;
						case 2 :
							lineStyle(0, 0, 0);
							beginFill(couleur, 1);
							drawEllipse(0, 0, longueur / 2, longueur);
							endFill();
							break;
						case 3 :
							lineStyle(0, 0, 0);
							beginFill(couleur, 1);
							drawRect(0, 0, longueur, longueur);
							endFill();
							break;
						case 4 :
							lineStyle(1, couleur, 1);
							lineTo(posX, posY);
							break;
					}
				}
			}
 
			this.addEventListener(Event.ENTER_FRAME, this.evtThisEnterFrame);
		}
 
		/**
		*
		*/
		public override function toString():String {
			return  "Particule / name=" + this.name;
		}
 
		// -----------------------------------------------------------------------------------
		// EVENEMENTS
		// -----------------------------------------------------------------------------------
 
		// --------- gestion de la classe dans la liste d'affichage ---------
 
		private function evtThisAddedToStage(ev:Event):void {
			this._surScene = true;
			this.dessiner();
		}
 
		private function evtThisEnterFrame(ev:Event):void {
			this.x += Math.cos(this.angle) * vitesse;
			this.y -= Math.sin(this.angle) * vitesse;
			this.alpha -= ecartAlpha;
			if ( this.alpha <= 0 ) {
				this.parent.removeChild(this);
			}
		}
 
		private function evtThisRemovedFromStage(ev:Event):void {
			this._surScene = false;
			this.removeEventListener(Event.ENTER_FRAME, this.evtThisEnterFrame);
			this.removeEventListener(Event.ADDED_TO_STAGE, this.evtThisAddedToStage);
			this.removeEventListener(Event.REMOVED_FROM_STAGE, this.evtThisRemovedFromStage);
		}
	}
}

Ensuite nous avons la classe qui va s’occuper de créer les particules en attribuant des valeurs aléatoires pour simuler quelque chose de logique :

/* ----------------------------------------------------------------------------
*
*                            classe Particules v1.0
*
* Lorenzo le 12/04/2010
*
*
* AS >= 3.0
* package commun Flash et Flex
 ------------------------------------------------------------------------------- */
package lol.dessin {
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.utils.getTimer;
 
	import lol.base.Nb;
	import lol.dessin.Particule;
 
	/**
	 * @author Lorenzo
	 *
	 * Emetteur de Particule.
	 *
	 *
	 * @example
	 *
			var p:Particules = new Particules();
			p.angle = 0;
			p.ecartAngleMax = Math.PI / 10;
			p.longueurMin = 4;
			p.longueurMax = 7;
			p.vitesseMin = 7;
			p.vitesseMax = 10;
			p.ecartAlphaMin = 0.05;
			p.ecartAlphaMax = 0.1;
			p.type = Particule.TRAIT;
			p.actif = true;
			p.couleur = [0xFF0000, 0xFF8000, 0xFECF41];
			this.addChild(p);
	 * 
	 */
	public class Particules extends Sprite{
		// -----------------------------------------------------------------------------------
		// DECLARATIONS
		// -----------------------------------------------------------------------------------
 
		// ----------------------------------- Paramètres utilisateurs -----------------------------------
 
		private var _angle:Number = 0;
		private var _duree:Number = 0;
		private var _couleur:* = 0xFF0000;
		private var _longueurMin:Number = 0.5;
		private var _longueurMax:Number = 2;
		private var _ecartAngleMax:Number = Math.PI / 6;
		private var _vitesseMin:Number = 0.5;
		private var _vitesseMax:Number = 2;
		private var _ecartAlphaMin:Number = 0.01;
		private var _ecartAlphaMax:Number = 0.05;
		private var _type:uint = 1;
		private var _actif:Boolean = true;
		private var _debug:Boolean = false;
 
		// ----------------------------------- Paramètres classe -----------------------------------
 
		// Indique si le composant est sur la scene
		protected var _surScene:Boolean = false;
 
		// le nombre total d'occurrence de cette classe
		private static  var totalOccurence:uint = 0;
 
		//
		private  var _idCouleur:uint = 0;
 
		private var _dureeMesure:Number = 0;
		private var _dureedepart:Number = 0;
 
		// ----------------------------------- Evenements -----------------------------------
 
		// -----------------------------------------------------------------------------------
		// CONSTRUCTEUR
		// -----------------------------------------------------------------------------------
		/**
		* Constructeur de la classe
		*/
		public function Particules() {
			super();
 
			this.addEventListener(Event.ADDED_TO_STAGE, this.evtThisAddedToStage);
			this.addEventListener(Event.REMOVED_FROM_STAGE, this.evtThisRemovedFromStage);
 
			this.name = "spriteParticules_" + totalOccurence;
			totalOccurence++;
		}
 
		// -----------------------------------------------------------------------------------
		// METHODES
		// -----------------------------------------------------------------------------------
		private function dessiner():void {
			if ( !this._surScene ) {
				return;
			}
			if ( this._debug ) {
				graphics.clear();
				graphics.lineStyle(0, 0, 1, true, "none");
				graphics.moveTo(-3, 0);
				graphics.lineTo(3, 0);
				graphics.moveTo(0, -3);
				graphics.lineTo(0, 3);
			}
		}
 
		/**
		*
		*/
		public override function toString():String {
			return  "Particules / name=" + this.name;
		}
 
		/**
		* Lancer l'éjection des particules
		*/
		public function lancer():void {
			this._actif = true;
			this._dureedepart = getTimer();
			this.addEventListener(Event.ENTER_FRAME, this.evtThisEnterFrame);
		}
 
		/**
		* Mettre en pause l'éjection des particules
		*/
		public function pause():void {
			this._actif = false;
			this.removeEventListener(Event.ENTER_FRAME, this.evtThisEnterFrame);
		}
 
		/**
		* Attention, dés que l'occurence est marqué pour destruction, elle ne doit plus être utilisé !
		*/
		public function detruire():void {
			this.pause();
			this.removeEventListener(Event.ADDED_TO_STAGE, this.evtThisAddedToStage);
			this.removeEventListener(Event.REMOVED_FROM_STAGE, this.evtThisRemovedFromStage);
			while (this.numChildren) {
				this.removeChildAt(0);
			}
		}
 
		// -----------------------------------------------------------------------------------
		// EVENEMENTS
		// -----------------------------------------------------------------------------------
 
		// --------- gestion de la classe dans la liste d'affichage ---------
 
		private function evtThisAddedToStage(ev:Event):void {
			this._surScene = true;
			this.dessiner();
		}
 
		private function evtThisRemovedFromStage(ev:Event):void {
			this._surScene = false;
			this.graphics.clear();
			this.removeEventListener(Event.ENTER_FRAME, this.evtThisEnterFrame);
			this.removeEventListener(Event.ADDED_TO_STAGE, this.evtThisAddedToStage);
			this.removeEventListener(Event.REMOVED_FROM_STAGE, this.evtThisRemovedFromStage);
		}
 
		// --------- moteur ---------
 
		private function evtThisEnterFrame(ev:Event):void {
			//if ( !this._actif ) return;
			if ( this._duree && (getTimer() - this._dureedepart) >= this._duree ) {
				this.pause();
				return;
			}
			var p:Particule = new Particule();
			if( this._couleur is Array ){
				p.couleur = this._couleur[(_idCouleur % this._couleur.length)];
				_idCouleur++;
			}else{
				p.couleur = this._couleur;
			}
			p.ecartAlpha = Nb.aleatoire(this._ecartAlphaMin, this._ecartAlphaMax, 5);
			p.longueur = Nb.aleatoire(this._longueurMin, this._longueurMax, 5);
			p.ecartAngleMax = this._ecartAngleMax;
			p.angle = this._angle;
			p.vitesse = Nb.aleatoire(this._vitesseMin, this._vitesseMax, 5);
			p.type = this._type;
			this.addChild(p);
		}
 
		// -----------------------------------------------------------------------------------
		// GET/SET
		// -----------------------------------------------------------------------------------
 
		public function get angle():Number { return _angle; }
		public function set angle(val:Number):void {
			_angle = val
		}
		public function get duree():uint { return _duree; }
		public function set duree(val:uint):void {
			_duree = val
		}
		public function get couleur():* { return _couleur; }
		public function set couleur(val:*):void {
			_couleur = val
		}
		public function get longueurMin():Number { return _longueurMin; }
		public function set longueurMin(val:Number):void {
			_longueurMin = val
		}
		public function get longueurMax():Number { return _longueurMax; }
		public function set longueurMax(val:Number):void {
			_longueurMax = val
		}
		public function get ecartAngleMax():Number { return _ecartAngleMax; }
		public function set ecartAngleMax(val:Number):void {
			_ecartAngleMax = val
		}
		public function get vitesseMin():Number { return _vitesseMin; }
		public function set vitesseMin(val:Number):void {
			_vitesseMin = val
		}
		public function get vitesseMax():Number { return _vitesseMax; }
		public function set vitesseMax(val:Number):void {
			_vitesseMax = val
		}
		public function get ecartAlphaMin():Number { return _ecartAlphaMin; }
		public function set ecartAlphaMin(val:Number):void {
			_ecartAlphaMin = val
		}
		public function get ecartAlphaMax():Number { return _ecartAlphaMax; }
		public function set ecartAlphaMax(val:Number):void {
			_ecartAlphaMax = val
		}
		public function get type():uint { return _type; }
		public function set type(val:uint):void {
			_type = val
		}
		public function get actif():Boolean { return _actif; }
 
		public function get debug():Boolean { return _debug; }
		public function set debug(val:Boolean):void {
			_debug = val
		}
 
	}
}

Voici le SWF de test avec des réglettes afin de pouvoir faire varier les différentes propriétés :

En faisant quelques tests, j’ai remarqué qu’il était simple de générer des effets visuels tel que la pluie, neige, fumée, le but 1er de ces classes à donc été largement atteint :)


c’est aprés avoir fait les classes Particule, que j’ai eu l’idée de faire les classes Variateur afin de pouvoir générer des effets visuels sympa en combinant les 2, je posterai mes tests dans quelques jours.

Laisser un commentaire

*