Forums Développement Multimédia

Les formations Mediabox
Les formations Mediabox

Classe Timer, approche (neige, scintillements…)

Compatible ActionScript 3. Cliquer pour en savoir plus sur les compatibilités.Compatible Flash CS3. Cliquer pour en savoir plus sur les compatibilités.Par nataly (Nataly), Monsieur Spi, le 02 mars 2010
  • Révision : Monsieur Spi - 7/3/2010

Dans ce tuto, sous couvert de faire neiger, pleuvoir ou scintiller, nous allons voir l'utilisation de la classe Timer dans le cadre d'appels récurrents.

Dit autrement : Comment faire quelque chose à intervalle régulier.

Je suppose que vous savez faire une interpolation de mouvement, technique dite classique (jusqu'à CS3) ou interpolation de mouvement (CS4).
Je suppose aussi que vous connaissez (juste un peu) la classe MovieClip. Que vous en connaissez les principales propriétés (x, y, scaleX…) ainsi que la méthode addChild.

Puisque c'est le cas 8-), allons y !

Principe

Après tout, neiger, ce n'est jamais qu'un flocon de neige qui tournoie en descendant, puis un autre, puis un autre…

En un peu plus sophistiqué, à peine. Il sera bon, par exemple, que tous les flocons ne tombent pas au même endroit, et qu'ils aient des tailles et des trajectoires (apparemment) différentes. On se préoccupera de ces détails dans une deuxième partie. Pour l'heure (et sans jeu de mot) il va s'agir de s'intéresser à la classe Timer qui a pour vocation, justement, de nous permettre d'invoquer une fonction (faire quelque-chose) à intervalle régulier.

La classe Timer (approche)

Comment ça se mange ?

L'objet Timer se “contente” de diffuser un événement à une fréquence donnée. Charge à nous de lui ajouter un écouteur qui écoutera les “battements”.

Pour utiliser un objet Timer, il faut :

1) Créer un nouvel objet Timer qu'on initialise avec une valeur en millisecondes pour le délai entre deux battements, et facultativement, le nombre de battements (je veux faire un truc 10 fois).

Pour les initiés ça se dit comme ça dans la doc ;)
public function Timer(delay:Number, repeatCount:int = 0)

var tmTest:Timer= new Timer(500,25) battra toutes les demies-seconde, 25 fois
var tmTest:Timer= new Timer(2000) battra toutes les deux secondes, indéfiniment

2) Lui ajouter un écouteur d'événement “battement” (TimerEvent.TIMER)

tmTest.addEventListener(TimerEvent.TIMER,FonctionDeRappel);

(La fonction de rappel requiert un paramètre de type TimerEvent)

3) Le mettre en marche

tmTest.start();

Par exemple

je veux écrire “Trop facile” dans la fenêtre sortie toutes les secondes ?

Hop ! A vous…
Ensuite promis, on fait des flocons :)

// On crée un objet de type Timer, nommé tmDemo, qui battra toute les secondes
var tmDemo:Timer=new Timer(1000);
// on lui ajoute un écouteur auquel on souscrit la fonction Demo
tmDemo.addEventListener(TimerEvent.TIMER,Demo);
// On le met en marche
tmDemo.start();
 
// la fonction de rappel
function Demo(te:TimerEvent) { 	
   trace("trop facile"); 
}

Les plus curieux iront consulter la doc et constateront avec ravissement que la Classe Timer met à notre disposition une méthode stop() qui comme son nom l'indique… ;-) ainsi qu'une méthode reset() qui réinitialise le compteur interne à l'objet (utile quand on a déterminé un nombre fini de battements).
Et aussi un événement timerComplete distribué au dernier battement

var tm:Timer = new Timer(100, 5); 
tm.addEventListener(TimerEvent.TIMER,QdBat); 
tm.addEventListener(TimerEvent.TIMER_COMPLETE,TestFin);
 
tm.start();
 
function QdBat(te:TimerEvent) {   
   trace("Passage "+tm.currentCount);
   // Ou :
   //trace(te.target.currentCount)
} 
 
function TestFin(te:TimerEvent) {
   trace("fini"); 
}

Réalisation graphique

Attaquons donc cette histoire de flocons, ensuite selon le même principe nous déclinerons avec des étoiles.

On veut un paysage sur lequel il neige.

Débarrassons nous de l'histoire du paysage d'une forme(s) vite fait pour figurer une montagne - J'ai bien dit forme(s), pas un clip, ça m'arrange pour la suite ;) Plus tard vous ferez bien comme vous voudrez -
Pensez à donner un fond sombre à la scène (plus pratique pour voir les flocons blancs), ça fera l'affaire pour l'entraînement.

Et réfléchissons à ce que c'est, visuellement, de la neige qui tombe.

Des flocons qui descendent en tournoyant, c'est :
• d'abord, un flocon fixe : un graphique
• ensuite, ce flocon fixe est animé - dans un clip - sur une trajectoire en arabesque, avec un effet de rotation (utilisez les guides de mouvement avant CS4). Il descend en tournoyant, donc ;)
• enfin, des instances de ce clip flocon-qui-descend sont ajoutées, régulièrement, sur la scène (on voit venir le Timer ;))

A vos souris pour fabriquer le graphique et le clip ! Tada !

Si besoin est, un coup de main
ici,
pour réaliser un flocon…

Un graphique

Un clip qui anime le graphique
L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.

Un zeste de code

Nous disposons maintenant d'un clip que je nomme pour l'exemple Mv_Flocon1 (oui, bon… Il se pourrait bien que, plus tard, nous prenne l'idée d'en fabriquer d'autre… autant le numéroter tout de suite ;))

Ce clip, nous nous proposons d'en ajouter de multiples instances à l'aide de la méthode addChild. Il faut donc cocher la case exporter pour actionScript dans le panneau propriétés. Et penser à mettre un stop() sur la dernière image ;)

J'en entends d'ici des qui râlent : “oui, mais ajouter des clips comme ça, à n'en plus finir, ça va bouffer les ressources”. J'en suis bien d'accord :) Nous allons donc écrire un peu de code sur la dernière image de l'animation pour supprimer l'instance de la liste d'affichage quand le flocon a fini sa course.

stop();
this.parent.removeChild(this);

Pourquoi conserver le stop ?
Parce que, ce qu'il faut garder à l'esprit, c'est que lorsqu'on supprime un objet de la liste d'affichage, on ne supprime pas pour autant l'objet lui même. C'est le ramasse miette qui s'en chargera, quand ça lui chantera. Il nous faut donc arrêter la tête de lecture, sans quoi elle boucle au début, lit jusqu'à la dernière image où elle rencontre la ligne :
this.parent.removeChild(…

Et là… Problème :-? this… c'est ce clip, qui a été supprimé de la liste d'affichage. Il n'y a donc plus de parent… et AS crie : “impossible d'accéder à la propriété ou à la méthode d'une référence d'objet nul.” L'objet nul c'est parent, la méthode c'est removeChild…
(pour les St Thomas – dont je suis – virez le stop(), posez une instance sur la scène, testez et constatez ;))

Mise en œuvre

Bon, cette fois, c'est propre, ne reste plus qu'à utiliser un Timer pour ajouter des instances de ce clip, au hasard sur la largeur de la scène, plus ou moins haut, et affligées d'un effet miroir (scaleX) une fois de temps en temps.

Je dis “au hasard”, “plus ou moins haut”, “une fois de temps en temps”, tout ça, traduit en langage code, ça revient à une seule et même chose : de façon aléatoire.

La classe Math pour obtenir une valeur aléatoire

Pour générer une valeur aléatoire, la formule ésotérique, c'est :

Math.random() * (max - min + 1) + min

Vous obtiendrez une valeur comprise entre Max et Min (bornes comprises). Attention : pas un entier.

Par exemple pour une valeur comprise entre 0 et 5 :
vous écrirez : Math.random() * (5 - 0 + 1) + 0 → Math.random() * 6 et vous obtiendrez, par exemple, 3.733022902160883…

Si vous voulez une valeur entière alors il faudra arrondir. Le plus rapide (en temps d'exécution) c'est de forcer la conversion.

var monAlea:int //un entier
monAlea=Math.random() * 6;

Le code

Cette fois vous savez tout :) en avant pour les quelques lignes qui vont bien.

 
// un objet Timer qui battra toutes les 500 millisecondes
var tmNeige:Timer=new Timer(500);
// un écouteur
tmNeige.addEventListener(TimerEvent.TIMER,f_Neige);
// en route !
tmNeige.start();
 
// la fonction de rappel
function f_Neige(te:TimerEvent) {
	// nouveau clip de type Mv_Flocon1
	var floc:Mv_Flocon1=new Mv_Flocon1();
	// de 0 à 350
	floc.x=Math.random()*351;
	// entre -60 et -40
	floc.y=Math.random()*21-60;
	// une chance sur deux d'être en symétrie
        // aa vaut 0 ou 1
	var aa:int=Math.random()*2;
        // si aa vaut 0 on "retourne" le clip 
	if(aa==0){
           floc.scaleX=-1
        }
	// ajouter à la liste d'affichage
	addChild(floc);
}

On teste, et on applaudit 8-)
… et on ne commente pas ma montagne… :mrgreen:

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

Puis l'enthousiasme retombé, on se dit que ce qui serait mieux, ce serait des flocons différents. En gros on voudrait pouvoir ajouter non pas toujours le même clip, mais un parmi tant d'autres, choisi de façon aléatoire.

On sait générer un nouvel objet MovieClip qui existe dans la bibliothèque, on vient de le faire.
On sait aussi obtenir une valeur aléatoire entre bornes. De là à générer du Mv_flocon1 ou Mv_Flocon2 “au hasard”, il n'y a qu'un pas.

// une variable qui vaut 1 ou 2
var aa:int=Math.random()*2+1
// Une chaine qui vaut Mv_Flocon1" ou Mv_Flocon2"
var floconAleatoire:String="Mv_Flocon"+aa;

getDefinitionByName

Le pas est franchi, mais on n'est guère avancés, ce n'est pas une chaine dont nous avons besoin mais une référence à un objet de classe Mv_Flocon1 ou Mv_Flocon2…
Quelle chance :) getDefinitionByName(name:String) nous renvoie une référence à l'objet de la classe spécifiée par le paramètre (c'est pas moi qui le dit c'est la doc ;)). Ainsi :

var classMv_Rond:Class = getDefinitionByName(“Mv_Rond”) as Class;

nous renverrait une référence au clip Mv_Rond de la bibliothèque.
Il ne resterait plus qu'à l'ajouter à la liste d'affichage, “normalement” :

var leRond:MovieClip= new classMv_Rond();

Et bien, on a tout ce qu'il faut pour ajouter à la liste d'affichage l'un ou l'autre des deux 'clips flocons' de la bibliothèque.
Si ce n'est pas déjà fait il est temps de fabriquer un deuxième clip qui anime un flocon qui descend en virevoltant… Et de modifier la première ligne de la fonction f_Neige :

var floc:Mv_Flocon1=new Mv_Flocon1();

function f_Neige(te:TimerEvent) {
	// un entier qui vaut 1 ou 2
	var aa:int=Math.random()*2+1;
	// une référence à l'objet de classe, aléatoire
	var classFlocon:Class=getDefinitionByName("Mv_Flocon"+aa) as Class;
	// Ajouter l'instance
	var floc:MovieClip= new classFlocon();
	// de 0 à 350
	floc.x=Math.random()*351;
	// entre -60 et -40
	floc.y=Math.random()*21-60;
	// une chance sur deux d'être en symétrie
	// aa vaut 0 ou 1
	aa=Math.random()*2;
	// si aa vaut 0 on "retourne" le clip 
	if (aa==0) {
		floc.scaleX=-1;
	}
	// ajouter à la liste d'affichage
	addChild(floc);
}

Franchement ? C'était tout bête, non ?
Comment ça, ça marche pas ? 8-o
Pire, il crie des horreurs dans la fenêtre de sortie…

ReferenceError: Error #1065: La variable Mv_Flocon2 n'est pas définie.
at global/flash.utils::getDefinitionByName()
at IlNeige_fla::MainTimeline/f_Neige()
at flash.utils::Timer/_timerDispatch()
at flash.utils::Timer/tick()

Z'auriez pas oublié de cocher la case 'exporter pour actionScript' ? ;)

Les étoiles qui scintillent en arrière plan

Pour ce qui est des étoiles qui scintillent, c'est le même cas de figure, la seule chose qui change c'est le fait de les ajouter en arrière plan.

Commençons par la construction graphique. Une étoile qui scintille, c'est :
• Une étoile tout court, un graphique
• Puis ce graphique fait l'objet d'une interpolation de mouvement, qui ne le déplace pas mais le fait grossir et apparaître (vous pouvez utiliser l'apha), puis diminuer et disparaître. Il peut même tourner sur lui même, je laisse à votre grande créativité :)

Il s'agit donc de fabriquer deux clips (au moins). Je pose que vous allez les nommer Mv_Etoile1 et Mv_Etoile2.
Bien sûr - j'ose à peine le dire ;) - vous cochez la case 'exporter pour actionScript' et vous posez un stop() suivi d'un parent.removeChild(this) sur la dernière image.

Décidons d'ajouter les étoiles sur un rythme plus lent, toutes les secondes par exemple. Le code est quasiment le même que celui qu'on vient d'écrire, à la nuance près qu'on ne va pas retourner les instances (scaleX)(sauf à ce que vous y teniez…), et qu'il s'agit de les distribuer sur une surface plus haute que les flocons, puisqu'elles ne se déplacent pas… Et puis surtout, il faut les ajouter derrière la montagne.

Lorsqu'on ajoute un clips à l'aide de addChild, on l'empile, on le met au dessus des autres. Pour ajouter un objet à un numéro d'index spécifique, il faut utiliser addChildAt(N°Index).
0 est le numéro d'index le plus bas.
Ajouter un objet à un index occupé, a pour conséquence de déplacer l'objet qui s'y trouvait vers le haut et tous ceux du dessus aussi.

La montagne, une forme, se trouve à l'index 0.
Pour ajouter des étoiles dessous il faut donc écrire addChildAt(0).

Cette fois, vous savez tout. A vous de jouer :)

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

var tmEtoile:Timer=new Timer(1000);
TmEtoile.addEventListener(TimerEvent.TIMER,f_Etoile);
TmEtoile.start();
 
function f_Etoile(te:TimerEvent) {
	// un entier qui vaut 1 ou 2
	var aa:int=Math.random()*2+1;
	// une référence à l'objet de classe, aléatoire
	var classEtoile:Class=getDefinitionByName("Mv_Etoile"+aa) as Class;
	// Ajouter l'instance
	var etoile:MovieClip= new classEtoile();
	// de 0 à 350
	etoile.x=Math.random()*351;
	// entre 0 et 450
	etoile.y=Math.random()*451;
	addChildAt(etoile,1);
}

Oui, bon ! Dans le code reproduit, il est écrit : addChildAt(etoile,1), de qui se moque-t-on ?!
Elle pourrait être attentive, la fille, et se relire…
Que non, répliquez vous instantanément - perspicace que vous êtes - le ciel est en dégradé, elle a ajouté un rectangle en arrière plan, c'est pour ça… Le rectangle est au niveau 0, il faut ajouter en 1 !
Comme l'idée est bonne (si, si), vous vous empressez de faire mieux. Bien consciencieusement, vous ajoutez un calque, sous celui qui porte la montagne - qu'on soit sûr - et vous dessinez une forme équipée de son dégradé.
Vous passez 1 à addChild, vous testez… et vous constatez que les étoiles sont par dessus la montagne.:mrgreen:

Il faut le savoir : les calques n'existent pas pour la liste d'affichage, toutes les formes superposées -je dis bien les formes, pas les clips ou graphiques - sont reconnues comme un même objet, même si elles sont sur des calques différents.

Il vous suffit de convertir la forme en graphique, et le tour est joué.

Limiter le nombre d'appels

Pour ce qui est des appel récurrents nous venons donc d'aborder la classe Timer dans le cadre spécifique d'un appel indéfini. N'oubliez pas que vous pouvez aussi en limiter l'exécution en lui passant un deuxième paramètre.
Un feu d'artifice par exemple, lancerait une salve d'une dizaine de fusées.

Pour illustrer sans perdre le bénéfice de notre travail, nous allons pousser un peu plus loin le raisonnement.
Ce qui est chagrinant ici, c'est le fait de supprimer les instances de la liste d'affichage en comptant sur le ramasse-miette (système interne de gestion des ressources qui échappe à notre contrôle) pour les supprimer, vraiment, de la mémoire. Sans compter que nous ne pouvons pas vraiment choisir le nombre de flocons (ou d'étoiles) animés en même temps à l'écran (sauf à se lancer dans des calculs compliqués ;))

Nous avons pris le parti d'ajouter des clips qui s'auto-suppriment à la dernière image.
On pourrait décider de laisser boucler les clips. Quand un flocon disparait en bas (fin d'animation), il réapparait en haut (boucle). Auquel cas, si le timer ne cesse d'ajouter des flocons… On va finir avec un écran tout blanc ;)
Solution : décider du nombre maximum de flocons (et d'étoiles). Ce qui revient à passer au Timer ce fameux deuxième paramètre. Lui “dire” : 'tu invoques la fonction f_Neige 60 fois' (par exemple).

Nous allons donc supprimer le code de la dernière image, plus de stop, plus de removeChild.

Heuhh… oui, mais moi j'aimais bien l'idée des flocons qui tombent au hasard. Si on fait ça et que le hasard veut qu'ils soient majoritairement groupés d'un côté, ça ne changera jamais. Pareil pour les étoiles, en plus elles sont fixes, on va vraiment les voir clignoter (je m'éteins et je m'allume, toujours au même endroit).
… Sauf à ce qu'on définisse à chaque “tour” (boucle) de nouvelles coordonnées…

Ah oui ! Sur la dernière image (quand le flocon a quitté la scène ou que l'étoile n'est plus visible) avant la boucle, on détermine de nouvelles coordonnées aléatoires (et autres propriétés).

Presque… Tant qu'à faire, autant poser ce code image 1, et en profiter pour alléger la fonction d'appel (f_Neige et f_Etoile) qui se contentera de créer une nouvelle instance et de l'ajouter à la liste d'affichage.

Vous allez vous en sortir d'un petit couper/coller un rien modifié

Les flocons

// de 0 à 350
x=Math.random()*351;
// entre -60 et -40
y=Math.random()*21-60;
// une chance sur deux d'être en symétrie
// aa vaut 0 ou 1
var aa:int=Math.random()*2;
// si aa vaut 0 on "retourne" le clip 
if (aa==0) {
	scaleX=-1;
}

La neige

// de 0 à 350
x=Math.random()*351;
// entre 0 et 450
y=Math.random()*451;

Image 1 de la scène

var tmNeige:Timer=new Timer(500,60);
tmNeige.addEventListener(TimerEvent.TIMER,f_Neige);
tmNeige.start();
 
var tmEtoile:Timer=new Timer(1000,35);
tmEtoile.addEventListener(TimerEvent.TIMER,f_Etoile);
tmEtoile.start();
 
function f_Etoile(te:TimerEvent) {
	// un entier qui vaut 1 ou 2
	var aa:int=Math.random()*2+1;
	// une référence à l'objet de classe, aléatoire
	var classEtoile:Class=getDefinitionByName("Mv_Etoile"+aa) as Class;
	//nouvelle instance
	var etoile:MovieClip= new classEtoile();
	// Ajouter à la liste d'affichage
	addChildAt(etoile,1);
}
 
function f_Neige(te:TimerEvent) {
	// un entier qui vaut 1 ou 2
	var aa:int=Math.random()*2+1;
	// une référence à l'objet de classe, aléatoire
	var classFlocon:Class=getDefinitionByName("Mv_Flocon"+aa) as Class;
	// nouvelle instance
	var floc:MovieClip= new classFlocon();
	// ajouter à laliste d'affichage
	addChild(floc);
}

Et voilà, une neige plus ou moins dense, un ciel plus ou moins étoilé, dépendront du deuxième paramètre.

En conclusion

:arrow: Ce tuto est destiné à illustrer la mise en œuvre de la classe Timer et non la meilleure technique pour faire neiger, qui dépendra du contexte et de vos exigences. Par exemple une simple carte de vœux avec une petite animation sur fond floconneux pourrait fort bien être réalisée en supprimant les clips en fin de scénario (time-line). En revanche la page de fond d'un site ou d'un jeu (par exemple) destinée à rester longuement à l'écran, nécessitera que vous économisiez les ressources sans vous reposer sur le ramasse miettes.

:arrow: Gardez à l'esprit que le Timer est subordonné à la cadence de l'animation. Ici elle est cadencée à 20 i/s, soit 50 millisecondes, les intervalles de 500 et 1000 étant des multiples, tout va bien. Par ailleurs du code trop lourd (trop long à l'exécution) risque de décaler l'appel.

:arrow: Sachez aussi que pour exécuter une action à la cadence de l'animation, il existe un événement ENTER_FRAME. Tous les objets d'affichage le diffusent :

monClip.addEventListener(Event.ENTER_FRAME,fonctionRappel)

Et encore ne perdez pas de vue que ce que nous avons ici construit graphiquement, à l'aide d'interpolations et autres guides de mouvement, pourrait tout aussi bien être “codé”. D'ailleurs, ne vous y trompez pas, les animations que vous réalisez “à la main” sont transformées en code quand vous générez un swf, ainsi que tout ce que vous avez construit à l'aide de l'interface graphique. Dans l'absolu, on peut faire une animation swf sans même un clip dans la bibliothèque, en décrivant tout en code.

Les sources

S'affranchir des interpolations

Ajouté par Monsieur Spi.

Pour ceux qui voudraient s’affranchir des guides, et plus généralement de l'interpolation pour le mouvement des flocons voici une version qui utilise principalement le code.

Cette version vous propose également une autre manière d’aborder votre animation puisque nous allons essayer de nous concentrer essentiellement sur le code. Cela va nous obliger à repenser une partie de notre projet, on peut considérer qu’il s’agit là d’un « niveau 2 » d’apprentissage. Comme on dit souvent, il y a autant de manières différentes d'écrire un programme qu'il y a de développeurs. C'est pourquoi il sera également amusant de voir comment chaque version aborde le problème. Notez qu'il n'y a pas de bonne ou de mauvaise version, les deux aboutissent au même résultat, il y a juste des manières de faire différentes.

Alors on pourrait se demander pourquoi essayer de passer par du code et ne pas laisser tout simplement les guides qu’on à commencé à utiliser. Question pertinente, la réponse est simple, lorsque l’on utilise un guide de déplacement chaque mouvement de chaque flocon est lié à ce guide. Par conséquent difficile de donner des mouvements libres à vos flocons ou de proposer différents types de chutes de neige. C’est donc uniquement par souci d’ajouter des fonctionnalités supplémentaires que nous allons essayer de nous affranchir des interpolations.

Vous avez vu dans l’exemple précédent comment aller chercher un clip qui provient de la bibliothèque et comment l’utiliser dans votre code pour le faire apparaître sur la scène.

Nous allons donc nous servir de cela dès le départ pour poser notre décor.

Dans la bibliothèque créez 3 clips et exportez les pour ActionScript.

  • Ciel exporté en « Ciel » représentera votre ciel
  • Montagnes exporté en « Montagnes » représentera les montagnes à l’arrière plan
  • Conteneur exporté en « Conteneur » représentera la chute de neige il contiendra tous les flocons.

Faites bien attention à ce que le point de repère de vos clips soit bien placé en haut et à gauche du clip, cela influera sur leur position sur la scène quand on voudra les placer.

Vous l'aurez compris on retire tout ce qu'il y a sur la scène, il ne reste plus qu'un image clé vide où l'on va placer le code.

On ouvre la fenêtre de code (F9) et on va commencer notre programme.

On commence par déclarer tous les objets utiles :

// objets
var ciel:MovieClip=new Ciel();
var montagnes:MovieClip=new Montagnes();
var conteneur:MovieClip=new Conteneur();

Puis on les place sur la scène, attention l’ordre dans lequel ils sont placés est important. Le ciel est au premier niveau, par-dessus on pose les montagnes et par-dessus on pose notre conteneur.

// mise en place
addChild(ciel);
addChild(montagnes);
addChild(conteneur);

Jusque là pas trop de problèmes, on n'a rien sur la timeline, tout dans la bibliothèque et on va chercher des références dans la bibliothèque pour créer des occurrences de ces références que l’on pose sur la scène dans un ordre précis.

A partir de là nous allons réfléchir un peu avant de nous lancer dans la suite du code. Pour commencer nous allons avoir besoin de variables qui vont nous simplifier la vie. Les variables ne servent pas qu’à effectuer des calculs elles sont également très pratiques pour faire des raccourcis dans votre programme ce qui le rendra plus lisible.

// variables
var nbrFlocons:Number=50;
var w:Number=stage.stageWidth;
var h:Number=stage.stageHeight;

Tout d’abord nous allons indiquer le nombre flocons présents à l’écran.
Notez donc qu’il suffira de modifier ce chiffre pour obtenir une chute de neige plus ou moins dense, plus il y a de flocons et plus c’est dense.

Ensuite nous allons utiliser deux raccourcis pour nous éviter d’avoir à chaque fois de longs textes à taper dans notre programme, ainsi les variables « w » et « h » représenterons respectivement la largeur de la scène et sa hauteur.

Bien nous avons choisi de créer 50 flocons, nous allons donc commencer à remplir notre conteneur avec des copies (occurrences) du flocon de référence qui se trouve dans notre bibliothèque.

Nous ne l’avons pas encore créé ?
Qu’à cela ne tienne, on va donc créer un clip nommé et exporté pour ActionScript avec le nom « Flocon ». Dans ce clip vous allez placer le flocon original mais cette fois le repère de position du flocon doit être au centre, nous allons faire effectuer des rotations à ce flocons autour de son axe central.

A la suite du programme nous allons donc créer nos 50 flocons.
Il faut entrevoir chaque flocon comme un individu, il aura ses propres caractéristiques qui permettront de le définir : sa propre vitesse de chute, son propre poids, sa propre taille, sa propre vitesse de rotation, ses propres mouvements.

A chaque fois que nous allons créer un flocon nous allons donc aussi lui indiquer tous ses paramètres pour en faire un individu puis nous indiquerons à cet individu comment il doit se déplacer en fonction de ses propres paramètres (c’est un écouteur).

Allez assez de blabla passons au code :

// ajoute les flocons
for (var i:Number=1; i<nbrFlocons; i++) {
 
 
	var flocon:MovieClip=new Flocon();// crée un occurence du flocon
	conteneur.addChild(flocon);// ajoute l'occurence au conteneur
 
	flocon.x=Math.random()*w; // position X de l'occurence
	flocon.y=-Math.random()*h; // position Y de l'occurence
	flocon.vitesseY=Math.random()*2+2; // vitesse de déplacement sur Y de l'occurence
	flocon.vitesseX=Math.random()*2; // vitesse de déplacement sur X de l'occurence
	flocon.rot=Math.random()*10; // vitesse de rotation de l'occurence
	flocon.scaleX= flocon.scaleY=Math.random(); // taille de l'occurence
	flocon.cible=Math.random()*w; // cible à atteindre
 
	// ajoute un écouteur à cette occurence
	flocon.addEventListener(Event.ENTER_FRAME, floconTombe);
}

Là nous effectuons une boucle à l’aide de l’instruction « for » (voir notice de Flash).
Cette boucle va s’incrémenter sur la variable « i » qui prendra des valeurs de 1 à 50 (le nombre de flocons max). A chaque incrémentation la boucle crée une nouvelle occurrence du flocon de référence et la place dans le conteneur. Pour chaque occurrence on crée tous les paramètres utiles, ces paramétres sont assez simples à comprendre nous n’allons donc pas les détailler sauf le dernier, « cible ». « cible » est un paramètre un peu particulier, il va nous permettre de donner aux flocons un mouvement aléatoire sur l’axe des X mais de manière fluide, le flocon se dirigera sur X en fonction d’une position ciblée, d’une « cible ».

Vous remarquerez que j’utilise presque à chaque fois la commande « Math.random() », elle permet d’obtenir un chiffre aléatoire compris entre 0 et 1. Ainsi on positionne aléatoirement le flocon sur l’axe X, on le positionne aléatoirement sur l’axe Y (mais en négatif de manière à ce que le flocon ne semble pas apparaître d’un coup sur la scène, il apparaît d’abord au dessus puis rentre dans la scène par le haut), sa vitesse de chute (vitesseY) est aléatoire, ainsi que sa vitesse de déplacement sur X et que sa rotation.

Enfin on ajoute un écouteur qui va permettre au flocon de savoir comment il doit se déplacer en fonction de ses propres paramètres, pour cela on utilisera non pas un TIMER comme nous l’avons vu dans l’exercice précédent mais un ENTER_FRAME.

L’écouteur peut donc se traduire ainsi :
A chaque fois que le clip est lu (c'est-à-dire tout le temps) lance la fonction « floconTombe » pour le déplacer.

Bien, il ne reste plus qu’à apprendre à nos flocons à se déplacer et pour cela nous allons construire une nouvelle fonction, celle qu’appellent les écouteurs des flocons :

// déplacement des flocons
function floconTombe(e:Event):void {
 
	var ob=e.target; // l'occurence concernée par l'écouteur
 
	ob.rotation+=ob.rot;// rotation du flocon
	ob.y+=ob.vitesseY;// chute du flocon
 
	// déplacement du X du flocon
 
	if (Math.random()<0.1) {
		ob.cible=Math.random()*w; // changement aléatoire de la cible
	}
 
	// déplacement du flocon vers la cible à sa propre vitesse sur X
	if (ob.x>ob.cible) {
		ob.x-=ob.vitesseX;
	}
	if (ob.x<ob.cible) {
		ob.x+=ob.vitesseX;
	}
 
	// raplace le flocon en haut si il atteind le bas de l'écran
	if (ob.y>h) {
		ob.y=-Math.random()*h;
		ob.x=Math.random()*w;
	}
}

On peut mettre à peu près tout ce qu’on veut dans cette fonction, mais pour le moment nous allons nous limiter à des actions simples.

Tout d’abord on fait tourner le flocon sur lui-même.
Puis on le fait tomber à sa propre vitesse.
Ensuite on gère son déplacement sur X.

Un flocon est en général porté par le vent de manière assez aléatoire, nous allons donc essayer de simuler simplement ce comportement, pour cela on détermine une coordonnée cible sur X et on déplace le flocon en direction de cette coordonnée à sa propre vitesse.

Le dernier bloc de la fonction semble anodin c’est pourtant une instruction très importante. Nous souhaitons que le nombre de flocons à l’écran soit constant, nous pourrions nous contenter de supprimer tout simplement les flocons quand ils touchent le bas de l’écran et en recréer de nouveaux en haut de l’écran. Mais cette méthode est surconsomatrice en ressources et quand on essayes de faire un programme on essayes également de l’optimiser au maximum. Ici une optimisation simple est possible, il suffit de replacer le flocon sortant en haut de l’écran, ainsi conserve toujours le même nombre de flocons sans avoir à les détruire et à en recréer de nouveaux.

Et voici le résultat, ce n'est pas parfait bien sur mais c'est totalement paramétrable, il vous suffit de jouer avec les paramètres des flocons ou bien de modifier la fonction de déplacement pour obtenir des chutes de neiges variables.

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

ilneigecs_spi.fla



Ajout par lilive, 16/03/2010

Je ne résiste pas à l'attrait de la neige qui tombe :). Voici donc une autre version où j'ai essayé de régler le rendu à mon gout:

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

ilneigecs3_liliv.fla

Comme j'utilise beaucoup de flocons, j'ai remplacé le dessin vectoriel du flocon par une image .png, ce qui diminue un peu la charge du processeur.