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