1 /** 2 * Hilo 3 * Copyright 2015 alibaba.com 4 * Licensed under the MIT License 5 */ 6 7 /** 8 * @language=zh 9 * <iframe src='../../../examples/ParticleSystem.html?noHeader' width = '550' height = '400' scrolling='no'></iframe> 10 * <br/> 11 * @class 粒子系统 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 * @property {Number} emitTime 发射间隔 20 * @property {Number} emitTimeVar 发射间隔变化量 21 * @property {Number} emitNum 每次发射数量变化量 22 * @property {Number} emitNumVar 每次发射数量 23 * @property {Number} emitterX 发射器位置x 24 * @property {Number} emitterY 发射器位置y 25 * @property {Number} totalTime 总时间 26 * @property {Number} gx 重力加速度x 27 * @property {Number} gy 重力加速度y 28 * @param {Object} properties 创建对象的属性参数。可包含此类所有可写属性。 29 * @param {Object} properties.particle 粒子属性配置 30 * @param {Number} properties.particle.x x位置 31 * @param {Number} properties.particle.y y位置 32 * @param {Number} properties.particle.vx x速度 33 * @param {Number} properties.particle.vy y速度 34 * @param {Number} properties.particle.ax x加速度 35 * @param {Number} properties.particle.ay y加速度 36 * @param {Number} properties.particle.life 粒子存活时间 单位s 37 * @param {Number} properties.particle.alpha 透明度 38 * @param {Number} properties.particle.alphaV 透明度变化 39 * @param {Number} properties.particle.scale 缩放 40 * @param {Number} properties.particle.scaleV 缩放变化速度 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 ParticleSystem(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 * @language=zh 102 * 重置属性 103 * @param {Object} cfg 104 */ 105 reset: function(cfg) { 106 Hilo.copy(this, cfg); 107 this.particle.system = this; 108 if(this.totalTime <= 0){ 109 this.totalTime = Infinity; 110 } 111 }, 112 /** 113 * @language=zh 114 * 更新 115 * @param {Number} dt 间隔时间 单位ms 116 */ 117 onUpdate: function(dt) { 118 dt *= .001; 119 if (this._isRun) { 120 this._totalRunTime += dt; 121 this._currentRunTime += dt; 122 if (this._currentRunTime >= this._emitTime) { 123 this._currentRunTime = 0; 124 this._emitTime = getRandomValue(this.emitTime, this.emitTimeVar); 125 this._emit(); 126 } 127 128 if (this._totalRunTime >= this.totalTime) { 129 this.stop(); 130 } 131 } 132 }, 133 /** 134 * @language=zh 135 * 发射粒子 136 */ 137 _emit: function() { 138 var num = getRandomValue(this.emitNum, this.emitNumVar)>>0; 139 for (var i = 0; i < num; i++) { 140 this.addChild(Particle.create(this.particle)); 141 } 142 }, 143 /** 144 * @language=zh 145 * 开始发射粒子 146 */ 147 start: function() { 148 this.stop(true); 149 this._currentRunTime = 0; 150 this._totalRunTime = 0; 151 this._isRun = true; 152 this._emitTime = getRandomValue(this.emitTime, this.emitTimeVar); 153 }, 154 /** 155 * @language=zh 156 * 停止发射粒子 157 * @param {Boolean} clear 是否清除所有粒子 158 */ 159 stop: function(clear) { 160 this.isRun = false; 161 if (clear) { 162 for (var i = this.children.length - 1; i >= 0; i--) { 163 this.children[i].destroy(); 164 } 165 } 166 } 167 }); 168 169 /** 170 * @language=zh 171 * @class 粒子 172 * @inner 173 * @param {Number} vx x速度 174 * @param {Number} vy y速度 175 * @param {Number} ax x加速度 176 * @param {Number} ay y加速度 177 * @param {Number} scaleV 缩放变化速度 178 * @param {Number} alphaV 透明度变换速度 179 * @param {Number} rotationV 旋转速度 180 * @param {Number} life 存活时间 181 */ 182 var Particle = Class.create({ 183 Extends:View, 184 constructor:function Particle(properties){ 185 this.id = this.id || properties.id || Hilo.getUid("Particle"); 186 Particle.superclass.constructor.call(this, properties); 187 this.init(properties); 188 }, 189 /** 190 * @language=zh 191 * 更新粒子。 192 */ 193 onUpdate: function(dt) { 194 dt *= .001; 195 if(this._died){ 196 return; 197 } 198 var ax = this.ax + this.system.gx; 199 var ay = this.ay + this.system.gy; 200 201 this.vx += ax * dt; 202 this.vy += ay * dt; 203 this.x += this.vx * dt; 204 this.y += this.vy * dt; 205 206 this.rotation += this.rotationV; 207 208 if (this._time > .1) { 209 this.alpha += this.alphaV; 210 } 211 212 this.scale += this.scaleV; 213 this.scaleX = this.scaleY = this.scale; 214 215 this._time += dt; 216 if (this._time >= this.life || this.alpha < 0) { 217 this.destroy(); 218 } 219 }, 220 /** 221 * @language=zh 222 * 设置粒子图像。 223 */ 224 setImage: function(img, frame) { 225 this.drawable = this.drawable||new Drawable(); 226 var frame = frame || [0, 0, img.width, img.height]; 227 228 this.width = frame[2]; 229 this.height = frame[3]; 230 this.drawable.rect = frame; 231 this.drawable.image = img; 232 }, 233 /** 234 * @language=zh 235 * 销毁粒子 236 */ 237 destroy: function() { 238 this.died = true; 239 this.removeFromParent(); 240 diedParticles.push(this); 241 }, 242 /** 243 * @language=zh 244 * 初始化粒子。 245 */ 246 init: function(cfg) { 247 this.system = cfg.system; 248 this._died = false; 249 this._time = 0; 250 this.alpha = 1; 251 for (var i = 0, l = PROPS.length; i < l; i++) { 252 var p = PROPS[i]; 253 var v = cfg[p] === undefined ? PROPS_DEFAULT[p] : cfg[p]; 254 this[p] = getRandomValue(v, cfg[p + 'Var']); 255 } 256 257 this.x += this.system.emitterX; 258 this.y += this.system.emitterY; 259 260 if (cfg.image) { 261 var frame = cfg.frame; 262 if(frame && frame[0].length){ 263 frame = frame[(Math.random() * frame.length) >> 0]; 264 } 265 this.setImage(cfg.image, frame); 266 if(cfg.pivotX !== undefined){ 267 this.pivotX = cfg.pivotX * frame[2]; 268 } 269 if(cfg.pivotY !== undefined){ 270 this.pivotY = cfg.pivotY * frame[3]; 271 } 272 } 273 }, 274 Statics:{ 275 /** 276 * @language=zh 277 * 生成粒子。 278 * @param {Object} cfg 粒子参数。 279 */ 280 create:function(cfg) { 281 if (diedParticles.length > 0) { 282 var particle = diedParticles.pop(); 283 particle.init(cfg); 284 return particle; 285 } else { 286 return new Particle(cfg); 287 } 288 } 289 } 290 291 }); 292 293 /** 294 * Get the random value. 295 * @private 296 * @param {Number} value The value. 297 * @param {Number} variances The variances. 298 * @return {Number} 299 */ 300 function getRandomValue(value, variances){ 301 return variances ? value + (Math.random() - .5) * 2 * variances : value; 302 } 303 304 return ParticleSystem; 305 })();