2017-10-17 03:16:52 +00:00
|
|
|
var Between = require('../../math/Between');
|
|
|
|
var Class = require('../../utils/Class');
|
|
|
|
var Components = require('../components');
|
|
|
|
var DegToRad = require('../../math/DegToRad');
|
|
|
|
var Easing = require('../../math/easing');
|
|
|
|
var GameObject = require('../GameObject');
|
|
|
|
var GetEaseFunction = require('../../tweens/builder/GetEaseFunction');
|
2017-10-17 20:32:45 +00:00
|
|
|
var MinMax2 = require('../../math/MinMax2');
|
|
|
|
var MinMax4 = require('../../math/MinMax4');
|
2017-10-17 03:16:52 +00:00
|
|
|
var Particle = require('./Particle');
|
|
|
|
var StableSort = require('../../utils/array/StableSort');
|
|
|
|
|
|
|
|
var ParticleEmitter = new Class({
|
|
|
|
|
|
|
|
Mixins: [
|
|
|
|
Components.BlendMode,
|
|
|
|
Components.RenderTarget,
|
|
|
|
Components.ScrollFactor,
|
|
|
|
Components.Visible
|
|
|
|
],
|
|
|
|
|
|
|
|
initialize:
|
|
|
|
|
|
|
|
function ParticleEmitter (manager, config)
|
|
|
|
{
|
|
|
|
if (config === undefined) { config = {}; }
|
|
|
|
|
|
|
|
this.manager = manager;
|
|
|
|
|
|
|
|
this.key = '';
|
|
|
|
|
|
|
|
this.frame = manager.frame;
|
|
|
|
|
2017-10-17 20:32:45 +00:00
|
|
|
this.particleClass = Particle;
|
2017-10-17 03:16:52 +00:00
|
|
|
|
|
|
|
this.dead = [];
|
|
|
|
this.alive = [];
|
|
|
|
|
2017-10-17 20:32:45 +00:00
|
|
|
this.x = 0;
|
|
|
|
this.y = 0;
|
|
|
|
|
|
|
|
// Implement ease into the MinMax component?
|
2017-10-17 03:16:52 +00:00
|
|
|
|
2017-10-17 20:32:45 +00:00
|
|
|
this.velocity = new MinMax4();
|
2017-10-17 03:16:52 +00:00
|
|
|
|
2017-10-17 20:32:45 +00:00
|
|
|
this.scale = new MinMax4(1);
|
2017-10-17 03:16:52 +00:00
|
|
|
|
2017-10-18 01:26:15 +00:00
|
|
|
this.gravity = new MinMax2();
|
2017-10-17 03:16:52 +00:00
|
|
|
|
2017-10-17 20:32:45 +00:00
|
|
|
this.alpha = new MinMax2(1);
|
2017-10-17 03:16:52 +00:00
|
|
|
|
2017-10-18 01:26:15 +00:00
|
|
|
this.angle = new MinMax2();
|
2017-10-17 03:16:52 +00:00
|
|
|
|
2017-10-17 20:32:45 +00:00
|
|
|
this.particleAngle = new MinMax2();
|
2017-10-17 03:16:52 +00:00
|
|
|
|
2017-10-17 20:32:45 +00:00
|
|
|
// The lifespan of the particles (in ms)
|
|
|
|
this.lifespan = new MinMax2(1000);
|
2017-10-17 03:16:52 +00:00
|
|
|
|
|
|
|
this.deathCallback = null;
|
|
|
|
this.deathCallbackScope = null;
|
|
|
|
|
|
|
|
this.emitCount = 1;
|
2017-10-17 20:32:45 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @property {number} frequency - How often a particle is emitted in ms (if emitter is started with Explode === false).
|
|
|
|
*/
|
|
|
|
this.frequency = 100;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @property {boolean} on - Determines whether the emitter is currently emitting particles. It is totally safe to directly toggle this.
|
|
|
|
* @default
|
|
|
|
*/
|
|
|
|
this.on = false;
|
|
|
|
// this.enabled = true;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @property {boolean} particleBringToTop - If this is `true` then when the Particle is emitted it will be bought to the top of the Emitters display list.
|
|
|
|
* @default
|
|
|
|
*/
|
|
|
|
this.particleBringToTop = false;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @property {boolean} particleSendToBack - If this is `true` then when the Particle is emitted it will be sent to the back of the Emitters display list.
|
|
|
|
* @default
|
|
|
|
*/
|
|
|
|
this.particleSendToBack = false;
|
|
|
|
|
|
|
|
this.timeScale = 1;
|
|
|
|
|
|
|
|
// this.delay = 0;
|
|
|
|
// this.delayCounter = 0;
|
|
|
|
// this.allowCreation = true;
|
|
|
|
|
2017-10-17 03:16:52 +00:00
|
|
|
this.emitShape = null;
|
|
|
|
|
|
|
|
this.easingFunctionAlpha = Easing.Linear;
|
|
|
|
this.easingFunctionScale = Easing.Linear;
|
|
|
|
this.easingFunctionRotation = Easing.Linear;
|
|
|
|
|
|
|
|
this.active = true;
|
|
|
|
},
|
|
|
|
|
|
|
|
setFrame: function (frame)
|
|
|
|
{
|
|
|
|
this.frame = this.manager.texture.get(frame);
|
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
setPosition: function (x, y)
|
|
|
|
{
|
|
|
|
this.x = x;
|
|
|
|
this.y = y;
|
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2017-10-17 20:32:45 +00:00
|
|
|
/*
|
2017-10-17 03:16:52 +00:00
|
|
|
setEase: function (easeName, easeParam)
|
|
|
|
{
|
|
|
|
var ease = GetEaseFunction(easeName, easeParam);
|
|
|
|
|
|
|
|
this.easingFunctionAlpha = ease;
|
|
|
|
this.easingFunctionScale = ease;
|
|
|
|
this.easingFunctionRotation = ease;
|
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
setAlphaEase: function (easeName, easeParam)
|
|
|
|
{
|
|
|
|
this.easingFunctionAlpha = GetEaseFunction(easeName, easeParam);
|
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
setScaleEase: function (easeName, easeParam)
|
|
|
|
{
|
|
|
|
this.easingFunctionScale = GetEaseFunction(easeName, easeParam);
|
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
setRotationEase: function (easeName, easeParam)
|
|
|
|
{
|
|
|
|
this.easingFunctionRotation = GetEaseFunction(easeName, easeParam);
|
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
2017-10-17 20:32:45 +00:00
|
|
|
*/
|
2017-10-17 03:16:52 +00:00
|
|
|
|
2017-10-17 20:32:45 +00:00
|
|
|
// Particle Emission
|
2017-10-17 03:16:52 +00:00
|
|
|
|
2017-10-17 20:32:45 +00:00
|
|
|
setVelocity: function (xMin, xMax, yMin, yMax)
|
|
|
|
{
|
|
|
|
this.velocity.set(xMin, xMax, yMin, yMax);
|
2017-10-17 03:16:52 +00:00
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2017-10-17 20:32:45 +00:00
|
|
|
setScale: function (xMin, xMax, yMin, yMax)
|
2017-10-17 03:16:52 +00:00
|
|
|
{
|
2017-10-17 20:32:45 +00:00
|
|
|
this.scale.set(xMin, xMax, yMin, yMax);
|
2017-10-17 03:16:52 +00:00
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2017-10-18 01:26:15 +00:00
|
|
|
setGravity: function (x, y)
|
2017-10-17 03:16:52 +00:00
|
|
|
{
|
2017-10-18 01:26:15 +00:00
|
|
|
this.gravity.set(x, y);
|
2017-10-17 03:16:52 +00:00
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2017-10-17 20:32:45 +00:00
|
|
|
setAlpha: function (min, max)
|
2017-10-17 03:16:52 +00:00
|
|
|
{
|
2017-10-17 20:32:45 +00:00
|
|
|
this.alpha.set(min, max);
|
2017-10-17 03:16:52 +00:00
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2017-10-17 20:32:45 +00:00
|
|
|
setAngle: function (min, max)
|
2017-10-17 03:16:52 +00:00
|
|
|
{
|
2017-10-17 20:32:45 +00:00
|
|
|
this.angle.set(min, max);
|
2017-10-17 03:16:52 +00:00
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2017-10-17 20:32:45 +00:00
|
|
|
setParticleAngle: function (min, max)
|
2017-10-17 03:16:52 +00:00
|
|
|
{
|
2017-10-17 20:32:45 +00:00
|
|
|
this.particleAngle.set(min, max);
|
2017-10-17 03:16:52 +00:00
|
|
|
|
2017-10-17 20:32:45 +00:00
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
setLifespan: function (min, max)
|
|
|
|
{
|
|
|
|
this.lifespan.set(min, max);
|
2017-10-17 03:16:52 +00:00
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2017-10-17 20:32:45 +00:00
|
|
|
setDelay: function (delay)
|
2017-10-17 03:16:52 +00:00
|
|
|
{
|
|
|
|
this.delay = delay;
|
|
|
|
this.delayCounter = delay / 1000;
|
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
setShape: function (shape)
|
|
|
|
{
|
|
|
|
this.emitShape = shape;
|
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2017-10-17 20:32:45 +00:00
|
|
|
// Particle Management
|
|
|
|
|
2017-10-17 03:16:52 +00:00
|
|
|
reserve: function (particleCount)
|
|
|
|
{
|
|
|
|
var dead = this.dead;
|
|
|
|
|
|
|
|
for (var count = 0; count < particleCount; ++count)
|
|
|
|
{
|
2017-10-17 20:32:45 +00:00
|
|
|
dead.push(new this.particleClass(this.x, this.y, this.frame));
|
2017-10-17 03:16:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
getAliveParticleCount: function ()
|
|
|
|
{
|
|
|
|
return this.alive.length;
|
|
|
|
},
|
|
|
|
|
|
|
|
getDeadParticleCount: function ()
|
|
|
|
{
|
|
|
|
return this.dead.length;
|
|
|
|
},
|
|
|
|
|
|
|
|
getParticleCount: function ()
|
|
|
|
{
|
|
|
|
return this.getAliveParticleCount() + this.getDeadParticleCount();
|
|
|
|
},
|
|
|
|
|
|
|
|
onParticleDeath: function (callback, context)
|
|
|
|
{
|
|
|
|
if (callback === undefined)
|
|
|
|
{
|
|
|
|
// Clear any previously set callback
|
|
|
|
this.deathCallback = null;
|
|
|
|
this.deathCallbackScope = null;
|
|
|
|
}
|
|
|
|
else if (typeof callback === 'function')
|
|
|
|
{
|
|
|
|
this.deathCallback = callback;
|
|
|
|
|
|
|
|
if (context)
|
|
|
|
{
|
|
|
|
this.deathCallbackScope = context;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
killAll: function ()
|
|
|
|
{
|
|
|
|
var dead = this.dead;
|
|
|
|
var alive = this.alive;
|
|
|
|
|
|
|
|
while (alive.length > 0)
|
|
|
|
{
|
|
|
|
dead.push(alive.pop());
|
|
|
|
}
|
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
forEachAlive: function (callback, thisArg)
|
|
|
|
{
|
|
|
|
var alive = this.alive;
|
|
|
|
var length = alive.length;
|
|
|
|
|
|
|
|
for (var index = 0; index < length; ++index)
|
|
|
|
{
|
|
|
|
callback.call(thisArg, alive[index]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
forEachDead: function (callback, thisArg)
|
|
|
|
{
|
|
|
|
var dead = this.dead;
|
|
|
|
var length = dead.length;
|
|
|
|
|
|
|
|
for (var index = 0; index < length; ++index)
|
|
|
|
{
|
|
|
|
callback.call(thisArg, dead[index]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
pause: function ()
|
|
|
|
{
|
|
|
|
this.active = false;
|
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
resume: function ()
|
|
|
|
{
|
|
|
|
this.active = true;
|
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2017-10-17 20:32:45 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-10-17 03:16:52 +00:00
|
|
|
explode: function (count)
|
|
|
|
{
|
|
|
|
this.emit(count);
|
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
emitAt: function (x, y, count)
|
|
|
|
{
|
|
|
|
var oldX = this.x;
|
|
|
|
var oldY = this.y;
|
|
|
|
|
|
|
|
this.x = x;
|
|
|
|
this.y = y;
|
|
|
|
|
|
|
|
var particle = this.emit(count);
|
|
|
|
|
|
|
|
this.x = oldX;
|
|
|
|
this.y = oldY;
|
|
|
|
|
|
|
|
return particle;
|
|
|
|
},
|
|
|
|
|
|
|
|
emit: function (count)
|
|
|
|
{
|
|
|
|
if (count === undefined) { count = 1; }
|
|
|
|
|
|
|
|
var particle = null;
|
|
|
|
|
|
|
|
var x = this.x;
|
|
|
|
var y = this.y;
|
2017-10-17 20:32:45 +00:00
|
|
|
// var shape = this.emitShape;
|
2017-10-17 03:16:52 +00:00
|
|
|
var dead = this.dead;
|
2017-10-18 01:26:15 +00:00
|
|
|
var allowCreation = true;
|
2017-10-17 03:16:52 +00:00
|
|
|
|
|
|
|
for (var index = 0; index < count; index++)
|
|
|
|
{
|
|
|
|
if (dead.length > 0)
|
|
|
|
{
|
|
|
|
particle = dead.pop();
|
|
|
|
particle.reset(x, y, this.frame);
|
|
|
|
}
|
|
|
|
else if (allowCreation)
|
|
|
|
{
|
2017-10-17 20:32:45 +00:00
|
|
|
particle = new this.particleClass(x, y, this.frame);
|
2017-10-17 03:16:52 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2017-10-17 20:32:45 +00:00
|
|
|
// if (shape)
|
|
|
|
// {
|
|
|
|
// shape.getRandomPoint(particle);
|
|
|
|
// particle.x += x;
|
|
|
|
// particle.y += y;
|
|
|
|
// }
|
|
|
|
|
|
|
|
particle.emit(this);
|
|
|
|
|
|
|
|
// particle.velocityX = vx;
|
|
|
|
// particle.velocityY = vy;
|
|
|
|
// particle.life = Math.max(this.life, Number.MIN_VALUE);
|
|
|
|
// particle.lifeStep = particle.life;
|
|
|
|
// particle.start.scale = this.startScale;
|
|
|
|
// particle.end.scale = this.endScale;
|
|
|
|
// particle.scaleX = this.startScale;
|
|
|
|
// particle.scaleY = this.startScale;
|
|
|
|
// particle.start.alpha = this.startAlpha;
|
|
|
|
// particle.end.alpha = this.endAlpha;
|
|
|
|
// particle.start.rotation = DegToRad(this.startAngle);
|
|
|
|
// particle.end.rotation = DegToRad(this.endAngle);
|
|
|
|
// particle.color = (particle.color & 0x00FFFFFF) | (((this.startAlpha * 0xFF)|0) << 24);
|
|
|
|
// particle.index = this.alive.length;
|
2017-10-17 03:16:52 +00:00
|
|
|
|
|
|
|
this.alive.push(particle);
|
|
|
|
}
|
|
|
|
|
|
|
|
return particle;
|
|
|
|
},
|
|
|
|
|
|
|
|
preUpdate: function (time, delta)
|
|
|
|
{
|
2017-10-17 20:32:45 +00:00
|
|
|
// Scale the delta
|
|
|
|
delta *= this.timeScale;
|
|
|
|
|
2017-10-17 03:16:52 +00:00
|
|
|
var dead = this.dead;
|
|
|
|
var particles = this.alive;
|
2017-10-17 20:32:45 +00:00
|
|
|
|
2017-10-17 03:16:52 +00:00
|
|
|
var length = particles.length;
|
2017-10-17 20:32:45 +00:00
|
|
|
|
|
|
|
var step = (delta / 1000);
|
|
|
|
|
2017-10-17 03:16:52 +00:00
|
|
|
var deathCallback = this.deathCallback;
|
|
|
|
var deathCallbackScope = this.deathCallbackScope;
|
|
|
|
|
|
|
|
/* Simulation */
|
2017-10-17 20:32:45 +00:00
|
|
|
for (var index = 0; index < length; index++)
|
2017-10-17 03:16:52 +00:00
|
|
|
{
|
|
|
|
var particle = particles[index];
|
|
|
|
|
2017-10-17 20:32:45 +00:00
|
|
|
// update returns `true` if the particle is now dead (lifeStep < 0)
|
2017-10-18 01:26:15 +00:00
|
|
|
if (particle.update(this, delta, step))
|
2017-10-17 20:32:45 +00:00
|
|
|
{
|
|
|
|
// Moves the dead particle to the end of the particles array (ready for splicing out later)
|
|
|
|
var last = particles[length - 1];
|
|
|
|
particles[length - 1] = particle;
|
|
|
|
particles[index] = last;
|
|
|
|
index -= 1;
|
|
|
|
length -= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2017-10-17 03:16:52 +00:00
|
|
|
particle.velocityX += gravityX;
|
|
|
|
particle.velocityY += gravityY;
|
|
|
|
particle.x += particle.velocityX * emitterStep;
|
|
|
|
particle.y += particle.velocityY * emitterStep;
|
|
|
|
particle.normLifeStep = particle.lifeStep / particle.life;
|
|
|
|
|
|
|
|
var norm = 1 - particle.normLifeStep;
|
|
|
|
var alphaEase = this.easingFunctionAlpha(norm);
|
|
|
|
var scaleEase = this.easingFunctionScale(norm);
|
|
|
|
var rotationEase = this.easingFunctionRotation(norm);
|
|
|
|
var alphaf = (particle.end.alpha - particle.start.alpha) * alphaEase + particle.start.alpha;
|
|
|
|
var scale = (particle.end.scale - particle.start.scale) * scaleEase + particle.start.scale;
|
|
|
|
var rotation = (particle.end.rotation - particle.start.rotation) * rotationEase + particle.start.rotation;
|
|
|
|
|
|
|
|
particle.scaleX = particle.scaleY = scale;
|
|
|
|
particle.color = (particle.color & 0x00FFFFFF) | (((alphaf * 0xFF)|0) << 24);
|
|
|
|
particle.rotation = rotation;
|
|
|
|
|
|
|
|
if (particle.lifeStep <= 0)
|
|
|
|
{
|
|
|
|
var last = particles[length - 1];
|
|
|
|
particles[length - 1] = particle;
|
|
|
|
particles[index] = last;
|
|
|
|
index -= 1;
|
|
|
|
length -= 1;
|
|
|
|
|
|
|
|
if (deathCallback)
|
|
|
|
{
|
|
|
|
deathCallback.call(deathCallbackScope, particle);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
particle.lifeStep -= emitterStep;
|
2017-10-17 20:32:45 +00:00
|
|
|
*/
|
2017-10-17 03:16:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Move dead particles to the dead array
|
|
|
|
// We can skip this for 'emitCount' number of particles if 'this.enabled'
|
|
|
|
var deadLength = particles.length - length;
|
|
|
|
|
|
|
|
if (deadLength > 0)
|
|
|
|
{
|
|
|
|
dead.push.apply(dead, particles.splice(particles.length - deadLength, deadLength));
|
|
|
|
|
|
|
|
StableSort(particles, this.indexSort);
|
|
|
|
}
|
|
|
|
|
2017-10-18 01:26:15 +00:00
|
|
|
// this.delayCounter -= emitterStep;
|
2017-10-17 03:16:52 +00:00
|
|
|
|
2017-10-18 01:26:15 +00:00
|
|
|
// if (this.delayCounter <= 0 && this.enabled)
|
|
|
|
// {
|
2017-10-17 03:16:52 +00:00
|
|
|
this.emit(this.emitCount);
|
2017-10-18 01:26:15 +00:00
|
|
|
// this.delayCounter = this.delay / 1000;
|
|
|
|
// }
|
2017-10-17 03:16:52 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
indexSort: function (a, b)
|
|
|
|
{
|
|
|
|
return a.index - b.index;
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
module.exports = ParticleEmitter;
|