1 /**
  2  * Hilo
  3  * Copyright 2015 alibaba.com
  4  * Licensed under the MIT License
  5  */
  6 
  7 /**
  8  * <iframe src='../../../examples/ParticleSystem.html?noHeader' width = '550' height = '400' scrolling='no'></iframe>
  9  * <br/>
 10  * @class ParticleSystem A particle system.
 11  * @augments Container
 12  * @module hilo/game/ParticleSystem
 13  * @requires hilo/core/Hilo
 14  * @requires hilo/core/Class
 15  * @requires hilo/view/View
 16  * @requires hilo/view/Container
 17  * @requires hilo/view/Drawable
 18  * @requires hilo/util/util
 19  * @property {Number} [emitTime=0.2] Emit time interval(in second).
 20  * @property {Number} [emitTimeVar=0] Emit time interval variances.
 21  * @property {Number} [emitNum=10] Emit number.
 22  * @property {Number} [emitNumVar=0] Emit number variances.
 23  * @property {Number} [emitterX=0] The emitter x position.
 24  * @property {Number} [emitterY=0] The emitter y position.
 25  * @property {Number} [totalTime=Infinity] Total time.
 26  * @property {Number} [gx=0] The gravity x value.
 27  * @property {Number} [gy=0] The gravity y value.
 28  * @param {Object} properties properties The properties to create a view object, contains all writeable props of this class
 29  * @param {Object} properties.particle The config of particle.
 30  * @param {Number} [properties.particle.x=0] The x position.
 31  * @param {Number} [properties.particle.y=0] The y position
 32  * @param {Number} [properties.particle.vx=0] The x velocity.
 33  * @param {Number} [properties.particle.vy=0] The y velocity.
 34  * @param {Number} [properties.particle.ax=0] The x acceleration.
 35  * @param {Number} [properties.particle.ay=0] The y acceleration.
 36  * @param {Number} [properties.particle.life=1] The time particle lives(in second).
 37  * @param {Number} [properties.particle.alpha=1] The alpha.
 38  * @param {Number} [properties.particle.alphaV=0] The alpha decline rate.
 39  * @param {Number} [properties.particle.scale=1] The scale.
 40  * @param {Number} [properties.particle.scaleV=0] The scale decline rate.
 41 */
 42 var ParticleSystem = (function(){
 43     //粒子属性
 44     var props = ['x', 'y', 'vx', 'vy', 'ax', 'ay', 'rotation', 'rotationV', 'scale', 'scaleV', 'alpha', 'alphaV', 'life'];
 45     var PROPS = [];
 46     for(var i = 0, l = props.length;i < l;i ++){
 47         var p = props[i];
 48         PROPS.push(p);
 49         PROPS.push(p + "Var");
 50     }
 51 
 52     //粒子默认值
 53     var PROPS_DEFAULT = {
 54         x: 0,
 55         y: 0,
 56         vx: 0,
 57         vy: 0,
 58         ax: 0,
 59         ay: 0,
 60         scale:1,
 61         scaleV:0,
 62         alpha:1,
 63         alphaV:0,
 64         rotation: 0,
 65         rotationV: 0,
 66         life: 1
 67     };
 68 
 69     var diedParticles = [];
 70 
 71     var ParticleSystem = Class.create(/** @lends ParticleSystem.prototype */{
 72         Extends:Container,
 73         constructor:function(properties){
 74             this.id = this.id || properties.id || Hilo.getUid("ParticleSystem");
 75 
 76             this.emitterX = 0;
 77             this.emitterY = 0;
 78 
 79             this.gx = 0;
 80             this.gy = 0;
 81             this.totalTime = Infinity;
 82 
 83             this.emitNum = 10;
 84             this.emitNumVar = 0;
 85 
 86             this.emitTime = .2;
 87             this.emitTimeVar = 0;
 88 
 89             this.particle = {};
 90 
 91             ParticleSystem.superclass.constructor.call(this, properties);
 92 
 93             this.reset(properties);
 94         },
 95         Statics:{
 96             PROPS:PROPS,
 97             PROPS_DEFAULT:PROPS_DEFAULT,
 98             diedParticles:diedParticles
 99         },
100         /**
101          * Reset the properties.
102          * @param {Object} cfg
103         */
104         reset: function(cfg) {
105             util.copy(this, cfg);
106             this.particle.system = this;
107             if(this.totalTime <= 0){
108                 this.totalTime = Infinity;
109             }
110         },
111         /**
112          * 更新
113          * @param {Number} dt delta time(in milliseconds)
114         */
115         onUpdate: function(dt) {
116             dt *= .001;
117             if (this._isRun) {
118                 this._totalRunTime += dt;
119                 this._currentRunTime += dt;
120                 if (this._currentRunTime >= this._emitTime) {
121                     this._currentRunTime = 0;
122                     this._emitTime = getRandomValue(this.emitTime, this.emitTimeVar);
123                     this._emit();
124                 }
125 
126                 if (this._totalRunTime >= this.totalTime) {
127                     this.stop();
128                 }
129             }
130         },
131         /**
132          * Emit particles.
133         */
134         _emit: function() {
135             var num = getRandomValue(this.emitNum, this.emitNumVar)>>0;
136             for (var i = 0; i < num; i++) {
137                 this.addChild(Particle.create(this.particle));
138             }
139         },
140         /**
141          * Start emit particles.
142         */
143         start: function() {
144             this.stop(true);
145             this._currentRunTime = 0;
146             this._totalRunTime = 0;
147             this._isRun = true;
148             this._emitTime = getRandomValue(this.emitTime, this.emitTimeVar);
149         },
150         /**
151          * Stop emit particles.
152          * @param {Boolean} clear Whether or not clear all the particles.
153         */
154         stop: function(clear) {
155             this._isRun = false;
156             if (clear) {
157                 for (var i = this.children.length - 1; i >= 0; i--) {
158                     this.children[i].destroy();
159                 }
160             }
161         }
162     });
163 
164     /**
165      * @class 粒子
166      * @inner
167      * @param {Number} vx The x velocity.
168      * @param {Number} vy The y velocity.
169      * @param {Number} ax The x acceleration.
170      * @param {Number} ay The y acceleration.
171      * @param {Number} scaleV The scale decline rate.
172      * @param {Number} alphaV The alpha decline rate.
173      * @param {Number} rotationV The rotate speed.
174      * @param {Number} life The time particle lives(in seconds)
175     */
176     var Particle = Class.create({
177         Extends:View,
178         constructor:function(properties){
179             this.id = this.id || properties.id || Hilo.getUid("Particle");
180             Particle.superclass.constructor.call(this, properties);
181             this.init(properties);
182         },
183         /**
184          * Update the particle.
185         */
186         onUpdate: function(dt) {
187             dt *= .001;
188             if(this._died){
189                 return false;
190             }
191             var ax = this.ax + this.system.gx;
192             var ay = this.ay + this.system.gy;
193 
194             this.vx += ax * dt;
195             this.vy += ay * dt;
196             this.x += this.vx * dt;
197             this.y += this.vy * dt;
198 
199             this.rotation += this.rotationV;
200 
201             if (this._time > .1) {
202                 this.alpha += this.alphaV;
203             }
204 
205             this.scale += this.scaleV;
206             this.scaleX = this.scaleY = this.scale;
207 
208             this._time += dt;
209             if (this._time >= this.life || this.alpha <= 0) {
210                 this.destroy();
211                 return false;
212             }
213         },
214         /**
215          * Set the image of particle.
216         */
217         setImage: function(img, frame) {
218             this.drawable = this.drawable||new Drawable();
219             frame = frame || [0, 0, img.width, img.height];
220 
221             this.width = frame[2];
222             this.height = frame[3];
223             this.drawable.rect = frame;
224             this.drawable.image = img;
225         },
226         /**
227          * Destroy the particle.
228         */
229         destroy: function() {
230             this._died = true;
231             this.alpha = 0;
232             this.removeFromParent();
233             diedParticles.push(this);
234         },
235         /**
236          * Init the particle.
237         */
238         init: function(cfg) {
239             this.system = cfg.system;
240             this._died = false;
241             this._time = 0;
242             this.alpha = 1;
243             for (var i = 0, l = PROPS.length; i < l; i++) {
244                 var p = PROPS[i];
245                 var v = cfg[p] === undefined ? PROPS_DEFAULT[p] : cfg[p];
246                 this[p] = getRandomValue(v, cfg[p + 'Var']);
247             }
248 
249             this.x += this.system.emitterX;
250             this.y += this.system.emitterY;
251 
252             if (cfg.image) {
253                 var frame = cfg.frame;
254                 if(frame && frame[0].length){
255                     frame = frame[(Math.random() * frame.length) >> 0];
256                 }
257                 this.setImage(cfg.image, frame);
258                 if(cfg.pivotX !== undefined){
259                     this.pivotX = cfg.pivotX * frame[2];
260                 }
261                 if(cfg.pivotY !== undefined){
262                     this.pivotY = cfg.pivotY * frame[3];
263                 }
264             }
265         },
266         Statics:{
267             /**
268              * Create the particle.
269              * @param {Object} cfg The config of particle.
270             */
271             create:function(cfg) {
272                 if (diedParticles.length > 0) {
273                     var particle = diedParticles.pop();
274                     particle.init(cfg);
275                     return particle;
276                 } else {
277                     return new Particle(cfg);
278                 }
279             }
280         }
281 
282     });
283 
284     /**
285      * Get the random value.
286      * @private
287      * @param  {Number} value     The value.
288      * @param  {Number} variances The variances.
289      * @return {Number}
290      */
291     function getRandomValue(value, variances){
292         return variances ? value + (Math.random() - .5) * 2 * variances : value;
293     }
294 
295     return ParticleSystem;
296 })();