      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 粒子系统
     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/Bitmap
     18  * @requires hilo/view/Drawable
     19  * @requires hilo/util/util
     20  * @property {Number} [emitTime=0.2] 发射间隔
     21  * @property {Number} [emitTimeVar=0] 发射间隔变化量
     22  * @property {Number} [emitNum=10] 每次发射数量
     23  * @property {Number} [emitNumVar=0] 每次发射数量变化量
     24  * @property {Number} [emitterX=0] 发射器位置x
     25  * @property {Number} [emitterY=0] 发射器位置y
     26  * @property {Number} [totalTime=Infinity] 总时间
     27  * @property {Number} [gx=0] 重力加速度x
     28  * @property {Number} [gy=0] 重力加速度y
     29  * @param {Object} properties 创建对象的属性参数。可包含此类所有可写属性。
     30  * @param {Object} properties.particle 粒子属性配置
     31  * @param {Number} [properties.particle.x=0] x位置
     32  * @param {Number} [properties.particle.y=0] y位置
     33  * @param {Number} [properties.particle.vx=0] x速度
     34  * @param {Number} [properties.particle.vy=0] y速度
     35  * @param {Number} [properties.particle.ax=0] x加速度
     36  * @param {Number} [properties.particle.ay=0] y加速度
     37  * @param {Number} [properties.particle.life=1] 粒子存活时间，单位s
     38  * @param {Number} [properties.particle.alpha=1] 透明度
     39  * @param {Number} [properties.particle.alphaV=0] 透明度变化
     40  * @param {Number} [properties.particle.scale=1] 缩放
     41  * @param {Number} [properties.particle.scaleV=0] 缩放变化速度
     42 */
     43 var ParticleSystem = (function(){
     44     //粒子属性
     45     var props = ['x', 'y', 'vx', 'vy', 'ax', 'ay', 'rotation', 'rotationV', 'scale', 'scaleV', 'alpha', 'alphaV', 'life'];
     46     var PROPS = [];
     47     for(var i = 0, l = props.length;i < l;i ++){
     48         var p = props[i];
     49         PROPS.push(p);
     50         PROPS.push(p + "Var");
     51     }
     52
     53     //粒子默认值
     54     var PROPS_DEFAULT = {
     55         x: 0,
     56         y: 0,
     57         vx: 0,
     58         vy: 0,
     59         ax: 0,
     60         ay: 0,
     61         scale:1,
     62         scaleV:0,
     63         alpha:1,
     64         alphaV:0,
     65         rotation: 0,
     66         rotationV: 0,
     67         life: 1
     68     };
     69
     70     var diedParticles = [];
     71
     72     var ParticleSystem = Class.create(/** @lends ParticleSystem.prototype */{
     73         Extends:Container,
     74         constructor:function(properties){
     75             this.id = this.id || properties.id || Hilo.getUid("ParticleSystem");
     76
     77             this.emitterX = 0;
     78             this.emitterY = 0;
     79
     80             this.gx = 0;
     81             this.gy = 0;
     82             this.totalTime = Infinity;
     83
     84             this.emitNum = 10;
     85             this.emitNumVar = 0;
     86
     87             this.emitTime = .2;
     88             this.emitTimeVar = 0;
     89
     90             this.particle = {};
     91
     92             ParticleSystem.superclass.constructor.call(this, properties);
     93
     94             this.reset(properties);
     95         },
     96         Statics:{
     97             PROPS:PROPS,
     98             PROPS_DEFAULT:PROPS_DEFAULT,
     99             diedParticles:diedParticles
    100         },
    101         /**
    102          * 重置属性
    103          * @param {Object} cfg
    104         */
    105         reset: function(cfg) {
    106             util.copy(this, cfg);
    107             this.particle.system = this;
    108             if(this.totalTime <= 0){
    109                 this.totalTime = Infinity;
    110             }
    111         },
    112         /**
    113          * 更新
    114          * @param {Number} dt 间隔时间 单位ms
    115         */
    116         onUpdate: function(dt) {
    117             dt *= .001;
    118             if (this._isRun) {
    119                 this._totalRunTime += dt;
    120                 this._currentRunTime += dt;
    121                 if (this._currentRunTime >= this._emitTime) {
    122                     this._currentRunTime = 0;
    123                     this._emitTime = getRandomValue(this.emitTime, this.emitTimeVar);
    124                     this._emit();
    125                 }
    126
    127                 if (this._totalRunTime >= this.totalTime) {
    128                     this.stop();
    129                 }
    130             }
    131         },
    132         /**
    133          * 发射粒子
    134         */
    135         _emit: function() {
    136             var num = getRandomValue(this.emitNum, this.emitNumVar)>>0;
    137             for (var i = 0; i < num; i++) {
    138                 this.addChild(Particle.create(this.particle));
    139             }
    140         },
    141         /**
    142          * 开始发射粒子
    143         */
    144         start: function() {
    145             this.stop(true);
    146             this._currentRunTime = 0;
    147             this._totalRunTime = 0;
    148             this._isRun = true;
    149             this._emitTime = getRandomValue(this.emitTime, this.emitTimeVar);
    150         },
    151         /**
    152          * 停止发射粒子
    153          * @param {Boolean} clear 是否清除所有粒子
    154         */
    155         stop: function(clear) {
    156             this._isRun = false;
    157             if (clear) {
    158                 for (var i = this.children.length - 1; i >= 0; i--) {
    159                     this.children[i].destroy();
    160                 }
    161             }
    162         }
    163     });
    164
    165     /**
    166      * @class 粒子
    167      * @inner
    168      * @param {Number} vx x速度
    169      * @param {Number} vy y速度
    170      * @param {Number} ax x加速度
    171      * @param {Number} ay y加速度
    172      * @param {Number} scaleV 缩放变化速度
    173      * @param {Number} alphaV 透明度变换速度
    174      * @param {Number} rotationV 旋转速度
    175      * @param {Number} life 存活时间
    176     */
    177     var Particle = Class.create({
    178         Extends:View,
    179         constructor:function(properties){
    180             this.id = this.id || properties.id || Hilo.getUid("Particle");
    181             Particle.superclass.constructor.call(this, properties);
    182             this.init(properties);
    183         },
    184         /**
    185          * 更新粒子。
    186         */
    187         onUpdate: function(dt) {
    188             dt *= .001;
    189             if(this._died){
    190                 return false;
    191             }
    192             var ax = this.ax + this.system.gx;
    193             var ay = this.ay + this.system.gy;
    194
    195             this.vx += ax * dt;
    196             this.vy += ay * dt;
    197             this.x += this.vx * dt;
    198             this.y += this.vy * dt;
    199
    200             this.rotation += this.rotationV;
    201
    202             if (this._time > .1) {
    203                 this.alpha += this.alphaV;
    204             }
    205
    206             this.scale += this.scaleV;
    207             this.scaleX = this.scaleY = this.scale;
    208
    209             this._time += dt;
    210             if (this._time >= this.life || this.alpha <= 0) {
    211                 this.destroy();
    212                 return false;
    213             }
    214         },
    215         /**
    216          * 设置粒子图像。
    217         */
    218         setImage: function(img, frame) {
    219             this.drawable = this.drawable||new Drawable();
    220             frame = frame || [0, 0, img.width, img.height];
    221
    222             this.width = frame[2];
    223             this.height = frame[3];
    224             this.drawable.rect = frame;
    225             this.drawable.image = img;
    226         },
    227         /**
    228          * 销毁粒子
    229         */
    230         destroy: function() {
    231             this._died = true;
    232             this.alpha = 0;
    233             this.removeFromParent();
    234             diedParticles.push(this);
    235         },
    236         /**
    237          * 初始化粒子。
    238         */
    239         init: function(cfg) {
    240             this.system = cfg.system;
    241             this._died = false;
    242             this._time = 0;
    243             this.alpha = 1;
    244             for (var i = 0, l = PROPS.length; i < l; i++) {
    245                 var p = PROPS[i];
    246                 var v = cfg[p] === undefined ? PROPS_DEFAULT[p] : cfg[p];
    247                 this[p] = getRandomValue(v, cfg[p + 'Var']);
    248             }
    249
    250             this.x += this.system.emitterX;
    251             this.y += this.system.emitterY;
    252
    253             if (cfg.image) {
    254                 var frame = cfg.frame;
    255                 if(frame && frame[0].length){
    256                     frame = frame[(Math.random() * frame.length) >> 0];
    257                 }
    258                 this.setImage(cfg.image, frame);
    259                 if(cfg.pivotX !== undefined){
    260                     this.pivotX = cfg.pivotX * frame[2];
    261                 }
    262                 if(cfg.pivotY !== undefined){
    263                     this.pivotY = cfg.pivotY * frame[3];
    264                 }
    265             }
    266         },
    267         Statics:{
    268             /**
    269              * 生成粒子。
    270              * @param {Object} cfg 粒子参数。
    271             */
    272             create:function(cfg) {
    273                 if (diedParticles.length > 0) {
    274                     var particle = diedParticles.pop();
    275                     particle.init(cfg);
    276                     return particle;
    277                 } else {
    278                     return new Particle(cfg);
    279                 }
    280             }
    281         }
    282
    283     });
    284
    285     /**
    286      * Get the random value.
    287      * @private
    288      * @param  {Number} value     The value.
    289      * @param  {Number} variances The variances.
    290      * @return {Number}
    291      */
    292     function getRandomValue(value, variances){
    293         return variances ? value + (Math.random() - .5) * 2 * variances : value;
    294     }
    295
    296     return ParticleSystem;
    297 })();