      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 })();