Loads of little fixes all across the emitter classes.

This commit is contained in:
Richard Davey 2017-10-23 17:11:13 +01:00
parent 80a652e071
commit 816b228cc5
3 changed files with 149 additions and 253 deletions

View file

@ -1,23 +1,26 @@
var GetEaseFunction = require('../../tweens/builder/GetEaseFunction');
var GetValue = require('../../utils/object/GetValue');
var GetFastValue = require('../../utils/object/GetFastValue');
var FloatBetween = require('../../math/FloatBetween'); var FloatBetween = require('../../math/FloatBetween');
var GetEaseFunction = require('../../tweens/builder/GetEaseFunction');
var GetFastValue = require('../../utils/object/GetFastValue');
var Wrap = require('../../math/Wrap');
function hasOnEmit (def) function has (object, key)
{ {
return (!!def.onEmit && typeof def.onEmit === 'function'); return (object.hasOwnProperty(key));
} }
function hasOnUpdate (def) function hasBoth (object, key1, key2)
{ {
return (!!def.onUpdate && typeof def.onUpdate === 'function'); return (object.hasOwnProperty(key1) && object.hasOwnProperty(key2));
} }
function hasGetters (def) function hasGetters (def)
{ {
return hasOnEmit(def) || hasOnUpdate(def); return (has(def, 'onEmit')) || (has(def, 'onUpdate'));
} }
var steps = 0;
var counter = 0;
var GetValueOp = function (config, key, defaultValue) var GetValueOp = function (config, key, defaultValue)
{ {
var propertyValue = GetFastValue(config, key, defaultValue); var propertyValue = GetFastValue(config, key, defaultValue);
@ -73,31 +76,42 @@ var GetValueOp = function (config, key, defaultValue)
particleUpdate = propertyValue; particleUpdate = propertyValue;
} }
else if (t === 'object' && propertyValue.hasOwnProperty('start') && propertyValue.hasOwnProperty('end')) else if (t === 'object' && (has(propertyValue, 'random') || hasBoth(propertyValue, 'start', 'end') || hasBoth(propertyValue, 'min', 'max')))
{ {
var start = propertyValue.start; var start = (propertyValue.hasOwnProperty('start')) ? propertyValue.start : propertyValue.min;
var end = propertyValue.end; var end = (propertyValue.hasOwnProperty('end')) ? propertyValue.end : propertyValue.max;
var isRandom = false;
// A random starting value: // A random starting value:
// x: { start: 100, end: 400, randomStart: true } // x: { start: 100, end: 400, random: true } OR { min: 100, max: 400, random: true } OR { random: [ 100, 400 ] }
if (propertyValue.hasOwnProperty('randomStart')) if (has(propertyValue, 'random'))
{ {
particleEmit = function () isRandom = true;
var rnd = propertyValue.random;
// x: { random: [ 100, 400 ] } = the same as doing: x: { start: 100, end: 400, random: true }
if (Array.isArray(rnd))
{ {
return FloatBetween(start, end); start = rnd[0];
}; end = rnd[1];
} }
else
{ particleEmit = function (particle, key)
particleEmit = function ()
{ {
return start; var data = particle.data[key];
var value = FloatBetween(start, end);
data.min = value;
return value;
}; };
} }
if (propertyValue.hasOwnProperty('steps')) if (has(propertyValue, 'steps'))
{ {
// A stepped (per emit) range // A stepped (per emit) range
@ -105,11 +119,18 @@ var GetValueOp = function (config, key, defaultValue)
// Increments a value stored in the emitter // Increments a value stored in the emitter
particleUpdate = function (particle, key) steps = propertyValue.steps;
{ counter = start;
var emitter = particle.emitter;
return emitter.getNext(key); particleEmit = function ()
{
var value = counter;
var i = value + ((end - start) / steps);
counter = Wrap(i, start, end);
return counter;
}; };
} }
else else
@ -122,12 +143,25 @@ var GetValueOp = function (config, key, defaultValue)
var easeFunc = GetEaseFunction(ease); var easeFunc = GetEaseFunction(ease);
if (!isRandom)
{
particleEmit = function (particle, key)
{
var data = particle.data[key];
data.min = start;
data.max = end;
return start;
};
}
particleUpdate = function (particle, key, t, value) particleUpdate = function (particle, key, t, value)
{ {
var data = particle.data[key]; var data = particle.data[key];
return data.calc * easeFunc(t) + data.min; return (data.max - data.min) * easeFunc(t) + data.min;
} };
} }
} }
else if (t === 'object' && hasGetters(propertyValue)) else if (t === 'object' && hasGetters(propertyValue))
@ -150,12 +184,12 @@ var GetValueOp = function (config, key, defaultValue)
} }
*/ */
if (hasOnEmit(propertyValue)) if (has(propertyValue, 'onEmit'))
{ {
particleEmit = propertyValue.onEmit; particleEmit = propertyValue.onEmit;
} }
if (hasOnUpdate(propertyValue)) if (has(propertyValue, 'onUpdate'))
{ {
particleUpdate = propertyValue.onUpdate; particleUpdate = propertyValue.onUpdate;
} }

View file

@ -1,6 +1,5 @@
var Class = require('../../utils/Class'); var Class = require('../../utils/Class');
var DegToRad = require('../../math/DegToRad'); var DegToRad = require('../../math/DegToRad');
var FloatBetween = require('../../math/FloatBetween');
var Particle = new Class({ var Particle = new Class({
@ -44,10 +43,10 @@ var Particle = new Class({
// ease data // ease data
this.data = { this.data = {
tint: { min: 0xffffff, max: 0xffffff, current: 0xffffff }, tint: { min: 0xffffff, max: 0xffffff, current: 0xffffff },
alpha: { min: 1, max: 1, calc: 0 }, alpha: { min: 1, max: 1 },
angle: { min: 0, max: 0, calc: 0 }, angle: { min: 0, max: 0 },
scaleX: { min: 1, max: 1, calc: 0 }, scaleX: { min: 1, max: 1 },
scaleY: { min: 1, max: 1, calc: 0 } scaleY: { min: 1, max: 1 }
}; };
}, },
@ -67,15 +66,15 @@ var Particle = new Class({
emitter.zone.getRandomPoint(this); emitter.zone.getRandomPoint(this);
} }
this.x += emitter.x.getNext(); this.x += emitter.x.onEmit(this, 'x');
this.y += emitter.y.getNext(); this.y += emitter.y.onEmit(this, 'y');
var sx = emitter.speedX.getNext(); var sx = emitter.speedX.onEmit(this, 'speedX');
var sy = emitter.speedY.getNext(); var sy = (emitter.speedY) ? emitter.speedY.onEmit(this, 'speedY') : sx;
if (emitter.radial) if (emitter.radial)
{ {
var rad = DegToRad(emitter.emitterAngle.getNext()); var rad = DegToRad(emitter.emitterAngle.onEmit(this, 'angle'));
this.velocityX = Math.cos(rad) * Math.abs(sx); this.velocityX = Math.cos(rad) * Math.abs(sx);
this.velocityY = Math.sin(rad) * Math.abs(sy); this.velocityY = Math.sin(rad) * Math.abs(sy);
@ -86,104 +85,17 @@ var Particle = new Class({
this.velocityY = sy; this.velocityY = sy;
} }
this.life = emitter.lifespan.getNext(); this.life = emitter.lifespan.onEmit(this, 'lifespan');
this.lifeCurrent = this.life; this.lifeCurrent = this.life;
// eased values this.scaleX = emitter.scaleX.onEmit(this, 'scaleX');
this.scaleY = (emitter.scaleY) ? emitter.scaleY.onEmit(this, 'scaleY') : this.scaleX;
var dataScaleX = this.data.scaleX; this.angle = emitter.particleAngle.onEmit(this, 'angle');
var dataScaleY = this.data.scaleY; this.rotation = DegToRad(this.angle);
var dataAngle = this.data.angle;
var dataAlpha = this.data.alpha;
emitter.scaleX.copyToMinMax(dataScaleX); this.alpha = emitter.alpha.onEmit(this, 'alpha');
emitter.scaleY.copyToMinMax(dataScaleY);
emitter.particleAngle.copyToMinMax(dataAngle);
emitter.alpha.copyToMinMax(dataAlpha);
// Random overrides
if (emitter.randomScaleX)
{
var randomScaleX = FloatBetween(emitter.randomScaleX[0], emitter.randomScaleX[1]);
// If there is no current ease value set we override them both
if (dataScaleX.min === dataScaleX.max)
{
dataScaleX.min = randomScaleX;
dataScaleX.max = randomScaleX;
}
else
{
// Otherwise we just reset the start value, so it still eases to the end value
dataScaleX.min = randomScaleX;
}
}
if (emitter.randomScaleY)
{
var randomScaleY = FloatBetween(emitter.randomScaleY[0], emitter.randomScaleY[1]);
// If there is no current ease value set we override them both
if (dataScaleY.min === dataScaleY.max)
{
dataScaleY.min = randomScaleY;
dataScaleY.max = randomScaleY;
}
else
{
// Otherwise we just reset the start value, so it still eases to the end value
dataScaleY.min = randomScaleY;
}
}
if (emitter.randomAngle)
{
var randomAngle = FloatBetween(emitter.randomAngle[0], emitter.randomAngle[1]);
// If there is no current ease value set we override them both
if (dataAngle.min === dataAngle.max)
{
dataAngle.min = randomAngle;
dataAngle.max = randomAngle;
}
else
{
// Otherwise we just reset the start value, so it still eases to the end value
dataAngle.min = randomAngle;
}
}
if (emitter.randomAlpha)
{
var randomAlpha = FloatBetween(emitter.randomAlpha[0], emitter.randomAlpha[1]);
// If there is no current ease value set we override them both
if (dataAlpha.min === dataAlpha.max)
{
dataAlpha.min = randomAlpha;
dataAlpha.max = randomAlpha;
}
else
{
// Otherwise we just reset the start value, so it still eases to the end value
dataAlpha.min = randomAlpha;
}
}
// Pre-calc ease values
dataScaleX.calc = dataScaleX.max - dataScaleX.min;
dataScaleY.calc = dataScaleY.max - dataScaleY.min;
dataAngle.calc = dataAngle.max - dataAngle.min;
dataAlpha.calc = dataAlpha.max - dataAlpha.min;
// Set initial values
this.scaleX = dataScaleX.min;
this.scaleY = dataScaleY.min;
this.angle = dataAngle.min;
this.rotation = DegToRad(dataAngle.min);
this.alpha = dataAlpha.min;
this.color = (this.color & 0x00FFFFFF) | (((this.alpha * 0xFF) | 0) << 24); this.color = (this.color & 0x00FFFFFF) | (((this.alpha * 0xFF) | 0) << 24);
this.index = emitter.alive.length; this.index = emitter.alive.length;
@ -197,21 +109,27 @@ var Particle = new Class({
// How far along in life is this particle? (t = 0 to 1) // How far along in life is this particle? (t = 0 to 1)
var t = 1 - (this.lifeCurrent / this.life); var t = 1 - (this.lifeCurrent / this.life);
this.velocityX += (emitter.gravity.x * step); this.velocityX += (emitter.gravityX * step);
this.velocityY += (emitter.gravity.y * step); this.velocityY += (emitter.gravityY * step);
this.x += this.velocityX * step; this.x += this.velocityX * step;
this.y += this.velocityY * step; this.y += this.velocityY * step;
var data = this.data; this.scaleX = emitter.scaleX.onUpdate(this, 'scaleX', t, this.scaleX);
this.scaleX = data.scaleX.calc * emitter.easingFunctionScale(t) + data.scaleX.min; if (emitter.scaleY)
this.scaleY = data.scaleY.calc * emitter.easingFunctionScale(t) + data.scaleY.min; {
this.scaleY = emitter.scaleY.onUpdate(this, 'scaleY', t, this.scaleY);
}
else
{
this.scaleY = this.scaleX;
}
this.angle = data.angle.calc * emitter.easingFunctionRotation(t) + data.angle.min; this.angle = emitter.particleAngle.onUpdate(this, 'angle', t, this.angle);
this.rotation = DegToRad(this.angle); this.rotation = DegToRad(this.angle);
this.alpha = data.alpha.calc * emitter.easingFunctionAlpha(t) + data.alpha.min; this.alpha = emitter.alpha.onUpdate(this, 'alpha', t, this.alpha);
this.color = (this.color & 0x00FFFFFF) | (((this.alpha * 0xFF) | 0) << 24); this.color = (this.color & 0x00FFFFFF) | (((this.alpha * 0xFF) | 0) << 24);

View file

@ -1,12 +1,9 @@
var BlendModes = require('../../renderer/BlendModes'); var BlendModes = require('../../renderer/BlendModes');
var Class = require('../../utils/Class'); var Class = require('../../utils/Class');
var Components = require('../components'); var Components = require('../components');
var GetEaseFunction = require('../../tweens/builder/GetEaseFunction');
var GetRandomElement = require('../../utils/array/GetRandomElement'); var GetRandomElement = require('../../utils/array/GetRandomElement');
var GetValue = require('../../utils/object/GetValue'); var GetValue = require('../../utils/object/GetValue');
var GetFastValue = require('../../utils/object/GetFastValue'); var GetFastValue = require('../../utils/object/GetFastValue');
var MinMax2 = require('../../math/MinMax2');
var MinMax4 = require('../../math/MinMax4');
var Particle = require('./Particle'); var Particle = require('./Particle');
var StableSort = require('../../utils/array/StableSort'); var StableSort = require('../../utils/array/StableSort');
var Vector2 = require('../../math/Vector2'); var Vector2 = require('../../math/Vector2');
@ -45,82 +42,48 @@ var ParticleEmitter = new Class({
// A point emitter will emit particles only in the direction derived from the speed values // A point emitter will emit particles only in the direction derived from the speed values
this.radial = GetFastValue(config, 'radial', true); this.radial = GetFastValue(config, 'radial', true);
// Not a value operation because you should be able to constantly alter this and effect all
// alive particles in real-time, instantly
this.gravityX = GetValue(config, 'gravityX', 0);
this.gravityY = GetValue(config, 'gravityY', 0);
// Value ops
this.speedX = GetValueOp(config, 'speedX', 0); this.speedX = GetValueOp(config, 'speedX', 0);
this.speedY = GetValueOp(config, 'speedY', 0); this.speedY = GetValueOp(config, 'speedY', 0);
if (config.hasOwnProperty('speed'))
{
this.speedX = GetValueOp(config, 'speed', 0);
this.speedY = null;
}
this.scaleX = GetValueOp(config, 'scaleX', 1); this.scaleX = GetValueOp(config, 'scaleX', 1);
this.scaleY = GetValueOp(config, 'scaleY', 1); this.scaleY = GetValueOp(config, 'scaleY', 1);
this.gravityX = GetValueOp(config, 'gravityX', 0); if (config.hasOwnProperty('scale'))
this.gravityY = GetValueOp(config, 'gravityY', 0); {
this.scaleX = GetValueOp(config, 'scale', 1);
this.scaleY = null;
}
this.alpha = GetValueOp(config, 'alpha', 1); this.alpha = GetValueOp(config, 'alpha', 1);
/* this.lifespan = GetValueOp(config, 'lifespan', 1000);
this.x = new MinMax2(GetValue(config, 'x', 0));
this.y = new MinMax2(GetValue(config, 'y', 0));
this.radial = GetValue(config, 'radial', true); this.emitterAngle = GetValueOp(config, 'angle', { min: 0, max: 360, random: true });
this.speedX = new MinMax2(GetValue(config, 'speedX', 0)); this.particleAngle = GetValueOp(config, 'particleAngle', 0);
this.speedY = new MinMax2(GetValue(config, 'speedY', 0));
var speedConfig = GetValue(config, 'speed', null);
if (speedConfig)
{
this.speedX.set(speedConfig);
this.speedY.set(speedConfig);
}
this.scaleX = new MinMax2(GetValue(config, 'scaleX', 1));
this.scaleY = new MinMax2(GetValue(config, 'scaleY', 1));
var scaleConfig = GetValue(config, 'scale', null);
if (scaleConfig)
{
this.scaleX.set(scaleConfig);
this.scaleY.set(scaleConfig);
}
this.gravity = new MinMax2(GetValue(config, 'gravity', 0));
this.alpha = new MinMax2(GetValue(config, 'alpha', 1));
*/
this.emitterAngle = new MinMax2(GetValue(config, 'angle', [ 0, 360 ]));
this.particleAngle = new MinMax2(GetValue(config, 'particleAngle', 0));
// The lifespan of the particles (in ms)
this.lifespan = new MinMax2(GetValue(config, 'lifespan', 1000));
// Random overrides
this.randomScaleX = GetValue(config, 'randomScaleX', null);
this.randomScaleY = GetValue(config, 'randomScaleY', null);
this.randomScale = GetValue(config, 'randomScale', null);
if (this.randomScale)
{
this.randomScaleX = this.randomScale;
this.randomScaleY = this.randomScale;
}
this.randomAlpha = GetValue(config, 'randomAlpha', null);
this.randomAngle = GetValue(config, 'randomAngle', null);
// Callbacks // Callbacks
this.emitCallback = GetValue(config, 'emitCallback', null); this.emitCallback = GetFastValue(config, 'emitCallback', null);
this.emitCallbackScope = GetValue(config, 'emitCallbackScope', null); this.emitCallbackScope = GetFastValue(config, 'emitCallbackScope', null);
this.deathCallback = GetValue(config, 'deathCallback', null); this.deathCallback = GetFastValue(config, 'deathCallback', null);
this.deathCallbackScope = GetValue(config, 'deathCallbackScope', null); this.deathCallbackScope = GetFastValue(config, 'deathCallbackScope', null);
var callbackScope = GetValue(config, 'callbackScope', null) var callbackScope = GetFastValue(config, 'callbackScope', null);
if (callbackScope) if (callbackScope)
{ {
@ -129,46 +92,43 @@ var ParticleEmitter = new Class({
} }
// Set to hard limit the amount of particle objects this emitter is allowed to create // Set to hard limit the amount of particle objects this emitter is allowed to create
this.maxParticles = GetValue(config, 'maxParticles', 0); this.maxParticles = GetFastValue(config, 'maxParticles', 1);
// How many particles are emitted each time the emitter updates // How many particles are emitted each time the emitter updates
this.quantity = GetValue(config, 'quantity', 1); this.quantity = GetFastValue(config, 'quantity', 1);
// How often a particle is emitted in ms (if emitter is a constant / flow emitter) // How often a particle is emitted in ms (if emitter is a constant / flow emitter)
// If emitter is an explosion emitter this value will be -1. // If emitter is an explosion emitter this value will be -1.
// Anything > -1 sets this to be a flow emitter // Anything > -1 sets this to be a flow emitter
this.frequency = GetValue(config, 'frequency', 0); this.frequency = GetFastValue(config, 'frequency', 0);
// Controls if the emitter is currently emitting particles. Already alive particles will continue to update until they expire. // Controls if the emitter is currently emitting particles. Already alive particles will continue to update until they expire.
this.on = GetValue(config, 'on', true); this.on = GetFastValue(config, 'on', true);
// Newly emitted particles are added to the top of the particle list, i.e. rendered above those already alive. Set to false to send them to the back. // Newly emitted particles are added to the top of the particle list, i.e. rendered above those already alive. Set to false to send them to the back.
this.particleBringToTop = GetValue(config, 'particleBringToTop', true); this.particleBringToTop = GetFastValue(config, 'particleBringToTop', true);
this.timeScale = GetValue(config, 'timeScale', 1); this.timeScale = GetFastValue(config, 'timeScale', 1);
// private
this.dead = []; this.dead = [];
this.alive = []; this.alive = [];
this._counter = 0; this._counter = 0;
// Optional Particle emission zone - must be an object that supports a `getRandomPoint` function, such as a Rectangle, Circle, Path, etc. // Optional Particle emission zone - must be an object that supports a `getRandomPoint` function, such as a Rectangle, Circle, Path, etc.
this.zone = GetValue(config, 'zone', null); this.zone = GetFastValue(config, 'zone', null);
this.active = GetValue(config, 'active', true); this.active = GetFastValue(config, 'active', true);
this.visible = GetValue(config, 'visible', true); this.visible = GetFastValue(config, 'visible', true);
this.blendMode = GetValue(config, 'blendMode', BlendModes.NORMAL); this.blendMode = GetFastValue(config, 'blendMode', BlendModes.NORMAL);
this.easingFunctionAlpha = GetEaseFunction(GetValue(config, 'alphaEase', 'Linear')); this.follow = GetFastValue(config, 'follow', null);
this.easingFunctionScale = GetEaseFunction(GetValue(config, 'scaleEase', 'Linear')); this.followOffset = new Vector2(GetFastValue(config, 'followOffset', 0));
this.easingFunctionRotation = GetEaseFunction(GetValue(config, 'rotationEase', 'Linear')); this.trackVisible = GetFastValue(config, 'trackVisible', false);
this.follow = GetValue(config, 'follow', null); var frame = GetFastValue(config, 'frame', null);
this.followOffset = new Vector2(GetValue(config, 'followOffset', 0));
this.trackVisible = GetValue(config, 'trackVisible', false);
var frame = GetValue(config, 'frame', null);
if (frame) if (frame)
{ {
@ -176,6 +136,16 @@ var ParticleEmitter = new Class({
} }
}, },
fromJSON: function (config)
{
},
toJSON: function ()
{
},
startFollow: function (target, offsetX, offsetY, trackVisible) startFollow: function (target, offsetX, offsetY, trackVisible)
{ {
if (offsetX === undefined) { offsetX = 0; } if (offsetX === undefined) { offsetX = 0; }
@ -227,6 +197,7 @@ var ParticleEmitter = new Class({
return this; return this;
}, },
/*
setPosition: function (x, y) setPosition: function (x, y)
{ {
this.x = x; this.x = x;
@ -235,38 +206,6 @@ var ParticleEmitter = new Class({
return this; return this;
}, },
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;
},
// Particle Emission // Particle Emission
setSpeed: function (xMin, xMax, yMin, yMax) setSpeed: function (xMin, xMax, yMin, yMax)
@ -317,6 +256,7 @@ var ParticleEmitter = new Class({
return this; return this;
}, },
*/
setQuantity: function (quantity) setQuantity: function (quantity)
{ {
@ -536,6 +476,7 @@ var ParticleEmitter = new Class({
// Store emitter coordinates in-case this was a placement explode, or emitAt // Store emitter coordinates in-case this was a placement explode, or emitAt
/*
var oldX = this.x; var oldX = this.x;
var oldY = this.y; var oldY = this.y;
@ -548,6 +489,7 @@ var ParticleEmitter = new Class({
{ {
this.y = y; this.y = y;
} }
*/
var dead = this.dead; var dead = this.dead;
@ -586,8 +528,10 @@ var ParticleEmitter = new Class({
} }
} }
/*
this.x = oldX; this.x = oldX;
this.y = oldY; this.y = oldY;
*/
return particle; return particle;
}, },