Dans ce tutoriel, je vais vous présenter le moteur de particules Stardust. Tout d'abord, je vais vous montrer comment configurer Stardust, puis je traiterai des responsabilités de base de la classe Stardust et de la manière dont elles collaborent pour que Stardust fonctionne dans son ensemble..
Ensuite, nous examinerons le flux de travail général d'un Stardust et nous nous attacherons à créer un effet de particules avec des étoiles sortant du curseur de la souris. les étoiles ralentissent progressivement, grossissent après la naissance et rétrécissent au moment de la mort.
Enfin, je démontrerai la flexibilité de Stardust en créant plusieurs variantes de l'exemple déjà complet, notamment en utilisant des clips animés en tant que particules, une échelle de temps de simulation de particules variable et en supprimant des objets d'affichage de différentes classes à partir d'un seul émetteur..
Ce didacticiel s'adresse aux personnes déjà familiarisées avec la programmation orientée objet (OOP) d'ActionScript 3.0. Je suppose donc que vous savez déjà très bien ce que sont les classes, les objets, l'héritage et l'interface. Pas de problème avec la POO? Alors allons tirer quelques étoiles!
Comme son nom l'indique, Stardust est utilisé pour créer des effets de particules. Si vous êtes un ActionScripter expérimenté, vous avez peut-être souvent créé des effets de particules à partir de rien et vous avez déclaré: "Je suis totalement cool avec la création d'effets de particules à partir de rien, alors pourquoi aurais-je besoin d'un moteur de particules?" Eh bien, Stardust est là pour vous aider à vous concentrer davantage sur la conception du comportement des particules que sur les tâches fastidieuses sous-jacentes, telles que la gestion de la mémoire. Au lieu d'écrire du code pour gérer les données relatives aux particules, d'initialiser et de supprimer les ressources, avec Stardust, vous pouvez ignorer ces routines ennuyeuses et décider simplement de la manière dont vous souhaitez que vos particules se comportent..
La structure de classe de Stardust est inspirée de FLiNT Particle System, un autre moteur de particules ActionScript 3.0. Ainsi, ils partagent certaines caractéristiques de base similaires.
En plus de ces fonctionnalités de base, Stardust fournit également plusieurs fonctionnalités avancées aux utilisateurs expérimentés..
En ce qui concerne les effets de particules, il est très important de gérer efficacement des données volumineuses. Stardust utilise beaucoup les pools d'objets et les listes chaînées pour améliorer les performances:
Avant de passer au codage proprement dit, nous devons récupérer une copie de Stardust Particle Engine. Il est publié sous licence MIT, ce qui signifie qu'il est totalement gratuit, que vous souhaitiez l'utiliser dans un projet commercial ou non commercial..
Voici la page d'accueil du projet Stardust: http://code.google.com/p/stardust-particle-engine/
Vous pouvez télécharger Stardust ici: http://code.google.com/p/stardust-particle-engine/downloads/list
Au moment de la rédaction de ce document, la dernière version pouvant être téléchargée à partir de la liste de téléchargement est la version 1.1.132 bêta. Vous pouvez toujours récupérer la dernière révision du référentiel SVN (qui peut ne pas être stable, cependant).
Sur la page d'accueil du projet, vous pouvez également trouver d'autres accessoires, tels que la documentation de l'API et une copie du manuel PDF. Il y a même des tutoriels vidéo sur YouTube.
Ici, je vais couvrir brièvement les classes de base Stardust et leurs responsabilités.
Cette classe est la super-classe de toutes les classes principales, qui définit les propriétés et les méthodes en particulier pour la sérialisation XML..
De manière générale, les effets de particules consistent essentiellement à contrôler un nombre d'entités ayant une apparence et des comportements similaires mais randomisés. La classe Random est destinée à générer des nombres aléatoires, qui peuvent être utilisés dans Stardust pour randomiser les propriétés des particules. Par exemple, la classe UniformRandom est une sous-classe de la classe Random, et son nom dit tout: le nombre aléatoire généré par un objet UniformRandom est uniformément distribué, et j'utiliserai cette classe en particulier pour l'ensemble du tutoriel..
Il arrive parfois qu'un nombre aléatoire à une dimension ne soit pas suffisant. Parfois, nous avons besoin de nombres aléatoires à deux dimensions, qui sont essentiellement des paires de nombres aléatoires, pour des propriétés telles que la position et la vitesse. La classe Zone permet de générer des paires de nombres aléatoires à deux dimensions. Cette classe modélise une paire de nombres aléatoires comme un point aléatoire dans une zone 2D. Par exemple, CircleZone génère des paires de nombres aléatoires (x, y) à partir de points aléatoires dans une région circulaire. Les classes Random et Zone sont principalement utilisées par la classe Initializer, qui sera traitée plus tard. La classe Zone3D est l'homologue 3D de cette classe, pour les effets de particules 3D..
La classe Emitter est à la base de l’encapsulation de tous les éléments de bas niveau. Un émetteur initialise les particules nouvellement créées avant leur ajout dans la simulation, met à jour les propriétés des particules à chaque itération de la boucle principale et supprime les particules mortes de la simulation. La méthode Emitter.step () est ce que vous souhaitez invoquer de manière répétée afin de maintenir Stardust opérationnel..
La classe Clock détermine le taux de création de nouvelles particules pour les émetteurs. Un objet Emitter contient exactement une référence à un objet Clock. Au début de chaque appel à une méthode Emitter.step (), l'émetteur demande à l'objet horloge le nombre de nouvelles particules qu'il doit créer. Prenons la classe SteadyClock, par exemple, elle indique aux émetteurs de créer de nouvelles particules à une vitesse constante..
Cette classe sert à initialiser des particules nouvellement créées. Un objet Initializer doit être ajouté à un émetteur pour fonctionner. Fondamentalement, une sous-classe Initializer initialise une seule propriété de particule. Par exemple, la classe d'initialisation de masse initialise la masse de nouvelles particules. Certains initialiseurs acceptent un objet aléatoire en tant que paramètre constructeur pour initialiser des particules avec des valeurs aléatoires. Le code suivant crée un initialiseur de Life qui initialise la vie des particules à des valeurs centrées de 50 avec une variation de 10, à savoir entre 40 et 60..
nouvelle vie (nouvelle UniformRandom (50, 10));
Les objets d'action mettent à jour les propriétés des particules à chaque itération de la boucle principale (méthode Emiter.step ()). Par exemple, la classe d'action Move met à jour les positions des particules en fonction de la vélocité. Un objet Action doit être ajouté à un émetteur pour fonctionner..
Maintenant que vous savez comment les classes principales collaborent, voyons un flux de travail général pour Stardust..
Vous commencez par créer un émetteur. Utilisez la classe Emitter2D pour les effets de particules 2D et la classe Emitter3D pour les effets 3D.
var emitter: Emitter = new Emitter2D ();
Pour spécifier le taux de création de particules, nous avons besoin d'une horloge. Cela peut être défini par la propriété Emitter.clock ou en transmettant une horloge comme premier paramètre au constructeur de l'émetteur..
// approche de propriété emitter.clock = new SteadyClock (1); // approche constructeur var emitter: Emitter = new Emitter2D (new SteadyClock (1));
Ajouter des initialiseurs à l'émetteur via la méthode Emitter.addInitializer ().
emitter.addInitializer (nouvelle vie (nouvelle UniformRandom (50, 10))); emitter.addInitializer (nouvelle échelle (nouvelle UniformRandom (1, 0.2)));
Ajouter des actions à l'émetteur via la méthode Emitter.addAction ().
emitter.addAction (nouveau Move ()); emitter.addAction (new Spin ());
Créer un rendu et ajouter l'émetteur à celui-ci via la méthode Renderer.addEmitter ().
Rendu var: Renderer = new DisplayObjectRenderer (conteneur); // "conteneur" est notre conteneur sprite renderer.addEmitter (emitter);
Enfin, appelez à plusieurs reprises la méthode Emitter.step () pour que la simulation de particules continue. Vous voudrez peut-être utiliser l'événement enter-frame ou un minuteur pour le faire. En un seul appel de la méthode Emitter.step (), l’horloge détermine le nombre de nouvelles particules à créer, ces nouvelles particules sont initialisées par des initialiseurs, toutes les particules sont mises à jour par des actions, les particules mortes sont supprimées et, enfin, le rendu est rendu. l'effet de particules.
// approche de l'événement enter-frame addEventListener (Event.ENTER_FRAME, mainLoop); // approche timer timer.addEventListener (TimerEvent.TIMER, mainLoop); fonction mainLoop (e: Event): void emitter.step ();
Bien. C'est à peu près tout pour l'amorce Stardust. Il est maintenant temps d'ouvrir Flash IDE et de vous salir les mains.
Créez un nouveau document Flash avec une dimension de 640X400, une cadence de 60 images par seconde et un arrière-plan sombre. Ici, j'ai fait un fond dégradé bleu foncé. Soit dit en passant, Stardust fonctionne bien avec Flash Player 9 et 10; il est donc normal que vous utilisiez Flash CS3 ou CS4. Dans ce tutoriel, je vais utiliser Flash CS3..
Nous créons un effet de particule avec des étoiles. Nous devons donc dessiner une étoile et la convertir en symbole, exporté au format ActionScript bien sûr. Ce symbole sera utilisé plus tard pour rendre notre effet de particules. Nommez le symbole et la classe exportée "Star".
Créez une nouvelle classe de document et nommez-la StarParticles.
package import flash.display.Sprite; Classe publique StarParticles étend Sprite fonction publique StarParticles ()
Comme mentionné dans le flux de travail général, la première étape consiste à créer un émetteur. Et l'étape suivante consiste à ajouter des initialiseurs et des actions à l'émetteur. Bien que cela puisse être fait dans le constructeur de la classe document, il est fortement recommandé de le faire dans une sous-classe distincte de l'émetteur. Il est toujours préférable de séparer la conception du comportement des particules du programme principal; ce faisant, le code est beaucoup plus propre et plus facile à modifier à l'avenir, sans être mélangé avec le programme principal.
Nous allons créer un effet de particule 2D, de sorte que Emitter2D est la classe d'émetteur que nous allons étendre. Étendez la classe Emitter2D et nommez-la StarEmitter, car nous allons lui faire tirer les étoiles plus tard. Le constructeur de l'émetteur accepte un paramètre Clock. Nous allons donc déclarer un paramètre de constructeur pour lui transmettre une référence d'objet Clock au constructeur de la superclasse..
package import idv.cjcat.stardust.twoD.emitters.Emitter2D; classe publique StarEmitter étend Emitter2D fonction publique StarEmitter (clock: Clock) // transmet l'objet clock au constructeur de la superclasse super (clock);
Une meilleure approche pour créer une sous-classe d'émetteur consiste à déclarer les paramètres de particule sous forme de constantes statiques, regroupées à un seul endroit. Ainsi, si vous souhaitez modifier les paramètres, vous saurez toujours où trouver les déclarations. La signification de ces constantes sera expliquée plus tard quand elles seront utilisées.
// durée de vie moyenne privée statique constante LIFE_AVG: Number = 30; // variation de la durée de vie private static const LIFE_VAR: Number = 10; // échelle moyenne privée statique privée SCALE_AVG: Number = 1; // variation d'échelle privée statique statique SCALE_VAR: Number = 0.4; // échelle de temps de croissance privé statique constant GROWING_TIME: Number = 5; // échelle rétrécissement temps privé privé statique const SHRINKING_TIME: Number = 10; // vitesse moyenne privée statique constante SPEED_AVG: Number = 10; // Variation de vitesse private static const SPEED_VAR: Number = 8; // moyenne oméga (vitesse angulaire) privée statique constante OMEGA_AVG: Number = 0; // Variante oméga privée statique statique statique OMEGA_VAR: Number = 5; // coefficient d'amortissement privé statique constant DAMPING: Number = 0.1;
De quels initialiseurs avons-nous besoin pour créer notre effet de particules? Jetons un coup d'oeil à la liste ci-dessous:
Et voici le code:
point = new SinglePoint (); addInitializer (new DisplayObjectClass (Star)); addInitializer (nouvelle vie (nouvelle UniformRandom (LIFE_AVG, LIFE_VAR))); addInitializer (nouvelle échelle (nouvelle unité UniformRandom (SCALE_AVG, SCALE_VAR))); addInitializer (nouvelle position (point)); addInitializer (nouvelle Velocity (nouvelle LazySectorZone (SPEED_AVG, SPEED_VAR))); addInitializer (nouvelle rotation (nouvelle UniformRandom (0, 180))); addInitializer (nouvelle Omega (nouvelle UniformRandom (OMEGA_AVG, OMEGA_VAR)));
D'accord, nous avons terminé avec les initialiseurs. Il est maintenant temps d'ajouter des actions à l'émetteur. Vous trouverez ci-dessous une liste d'actions dont nous avons besoin:
C'est tout. Notre émetteur est fait. Voici le code de cet émetteur dans son intégralité, y compris les instructions d'importation nécessaires.
package import idv.cjcat.stardust.common.actions.Age; import idv.cjcat.stardust.common.actions.DeathLife; import idv.cjcat.stardust.common.actions.ScaleCurve; importer idv.cjcat.stardust.common.clocks.Clock; import idv.cjcat.stardust.common.initializers.Life; import idv.cjcat.stardust.common.initializers.Scale; import idv.cjcat.stardust.common.math.UniformRandom; import idv.cjcat.stardust.twoD.actions.Damping; importer idv.cjcat.stardust.twoD.actions.Move; import idv.cjcat.stardust.twoD.actions.Spin; import idv.cjcat.stardust.twoD.emitters.Emitter2D; import idv.cjcat.stardust.twoD.initializers.DisplayObjectClass; import idv.cjcat.stardust.twoD.initializers.Omega; importer idv.cjcat.stardust.twoD.initializers.Position; import idv.cjcat.stardust.twoD.initializers.Rotation; importer idv.cjcat.stardust.twoD.initializers.Velocity; import idv.cjcat.stardust.twoD.zones.LazySectorZone; import idv.cjcat.stardust.twoD.zones.SinglePoint; Classe publique StarEmitter étend Emitter2D / ** * Constantes * / private static const LIFE_AVG: Number = 30; Const statique privée LIFE_VAR: Number = 10; Constante statique privée SCALE_AVG: Number = 1; Constante statique privée SCALE_VAR: Number = 0.4; Const statique privée GROWING_TIME: Number = 5; Const statique privée SHRINKING_TIME: Number = 10; Const statique privée SPEED_AVG: Number = 10; Constante statique privée SPEED_VAR: Number = 8; Constante statique privée OMEGA_AVG: Number = 0; Constante statique privée OMEGA_VAR: Number = 5; Constante statique privée DAMPING: Number = 0.1; public var point: SinglePoint; fonction publique StarEmitter (clock: Clock) super (clock); point = new SinglePoint (); // initializer addInitializer (new DisplayObjectClass (Star)); addInitializer (nouvelle vie (nouvelle UniformRandom (LIFE_AVG, LIFE_VAR))); addInitializer (nouvelle échelle (nouvelle unité UniformRandom (SCALE_AVG, SCALE_VAR))); addInitializer (nouvelle position (point)); addInitializer (nouvelle Velocity (nouvelle LazySectorZone (SPEED_AVG, SPEED_VAR))); addInitializer (nouvelle rotation (nouvelle UniformRandom (0, 180))); addInitializer (nouvelle Omega (nouvelle UniformRandom (OMEGA_AVG, OMEGA_VAR))); // actions addAction (new Age ()); addAction (new DeathLife ()); addAction (new Move ()); addAction (new Spin ()); addAction (nouvel amortissement (DAMPING)); addAction (nouvelle ScaleCurve (GROWING_TIME, SHRINKING_TIME));
Il est maintenant temps de revenir à la classe de document et de la terminer. Jetons un coup d'oeil aux tâches restantes.
Vous trouverez ci-dessous le code complet de la classe de document, avec les instructions d'importation nécessaires..
package import flash.display.Sprite; import flash.display.StageScaleMode; import flash.events.Event; import flash.geom.Rectangle; importer idv.cjcat.stardust.common.clocks.SteadyClock; import idv.cjcat.stardust.common.renderers.Renderer; importer idv.cjcat.stardust.twoD.renderers.DisplayObjectRenderer; Classe publique StarParticles étend Sprite private var emitter: StarEmitter; fonction publique StarParticles () // instancie l'émetteur StarEmitter = new StarEmitter (new SteadyClock (0.5)); // le conteneur sprite var conteneur: Sprite = new Sprite (); // le rendu qui rend l'effet de particules var renderer: Renderer = new DisplayObjectRenderer (conteneur); renderer.addEmitter (émetteur); // ajoute le conteneur à la liste d'affichage, au-dessus de l'arrière-plan addChildAt (conteneur, 1); // utilise l'événement enter-frame addEventListener (Event.ENTER_FRAME, mainLoop); fonction privée mainLoop (e: Event): void // met à jour la position de SinglePoint à la position de la souris emitter.point.x = mouseX; emitter.point.y = mouseY; // appelle la boucle principale emitter.step ();
Enfin, nous avons fini! Voyons maintenant le résultat. Appuyez sur CTRL + ENTRÉE dans Flash pour tester le film et vous verrez le résultat..
Nous n'avons pas encore fini! Faisons encore quelques variations. Le premier utilise des clips animés pour nos particules.
Cette première variante est assez simple, sans codage supplémentaire. C'est aussi simple que de créer une animation de scénario de base. Modifiez le symbole étoile dans Flash IDE, créez une autre image clé et modifiez la couleur de l'étoile dans cette image en rouge. Cela fait essentiellement clignoter les étoiles entre jaune et rouge. Vous voudrez peut-être insérer d'autres images vides entre elles, car une fréquence d'images de 60 images par seconde est trop rapide pour un clignotement de deux images..
Maintenant, testez le film et vérifiez le résultat. L'effet d'étoile clignotante a l'air caricatural; cela peut être utilisé pour des effets d'étourdissement classiques, ce qui est commun dans les dessins animés.
Comme je l'ai mentionné précédemment, l'une des fonctionnalités de Stardust est "l'échelle de temps de simulation ajustable", ce qui signifie que l'échelle de temps utilisée par Stardust pour la simulation de particules peut être ajustée de manière dynamique. Tout est fait en modifiant la propriété Emitter.stepTimeInterval, qui est 1 par défaut. L'extrait de code suiva