Forums Développement Multimédia

Les formations Mediabox
Les formations Mediabox

Chute ininterrompue de particules

Compatible ActionScript 1 et 2. Utilise des techniques dépréciées. Cliquer pour en savoir plus sur les compatibilités.Par ekameleon (Marc Alcaraz)

Salut à tous,

On va dupliquer dynamiquement des balles subissant un mouvement aléatoire (vitesse, direction), se recyclant (propriétés de position, d'alpha, d'échelle…) à chaque fois qu'elles sortent d'une certaine zône prédéfinie dans le code. Autrement dit ça simulerait une chute inintérompue de particules, dont chaqu'une d'elles se comporterait de façon aléatoire.

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

Dans le fabuleux bouquin de Penner on apprend que

position += vitesse += accélération

Dans un premier temps amusons nous avec la variable vitesse

Créons une occurence nommée “ball1”. Mettons la sur la scène, puis créons un calque “as”, sur la première image clé on a:

 
largScene = 400 
hautScene = 50 
PosIniY = - ball1._height/2 
 
ball1._alpha = 0 
 
function reinitializeBalles (mc){ 
 
   mc._xscale = mc._yscale = Math.random() * 40 + 60 
   mc._x = largScene * Math.random() 
   mc._y = PosIniY - Math.random() * hautScene 
   mc._alpha = 40 * Math.random() 
 
    this["vitesseX" + this["ball" + i].id] = Math.round((6 * Math.random() - 3) * 2) / 2 
   this["vitesseY" + this["ball" + i].id] = Math.round((3 * Math.random()) * 2) / 2 
 
} 
 
 
 nbreBalles = 50 
 
for(i = 1; i <=  nbreBalles; i++){ 
   this.ball1.duplicateMovieClip("ball" + i, i) 
   this["ball" + i].id = i 
 
   reinitializeBalles(this["ball" + i]) 
}

comprendre le code:

largScene = 400 
//variable définissant la largeur de la scene en px 
hautScene = 50 
//idem pour la hauteur de la scene 
PosIniY = - ball1._height/2 
//cette variable stocke une position initiale Y 
 
 
function reinitializeBalles (mc){ 
 
//la fonction de reinitialisation des propriétés de chaques occurence 
//qui sera appellée avec en paramètre l'occurence en question dans 
//leurs onEnterFrame respectifs 
 
   mc._xscale = mc._yscale = Math.random() * 40 + 60 
//ca varie aléatoirement entre 60 et 100 
   mc._x = largScene * Math.random() 
//ca varie aléatoirement sur toute la largeur de la scène 
   mc._y = PosIniY - Math.random() * hautScene 
   //en admettant que Math.random() * hautScene tendent 
//le plus possible vers 0, mc._y ne sera pas en dessous dans 
//la scène de PosIniY . 
//ce qui veut dire qu'à chaque réinitialisation de la position, 
//on ne verra pas la balle réapparaître brutalement tout en haut 
//de la scene 
 
   mc._alpha = 40 * Math.random() 
//ca varie aléatoirement entre 0 et 40 
 
 
    this["vitesseX" + this["ball" + i].id] = Math.round((6 * Math.random() - 3) * 2) / 2 
   this["vitesseY" + this["ball" + i].id] = Math.round((3 * Math.random()) * 2) / 2 
    //On arrondi leur valeurs à 0.5 près vu que Flash 
//dans son système de coordonnées prend en compte les 
//demi-pixels pour le non vectoriel 
    //vitesseX peut être positif comme négatif alors que 
//vitesseY doit être positif parce que on souhaite que la balle 
//descende (système de coordonnées dans Flash inversé sur l'axe des Y) 
} 
 
 
 nbreBalles = 50 
 
 //definissons la variable pour le nombre de balles sur la scène 
 
for(i = 1; i <=  nbreBalles; i++){ 
 
   this.ball1.duplicateMovieClip("ball" + i, i) 
 
   //Nous dupliquons donc 50 occurences à partir 
//de l'occurence "ball1" 
 
   this["ball" + i].id = i 
 
   //il est toujours bon de définir une variable propre à 
//chaque occurences qui servira d'identifiant dans les boucles for(){} 
 
   reinitializeBalles(this["ball" + i]) 
 
   //On appelle la fonction avec en paramètre 
//l'occurence concernée 
} 
 

Voila maintenant reste les onEnterFrame sans quoi l'animation serait fort limitée

Donc créons une deuxième image-clé pour le calque “as”, et inserrons tout ceci:

stop() 
 
   for(i = 1; i <=  nbreBalles; i++){       
      this["t" + this["ball" + i].id] = 0 
 
      this["ball" + i].onEnterFrame = function(){ 
 
      this._x += this._parent["vitesseX" + this.id] 
 
        this._y += this._parent["vitesseY" + this.id] 
 
      if(this._y >= hautScene + Math.abs(PosIniY)){ 
         reinitializeBalles (this) 
      } 
      this._parent["t" + this.id]++ 
} 
}

Analysons donc le tout:

stop() 
 
   for(i = 1; i <=  nbreBalles; i++){ 
 
 //essayez de mettre i à la place de this["ball" + i].id dans tout ce 
//script et vous verrez ce qu'il se passe... 
 
      this["t" + this["ball" + i].id] = 0 
 //une variable de temps qui va être propre à chaque 
//gestionnaire d'évènement onEnterFrame() de chaque occurence 
//et qui va s'incrémenter... 
 
      this["ball" + i].onEnterFrame = function(){ 
 
//Donc chaque occurence a son onEnterFrame propre 
 
   this._x += this._parent["vitesseX" + this.id] 
//dans la fonction reinitializeBalles(), 
//this._parent["vitesseX" + this.id] prend une certaine valeur 
//aléatoire indépendemment this._parent["vitesseY" + this.id]  ce qui explique 
//la différence de direction du déplacement de chaque occurence 
//par rapport à l'autre. Sinon chaque occurence se déplacerait de 
//45° vers la droite 
 
                this._y += this._parent["vitesseY" + this.id] 
 
      if(this._y >= hautScene + Math.abs(PosIniY)){ 
         //si l'occurence arrive tout en bas de 
//la scène de sorte qu'on ne la voit plus, on appelle la fonction avec 
//elle-même en paramètre 
         reinitializeBalles (this) 
      } 
      this._parent["t" + this.id]++ 
} 
}

Et walââ

[Edit par Lilive] Le .fla (pour CS3)

Guimetz


Pour ma part j'aurai fait autrement ce type d'exemple :) Par contre attention au code sur le wiki je pense que tu devrais faire attention à mettre des ; à la fin de toutes tes lignes de code et à être un peu plus rigoureux sur tes déclarations de variable etc. :)

Petite correction rapide de ton exemple avec des prototypes (le mieux serait de faire une classe)

Stage.align = "TL" ;
Stage.scaleMode = "noScale" ;
 
var stageWidth = 740 ;
var bottom = 400 ; 
var top = 0 ;
 
MovieClip.prototype.update = function() { 
	this._xscale = this._yscale = Math.random() * 40 + 60 ;
	this._x = stageWidth * Math.random() ;
	this._y = Math.abs(top - Math.random() * bottom) ;
  	this._alpha = 40 * Math.random() ;
        this.speedX = Math.round((2 * Math.random() - 3)) / 2 ;
	this.speedY = Math.round((3 * Math.random())) / 2 ;
	this.limit = bottom - (this._y - this._height) / 2 ;
} 
 
MovieClip.prototype.move = function () {
	this._x += this.speedX ;
	this._y += this.speedY ;
	if(this._y > this.limit) this.update() ;
}
 
// ---- Test
 
var max = 100 ; 
 
// initiatisation de l'application
 
for(var i = 0 ; i < max ; i++) {  
   var b:MovieClip = attachMovie("ball", "ball" + i, i) ;
   b.update() ;
}
 
 
// un seul onEnterFrame vaut mieux qu'un pour chaque MovieClip	
this.onEnterFrame = function(){ 
	for(var i = 0 ; i < max ; i++) {  
	   this["ball" + i].move() ;
	}
}

L'exemple au dessus fonctionne avec un petit clip dans la bibliothèque avec un nom de liaison “ball” dans les paramètres de liaison du symbole.

Maintenant voilà super rapidement la même version mais sous forme d'une classe

 
// ----o Constructor 
 
_global.Snow = function (target, ballCount, stageWidth, stageHeight, top) {
	this.target = target || _root ;
	this.stageWidth = stageWidth || 740 ;
	this.stageHeight = stageHeight || 400 ; 
	this.top = top || 0 ;
	this.ballCount = ballCount || 100 ; 
	this.init() ;
}
 
var p = Snow.prototype ;
 
// ----o Public Methods
 
p.init = function () {
	this.clear() ;
	for(var i = 0 ; i < this.ballCount ; i++) {  
	   var ball:MovieClip = this.target.attachMovie("ball", "ball" + i, i) ;
	   this.update(ball) ;
	}
}
 
p.update = function(mc) { 
	mc._xscale = mc._yscale = Math.random() * 40 + 60 ;
	mc._x = this.stageWidth * Math.random() ;
	mc._y = Math.abs(this.top - Math.random() * this.stageHeight) ;
  	mc._alpha = 40 * Math.random() ;
  	mc.speedX = Math.round((2 * Math.random() - 3)) / 2 ;
	mc.speedY = Math.round((3 * Math.random())) / 2 ;
	mc.limit = this.stageHeight - (mc._y - mc._height) / 2 ;
} 
p.move = function (mc) {
	mc._x += mc.speedX ;
	mc._y += mc.speedY ;
	if(mc._y > mc.limit) mc.update() ;
}
 
p.start = function () {
	var max = this.ballCount ;
	var owner = this ;
	this.target.onEnterFrame = function(){ 
		for(var i = 0 ; i < max ; i++) {  
		   owner.move(this["ball" + i]) ;
		}
	}
}
 
p.stop = function () {
	delete this.target.onEnterFrame ;
}
 
p.clear = function () {
	for(var prop in this.target) {  
		if (typeof(this.target[prop]) == "movieclip") {
		  this.target[prop].removeMovieClip() ;
		}
	}
}
 
// ----o Encapsulate
ASSetPropFlags(p, null, 1) ;
delete p ;
 
// -------------- TEST
 
Stage.align = "TL" ;
Stage.scaleMode = "noScale" ;
 
var mySnow = new Snow(this) ;
mySnow.start() ;

EKA :)