From 891666e3cd42d572d369aea37fa23b08fb5bec16 Mon Sep 17 00:00:00 2001 From: Richard Davey Date: Tue, 10 Jan 2023 21:23:44 +0000 Subject: [PATCH] Removed ParticleEmitterManager and merged changes into ParticleEmitter --- src/gameobjects/index.js | 4 +- src/gameobjects/particles/Particle.js | 6 +- src/gameobjects/particles/Particle2.js | 788 ---- src/gameobjects/particles/ParticleEmitter.js | 515 ++- src/gameobjects/particles/ParticleEmitter2.js | 3762 ----------------- ...er.js => ParticleEmitterCanvasRenderer.js} | 112 +- ...erCreator.js => ParticleEmitterCreator.js} | 8 +- .../particles/ParticleEmitterFactory.js | 26 + .../particles/ParticleEmitterManager.js | 637 --- .../particles/ParticleEmitterRender.js | 2 +- .../particles/ParticleEmitterWebGLRenderer.js | 6 +- .../particles/ParticleManagerFactory.js | 33 - .../particles/ParticleManagerRender.js | 26 - .../particles/ParticleManagerWebGLRenderer.js | 149 - src/gameobjects/particles/index.js | 1 - 15 files changed, 398 insertions(+), 5677 deletions(-) delete mode 100644 src/gameobjects/particles/Particle2.js delete mode 100644 src/gameobjects/particles/ParticleEmitter2.js rename src/gameobjects/particles/{ParticleManagerCanvasRenderer.js => ParticleEmitterCanvasRenderer.js} (58%) rename src/gameobjects/particles/{ParticleManagerCreator.js => ParticleEmitterCreator.js} (82%) create mode 100644 src/gameobjects/particles/ParticleEmitterFactory.js delete mode 100644 src/gameobjects/particles/ParticleEmitterManager.js delete mode 100644 src/gameobjects/particles/ParticleManagerFactory.js delete mode 100644 src/gameobjects/particles/ParticleManagerRender.js delete mode 100644 src/gameobjects/particles/ParticleManagerWebGLRenderer.js diff --git a/src/gameobjects/index.js b/src/gameobjects/index.js index c3fb80f7b..761d17965 100644 --- a/src/gameobjects/index.js +++ b/src/gameobjects/index.js @@ -77,7 +77,7 @@ var GameObjects = { Group: require('./group/GroupFactory'), Image: require('./image/ImageFactory'), Layer: require('./layer/LayerFactory'), - Particles: require('./particles/ParticleManagerFactory'), + Particles: require('./particles/ParticleEmitterFactory'), PathFollower: require('./pathfollower/PathFollowerFactory'), RenderTexture: require('./rendertexture/RenderTextureFactory'), Rope: require('./rope/RopeFactory'), @@ -110,7 +110,7 @@ var GameObjects = { Group: require('./group/GroupCreator'), Image: require('./image/ImageCreator'), Layer: require('./layer/LayerCreator'), - Particles: require('./particles/ParticleManagerCreator'), + Particles: require('./particles/ParticleEmitterCreator'), RenderTexture: require('./rendertexture/RenderTextureCreator'), Rope: require('./rope/RopeCreator'), Sprite: require('./sprite/SpriteCreator'), diff --git a/src/gameobjects/particles/Particle.js b/src/gameobjects/particles/Particle.js index b8151dd0b..5e1a6566b 100644 --- a/src/gameobjects/particles/Particle.js +++ b/src/gameobjects/particles/Particle.js @@ -27,7 +27,7 @@ var Particle = new Class({ initialize: - function Particle (emitter) + function Particle2 (emitter) { /** * The Emitter to which this Particle belongs. @@ -297,7 +297,7 @@ var Particle = new Class({ * @type {Phaser.Scene} * @since 3.60.0 */ - this.scene = emitter.manager.scene; + this.scene = emitter.scene; /** * The Animation State component of this Particle. @@ -342,7 +342,7 @@ var Particle = new Class({ */ emit: function (event, a1, a2, a3, a4, a5) { - return this.emitter.manager.emit(event, a1, a2, a3, a4, a5); + return this.emitter.emit(event, a1, a2, a3, a4, a5); }, /** diff --git a/src/gameobjects/particles/Particle2.js b/src/gameobjects/particles/Particle2.js deleted file mode 100644 index 6cf855167..000000000 --- a/src/gameobjects/particles/Particle2.js +++ /dev/null @@ -1,788 +0,0 @@ -/** - * @author Richard Davey - * @copyright 2013-2023 Photon Storm Ltd. - * @license {@link https://opensource.org/licenses/MIT|MIT License} - */ - -var AnimationState = require('../../animations/AnimationState'); -var Class = require('../../utils/Class'); -var DegToRad = require('../../math/DegToRad'); -var Rectangle = require('../../geom/rectangle/Rectangle'); -var RotateAround = require('../../math/RotateAround'); -var Vector2 = require('../../math/Vector2'); - -/** - * @classdesc - * A Particle is a simple Game Object controlled by a Particle Emitter and Manager, and rendered by the Manager. - * It uses its own lightweight physics system, and can interact only with its Emitter's bounds and zones. - * - * @class Particle - * @memberof Phaser.GameObjects.Particles - * @constructor - * @since 3.0.0 - * - * @param {Phaser.GameObjects.Particles.ParticleEmitter} emitter - The Emitter to which this Particle belongs. - */ -var Particle2 = new Class({ - - initialize: - - function Particle2 (emitter) - { - /** - * The Emitter to which this Particle belongs. - * - * A Particle can only belong to a single Emitter and is created, updated and destroyed via it. - * - * @name Phaser.GameObjects.Particles.Particle#emitter - * @type {Phaser.GameObjects.Particles.ParticleEmitter} - * @since 3.0.0 - */ - this.emitter = emitter; - - /** - * The texture used to render this Particle. - * - * @name Phaser.GameObjects.Particles.Particle#texture - * @type {Phaser.Textures.Texture} - * @default null - * @since 3.60.0 - */ - this.texture = null; - - /** - * The texture frame used to render this Particle. - * - * @name Phaser.GameObjects.Particles.Particle#frame - * @type {Phaser.Textures.Frame} - * @default null - * @since 3.0.0 - */ - this.frame = null; - - /** - * The x coordinate of this Particle. - * - * @name Phaser.GameObjects.Particles.Particle#x - * @type {number} - * @default 0 - * @since 3.0.0 - */ - this.x = 0; - - /** - * The y coordinate of this Particle. - * - * @name Phaser.GameObjects.Particles.Particle#y - * @type {number} - * @default 0 - * @since 3.0.0 - */ - this.y = 0; - - /** - * The x velocity of this Particle. - * - * @name Phaser.GameObjects.Particles.Particle#velocityX - * @type {number} - * @default 0 - * @since 3.0.0 - */ - this.velocityX = 0; - - /** - * The y velocity of this Particle. - * - * @name Phaser.GameObjects.Particles.Particle#velocityY - * @type {number} - * @default 0 - * @since 3.0.0 - */ - this.velocityY = 0; - - /** - * The x acceleration of this Particle. - * - * @name Phaser.GameObjects.Particles.Particle#accelerationX - * @type {number} - * @default 0 - * @since 3.0.0 - */ - this.accelerationX = 0; - - /** - * The y acceleration of this Particle. - * - * @name Phaser.GameObjects.Particles.Particle#accelerationY - * @type {number} - * @default 0 - * @since 3.0.0 - */ - this.accelerationY = 0; - - /** - * The maximum horizontal velocity this Particle can travel at. - * - * @name Phaser.GameObjects.Particles.Particle#maxVelocityX - * @type {number} - * @default 10000 - * @since 3.0.0 - */ - this.maxVelocityX = 10000; - - /** - * The maximum vertical velocity this Particle can travel at. - * - * @name Phaser.GameObjects.Particles.Particle#maxVelocityY - * @type {number} - * @default 10000 - * @since 3.0.0 - */ - this.maxVelocityY = 10000; - - /** - * The bounciness, or restitution, of this Particle. - * - * @name Phaser.GameObjects.Particles.Particle#bounce - * @type {number} - * @default 0 - * @since 3.0.0 - */ - this.bounce = 0; - - /** - * The horizontal scale of this Particle. - * - * @name Phaser.GameObjects.Particles.Particle#scaleX - * @type {number} - * @default 1 - * @since 3.0.0 - */ - this.scaleX = 1; - - /** - * The vertical scale of this Particle. - * - * @name Phaser.GameObjects.Particles.Particle#scaleY - * @type {number} - * @default 1 - * @since 3.0.0 - */ - this.scaleY = 1; - - /** - * The alpha value of this Particle. - * - * @name Phaser.GameObjects.Particles.Particle#alpha - * @type {number} - * @default 1 - * @since 3.0.0 - */ - this.alpha = 1; - - /** - * The angle of this Particle in degrees. - * - * @name Phaser.GameObjects.Particles.Particle#angle - * @type {number} - * @default 0 - * @since 3.0.0 - */ - this.angle = 0; - - /** - * The angle of this Particle in radians. - * - * @name Phaser.GameObjects.Particles.Particle#rotation - * @type {number} - * @default 0 - * @since 3.0.0 - */ - this.rotation = 0; - - /** - * The tint applied to this Particle. - * - * @name Phaser.GameObjects.Particles.Particle#tint - * @type {number} - * @webglOnly - * @since 3.0.0 - */ - this.tint = 0xffffff; - - /** - * The lifespan of this Particle in ms. - * - * @name Phaser.GameObjects.Particles.Particle#life - * @type {number} - * @default 1000 - * @since 3.0.0 - */ - this.life = 1000; - - /** - * The current life of this Particle in ms. - * - * @name Phaser.GameObjects.Particles.Particle#lifeCurrent - * @type {number} - * @default 1000 - * @since 3.0.0 - */ - this.lifeCurrent = 1000; - - /** - * The delay applied to this Particle upon emission, in ms. - * - * @name Phaser.GameObjects.Particles.Particle#delayCurrent - * @type {number} - * @default 0 - * @since 3.0.0 - */ - this.delayCurrent = 0; - - /** - * The normalized lifespan T value, where 0 is the start and 1 is the end. - * - * @name Phaser.GameObjects.Particles.Particle#lifeT - * @type {number} - * @default 0 - * @since 3.0.0 - */ - this.lifeT = 0; - - /** - * The data used by the ease equation. - * - * @name Phaser.GameObjects.Particles.Particle#data - * @type {object} - * @since 3.0.0 - */ - this.data = { - tint: { min: 0xffffff, max: 0xffffff }, - alpha: { min: 1, max: 1 }, - rotate: { min: 0, max: 0 }, - scaleX: { min: 1, max: 1 }, - scaleY: { min: 1, max: 1 }, - x: { min: 0, max: 0 }, - y: { min: 0, max: 0 }, - accelerationX: { min: 0, max: 0 }, - accelerationY: { min: 0, max: 0 }, - maxVelocityX: { min: 0, max: 0 }, - maxVelocityY: { min: 0, max: 0 }, - moveToX: { min: 0, max: 0 }, - moveToY: { min: 0, max: 0 }, - bounce: { min: 0, max: 0 } - }; - - /** - * Interal private value. - * - * @name Phaser.GameObjects.Particles.Particle#isCropped - * @type {boolean} - * @private - * @readonly - * @since 3.60.0 - */ - this.isCropped = false; - - /** - * A reference to the Scene to which this Game Object belongs. - * - * Game Objects can only belong to one Scene. - * - * You should consider this property as being read-only. You cannot move a - * Game Object to another Scene by simply changing it. - * - * @name Phaser.GameObjects.Particles.Particle#scene - * @type {Phaser.Scene} - * @since 3.60.0 - */ - this.scene = emitter.scene; - - /** - * The Animation State component of this Particle. - * - * This component provides features to apply animations to this Particle. - * It is responsible for playing, loading, queuing animations for later playback, - * mixing between animations and setting the current animation frame to this Particle. - * - * @name Phaser.GameObjects.Particles.Particle#anims - * @type {Phaser.Animations.AnimationState} - * @since 3.60.0 - */ - this.anims = new AnimationState(this); - - /** - * A rectangle that holds the bounds of this Particle after a call to - * the `Particle.getBounds` method has been made. - * - * @name Phaser.GameObjects.Particles.Particle#bounds - * @type {Phaser.Geom.Rectangle} - * @since 3.60.0 - */ - this.bounds = new Rectangle(); - }, - - /** - * The Event Emitter proxy. - * - * Passes on all parameters to the `ParticleEmitterManager` to emit directly. - * - * @method Phaser.GameObjects.Particles.Particle#emit - * @since 3.60.0 - * - * @param {(string|Symbol)} event - The event name. - * @param {any} [a1] - Optional argument 1. - * @param {any} [a2] - Optional argument 2. - * @param {any} [a3] - Optional argument 3. - * @param {any} [a4] - Optional argument 4. - * @param {any} [a5] - Optional argument 5. - * - * @return {boolean} `true` if the event had listeners, else `false`. - */ - emit: function (event, a1, a2, a3, a4, a5) - { - return this.emitter.emit(event, a1, a2, a3, a4, a5); - }, - - /** - * Checks to see if this Particle is alive and updating. - * - * @method Phaser.GameObjects.Particles.Particle#isAlive - * @since 3.0.0 - * - * @return {boolean} `true` if this Particle is alive and updating, otherwise `false`. - */ - isAlive: function () - { - return (this.lifeCurrent > 0); - }, - - /** - * Kills this particle. This sets the `lifeCurrent` value to 0, which forces - * the Particle to be removed the next time its parent Emitter runs an update. - * - * @method Phaser.GameObjects.Particles.Particle#kill - * @since 3.60.0 - */ - kill: function () - { - this.lifeCurrent = 0; - }, - - /** - * Sets the position of this particle to the given x/y coordinates. - * - * If the parameters are left undefined, it resets the particle back to 0x0. - * - * @method Phaser.GameObjects.Particles.Particle#setPosition - * @since 3.60.0 - * - * @param {number} [x=0] - The x coordinate to set this Particle to. - * @param {number} [y=0] - The y coordinate to set this Particle to. - */ - setPosition: function (x, y) - { - if (x === undefined) { x = 0; } - if (y === undefined) { y = 0; } - - this.x = x; - this.y = y; - }, - - /** - * Starts this Particle from the given coordinates. - * - * @method Phaser.GameObjects.Particles.Particle#fire - * @since 3.0.0 - * - * @param {number} [x] - The x coordinate to launch this Particle from. - * @param {number} [y] - The y coordinate to launch this Particle from. - */ - fire: function (x, y) - { - var emitter = this.emitter; - var ops = emitter.ops; - - var anim = emitter.getAnim(); - - if (anim) - { - this.anims.play(anim); - } - else - { - this.frame = emitter.getFrame(); - this.texture = this.frame.texture; - } - - if (!this.frame) - { - throw new Error('Particle has no texture frame'); - } - - // Updates particle.x and particle.y during this call - emitter.getEmitZone(this); - - if (x === undefined) - { - this.x += ops.x.onEmit(this, 'x'); - } - else if (ops.x.steps > 0) - { - // EmitterOp is stepped but x was forced (follower?) so use it - this.x += x + ops.x.onEmit(this, 'x'); - } - else - { - this.x += x; - } - - if (y === undefined) - { - this.y += ops.y.onEmit(this, 'y'); - } - else if (ops.y.steps > 0) - { - // EmitterOp is stepped but y was forced (follower?) so use it - this.y += y + ops.y.onEmit(this, 'y'); - } - else - { - this.y += y; - } - - this.life = ops.lifespan.onEmit(this, 'lifespan'); - this.lifeCurrent = this.life; - this.lifeT = 0; - - var sx = ops.speedX.onEmit(this, 'speedX'); - var sy = (ops.speedY.active) ? ops.speedY.onEmit(this, 'speedY') : sx; - - if (emitter.radial) - { - var rad = DegToRad(ops.angle.onEmit(this, 'angle')); - - this.velocityX = Math.cos(rad) * Math.abs(sx); - this.velocityY = Math.sin(rad) * Math.abs(sy); - } - else if (emitter.moveTo) - { - var mx = ops.moveToX.onEmit(this, 'moveToX'); - var my = ops.moveToY.onEmit(this, 'moveToY'); - var lifeS = this.life / 1000; - - this.velocityX = (mx - this.x) / lifeS; - this.velocityY = (my - this.y) / lifeS; - } - else - { - this.velocityX = sx; - this.velocityY = sy; - } - - if (emitter.acceleration) - { - this.accelerationX = ops.accelerationX.onEmit(this, 'accelerationX'); - this.accelerationY = ops.accelerationY.onEmit(this, 'accelerationY'); - } - - this.maxVelocityX = ops.maxVelocityX.onEmit(this, 'maxVelocityX'); - this.maxVelocityY = ops.maxVelocityY.onEmit(this, 'maxVelocityY'); - - this.delayCurrent = ops.delay.onEmit(this, 'delay'); - - this.scaleX = ops.scaleX.onEmit(this, 'scaleX'); - this.scaleY = (ops.scaleY.active) ? ops.scaleY.onEmit(this, 'scaleY') : this.scaleX; - - this.angle = ops.rotate.onEmit(this, 'rotate'); - this.rotation = DegToRad(this.angle); - - this.bounce = ops.bounce.onEmit(this, 'bounce'); - - this.alpha = ops.alpha.onEmit(this, 'alpha'); - - this.tint = ops.tint.onEmit(this, 'tint'); - }, - - /** - * An internal method that calculates the velocity of the Particle. - * - * @method Phaser.GameObjects.Particles.Particle#computeVelocity - * @since 3.0.0 - * - * @param {Phaser.GameObjects.Particles.ParticleEmitter} emitter - The Emitter that is updating this Particle. - * @param {number} delta - The delta time in ms. - * @param {number} step - The delta value divided by 1000. - * @param {Phaser.GameObjects.Particles.ParticleProcessor[]} processors - An array of all active Particle Processors. - * @param {number} t - The current normalized lifetime of the particle, between 0 (birth) and 1 (death). - */ - computeVelocity: function (emitter, delta, step, processors, t) - { - var ops = emitter.ops; - - var vx = this.velocityX; - var vy = this.velocityY; - - var ax = ops.accelerationX.onUpdate(this, 'accelerationX', t, this.accelerationX); - var ay = ops.accelerationY.onUpdate(this, 'accelerationY', t, this.accelerationY); - - var mx = ops.maxVelocityX.onUpdate(this, 'maxVelocityX', t, this.maxVelocityX); - var my = ops.maxVelocityY.onUpdate(this, 'maxVelocityY', t, this.maxVelocityY); - - vx += (emitter.gravityX * step); - vy += (emitter.gravityY * step); - - if (ax) - { - vx += (ax * step); - } - - if (ay) - { - vy += (ay * step); - } - - if (vx > mx) - { - vx = mx; - } - else if (vx < -mx) - { - vx = -mx; - } - - if (vy > my) - { - vy = my; - } - else if (vy < -my) - { - vy = -my; - } - - this.velocityX = vx; - this.velocityY = vy; - - // Apply any additional processors - for (var i = 0; i < processors.length; i++) - { - processors[i].update(this, delta, step, t); - } - }, - - /** - * Checks if this Particle is still within the bounds defined by the given Emitter. - * - * If not, and depending on the Emitter collision flags, the Particle may either stop or rebound. - * - * @method Phaser.GameObjects.Particles.Particle#checkBounds - * @since 3.0.0 - * - * @param {Phaser.GameObjects.Particles.ParticleEmitter} emitter - The Emitter to check the bounds against. - */ - checkBounds: function (emitter) - { - var bounds = emitter.bounds; - var bounce = -this.bounce; - - if (this.x < bounds.x && emitter.collideLeft) - { - this.x = bounds.x; - this.velocityX *= bounce; - } - else if (this.x > bounds.right && emitter.collideRight) - { - this.x = bounds.right; - this.velocityX *= bounce; - } - - if (this.y < bounds.y && emitter.collideTop) - { - this.y = bounds.y; - this.velocityY *= bounce; - } - else if (this.y > bounds.bottom && emitter.collideBottom) - { - this.y = bounds.bottom; - this.velocityY *= bounce; - } - }, - - /** - * The main update method for this Particle. - * - * Updates its life values, computes the velocity and repositions the Particle. - * - * @method Phaser.GameObjects.Particles.Particle#update - * @since 3.0.0 - * - * @param {number} delta - The delta time in ms. - * @param {number} step - The delta value divided by 1000. - * @param {Phaser.GameObjects.Particles.ParticleProcessor[]} processors - An array of all active Particle Processors. - * - * @return {boolean} Returns `true` if this Particle has now expired and should be removed, otherwise `false` if still active. - */ - update: function (delta, step, processors) - { - if (this.lifeCurrent === 0) - { - // Particle is dead via `Particle.kill` method. - return true; - } - - if (this.delayCurrent > 0) - { - this.delayCurrent -= delta; - - return false; - } - - this.anims.update(0, delta); - - var emitter = this.emitter; - var ops = emitter.ops; - - // How far along in life is this particle? (t = 0 to 1) - var t = 1 - (this.lifeCurrent / this.life); - - this.lifeT = t; - - this.x = ops.x.onUpdate(this, 'x', t, this.x); - this.y = ops.y.onUpdate(this, 'y', t, this.y); - - if (emitter.moveTo) - { - var mx = ops.moveToX.onUpdate(this, 'moveToX', t, emitter.moveToX); - var my = ops.moveToY.onUpdate(this, 'moveToY', t, emitter.moveToY); - var lifeS = this.lifeCurrent / 1000; - - this.velocityX = (mx - this.x) / lifeS; - this.velocityY = (my - this.y) / lifeS; - } - - this.computeVelocity(emitter, delta, step, processors, t); - - this.x += this.velocityX * step; - this.y += this.velocityY * step; - - if (emitter.bounds) - { - this.checkBounds(emitter); - } - - if (emitter.getDeathZone(this)) - { - this.lifeCurrent = 0; - - // No need to go any further, particle has been killed - return true; - } - - this.scaleX = ops.scaleX.onUpdate(this, 'scaleX', t, this.scaleX); - this.scaleY = this.scaleX; - - if (ops.scaleY.active) - { - this.scaleY = ops.scaleY.onUpdate(this, 'scaleY', t, this.scaleY); - } - - this.angle = ops.rotate.onUpdate(this, 'rotate', t, this.angle); - this.rotation = DegToRad(this.angle); - - this.alpha = ops.alpha.onUpdate(this, 'alpha', t, this.alpha); - - this.tint = ops.tint.onUpdate(this, 'tint', t, this.tint); - - this.bounce = ops.bounce.onUpdate(this, 'bounce', t, this.bounce); - - this.lifeCurrent -= delta; - - return (this.lifeCurrent <= 0); - }, - - /** - * This is a NOOP method and does nothing when called. - * - * @method Phaser.GameObjects.Particles.Particle#setSizeToFrame - * @since 3.60.0 - */ - setSizeToFrame: function () - { - // NOOP - }, - - /** - * Gets the bounds of this particle as a Geometry Rectangle, factoring in any - * transforms of the parent emitter and anything else above it in the display list. - * - * Once calculated the bounds can be accessed via the `Particle.bounds` property. - * - * @method Phaser.GameObjects.Particles.Particle#getBounds - * @since 3.60.0 - * - * @param {Phaser.GameObjects.Components.TransformMatrix} [matrix] - Optional transform matrix to apply to this particle. - * - * @return {Phaser.Geom.Rectangle} A Rectangle containing the transformed bounds of this particle. - */ - getBounds: function (matrix) - { - if (matrix === undefined) { matrix = this.emitter.getWorldTransformMatrix(); } - - var sx = Math.abs(matrix.scaleX) * this.scaleX; - var sy = Math.abs(matrix.scaleY) * this.scaleY; - - var x = this.x; - var y = this.y; - var rotation = this.rotation; - var width = (this.frame.width * sx) / 2; - var height = (this.frame.height * sy) / 2; - - var bounds = this.bounds; - - var topLeft = new Vector2(x - width, y - height); - var topRight = new Vector2(x + width, y - height); - var bottomLeft = new Vector2(x - width, y + height); - var bottomRight = new Vector2(x + width, y + height); - - if (rotation !== 0) - { - RotateAround(topLeft, x, y, rotation); - RotateAround(topRight, x, y, rotation); - RotateAround(bottomLeft, x, y, rotation); - RotateAround(bottomRight, x, y, rotation); - } - - matrix.transformPoint(topLeft.x, topLeft.y, topLeft); - matrix.transformPoint(topRight.x, topRight.y, topRight); - matrix.transformPoint(bottomLeft.x, bottomLeft.y, bottomLeft); - matrix.transformPoint(bottomRight.x, bottomRight.y, bottomRight); - - bounds.x = Math.min(topLeft.x, topRight.x, bottomLeft.x, bottomRight.x); - bounds.y = Math.min(topLeft.y, topRight.y, bottomLeft.y, bottomRight.y); - bounds.width = Math.max(topLeft.x, topRight.x, bottomLeft.x, bottomRight.x) - bounds.x; - bounds.height = Math.max(topLeft.y, topRight.y, bottomLeft.y, bottomRight.y) - bounds.y; - - return bounds; - }, - - /** - * Destroys this Particle. - * - * @method Phaser.GameObjects.Particles.Particle#destroy - * @since 3.60.0 - */ - destroy: function () - { - this.anims.destroy(); - - this.anims = null; - this.emitter = null; - this.texture = null; - this.frame = null; - this.scene = null; - } - -}); - -module.exports = Particle2; diff --git a/src/gameobjects/particles/ParticleEmitter.js b/src/gameobjects/particles/ParticleEmitter.js index 8242b2b5f..9c22db2ea 100644 --- a/src/gameobjects/particles/ParticleEmitter.js +++ b/src/gameobjects/particles/ParticleEmitter.js @@ -5,7 +5,6 @@ */ var Add = require('../../utils/array/Add'); -var BlendModes = require('../../renderer/BlendModes'); var Class = require('../../utils/Class'); var Components = require('../components'); var CopyFrom = require('../../geom/rectangle/CopyFrom'); @@ -13,17 +12,21 @@ var DeathZone = require('./zones/DeathZone'); var EdgeZone = require('./zones/EdgeZone'); var EmitterOp = require('./EmitterOp'); var Events = require('./events'); +var GameObject = require('../GameObject'); var GetFastValue = require('../../utils/object/GetFastValue'); var GetRandom = require('../../utils/array/GetRandom'); +var GravityWell = require('./GravityWell'); var HasAny = require('../../utils/object/HasAny'); var HasValue = require('../../utils/object/HasValue'); var Inflate = require('../../geom/rectangle/Inflate'); +var List = require('../../structs/List'); var MergeRect = require('../../geom/rectangle/MergeRect'); var Particle = require('./Particle'); var RandomZone = require('./zones/RandomZone'); var Rectangle = require('../../geom/rectangle/Rectangle'); var RectangleToRectangle = require('../../geom/intersects/RectangleToRectangle'); var Remove = require('../../utils/array/Remove'); +var Render = require('./ParticleEmitterRender'); var StableSort = require('../../utils/array/StableSort'); var TransformMatrix = require('../components/TransformMatrix'); var Vector2 = require('../../math/Vector2'); @@ -54,7 +57,7 @@ var configFastMap = [ 'maxAliveParticles', 'maxParticles', 'name', - 'on', + 'emitting', 'particleBringToTop', 'particleClass', 'radial', @@ -97,10 +100,11 @@ var configOpMap = [ /** * @classdesc - * A particle emitter represents a single particle stream. + * A Particle Emitter is a special kind of Game Object that manages a single stream of Particles. + * * It controls a pool of {@link Phaser.GameObjects.Particles.Particle Particles} and is controlled by a {@link Phaser.GameObjects.Particles.ParticleEmitterManager Particle Emitter Manager}. * - * Lots of emitter properties can be specified in a variety of formats, giving you lots + * Lots of emitter properties can be specified in a variety of formats, giving you plenty * of control over the values they return. Here are the different variations: * * ## An explicit static value: @@ -276,78 +280,47 @@ var configOpMap = [ * control over how your particles behave. * * @class ParticleEmitter + * @extends Phaser.GameObjects.GameObject * @memberof Phaser.GameObjects.Particles * @constructor - * @since 3.0.0 + * @since 3.60.0 * + * @extends Phaser.GameObjects.Components.AlphaSingle * @extends Phaser.GameObjects.Components.BlendMode + * @extends Phaser.GameObjects.Components.Depth + * @extends Phaser.GameObjects.Components.FX * @extends Phaser.GameObjects.Components.Mask + * @extends Phaser.GameObjects.Components.Pipeline * @extends Phaser.GameObjects.Components.ScrollFactor + * @extends Phaser.GameObjects.Components.Texture + * @extends Phaser.GameObjects.Components.Transform * @extends Phaser.GameObjects.Components.Visible * - * @param {Phaser.GameObjects.Particles.ParticleEmitterManager} manager - The Emitter Manager this Emitter belongs to. * @param {Phaser.Types.GameObjects.Particles.ParticleEmitterConfig} config - Settings for this emitter. */ var ParticleEmitter = new Class({ + Extends: GameObject, + Mixins: [ + Components.AlphaSingle, Components.BlendMode, + Components.Depth, + Components.FX, Components.Mask, + Components.Pipeline, Components.ScrollFactor, - Components.Visible + Components.Texture, + Components.Transform, + Components.Visible, + Render ], initialize: - function ParticleEmitter (manager, config) + function ParticleEmitter (scene, texture, config) { - /** - * The Emitter Manager this Emitter belongs to. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#manager - * @type {Phaser.GameObjects.Particles.ParticleEmitterManager} - * @since 3.0.0 - */ - this.manager = manager; - - /** - * The texture assigned to particles. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#texture - * @type {Phaser.Textures.Texture} - * @since 3.0.0 - */ - this.texture = manager.texture; - - /** - * The texture frames assigned to particles. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#frames - * @type {Phaser.Textures.Frame[]} - * @since 3.0.0 - */ - this.frames = [ manager.defaultFrame ]; - - /** - * The default texture frame assigned to particles. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#defaultFrame - * @type {Phaser.Textures.Frame} - * @since 3.0.0 - */ - this.defaultFrame = manager.defaultFrame; - - /** - * The name of this Particle Emitter. - * - * Empty by default and never populated by Phaser, this is left for developers to use. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#name - * @type {string} - * @default '' - * @since 3.0.0 - */ - this.name = ''; + GameObject.call(this, scene, 'ParticleEmitter'); /** * The Particle Class which will be emitted by this Emitter. @@ -589,7 +562,7 @@ var ParticleEmitter = new Class({ * @default true * @since 3.0.0 */ - this.on = true; + this.emitting = true; /** * Newly emitted particles are added to the top of the particle list, i.e. rendered above those already alive. @@ -720,40 +693,6 @@ var ParticleEmitter = new Class({ */ this.collideBottom = true; - /** - * Whether this emitter updates itself and its particles. - * - * Controlled by {@link Phaser.GameObjects.Particles.ParticleEmitter#pause} - * and {@link Phaser.GameObjects.Particles.ParticleEmitter#resume}. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#active - * @type {boolean} - * @default true - * @since 3.0.0 - */ - this.active = true; - - /** - * Set this to false to hide any active particles. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#visible - * @type {boolean} - * @default true - * @since 3.0.0 - * @see Phaser.GameObjects.Particles.ParticleEmitter#setVisible - */ - this.visible = true; - - /** - * The blend mode of this emitter's particles. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#blendMode - * @type {number} - * @since 3.0.0 - * @see Phaser.GameObjects.Particles.ParticleEmitter#setBlendMode - */ - this.blendMode = BlendModes.NORMAL; - /** * A Game Object whose position is used as the particle origin. * @@ -789,15 +728,13 @@ var ParticleEmitter = new Class({ this.trackVisible = false; /** - * The current texture frame, as an index of {@link Phaser.GameObjects.Particles.ParticleEmitter#frames}. + * The texture frames assigned to particles. * - * @name Phaser.GameObjects.Particles.ParticleEmitter#currentFrame - * @type {number} - * @default 0 + * @name Phaser.GameObjects.Particles.ParticleEmitter#frames + * @type {Phaser.Textures.Frame[]} * @since 3.0.0 - * @see Phaser.GameObjects.Particles.ParticleEmitter#setFrame */ - this.currentFrame = 0; + this.frames = []; /** * Whether texture {@link Phaser.GameObjects.Particles.ParticleEmitter#frames} are selected at random. @@ -830,26 +767,6 @@ var ParticleEmitter = new Class({ */ this.anims = []; - /** - * The default animation assigned to particles. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#defaultAnim - * @type {string} - * @since 3.60.0 - */ - this.defaultAnim = null; - - /** - * The current animation, as an index of {@link Phaser.GameObjects.Particles.ParticleEmitter#anims}. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#currentAnim - * @type {number} - * @default 0 - * @since 3.60.0 - * @see Phaser.GameObjects.Particles.ParticleEmitter#setAnim - */ - this.currentAnim = 0; - /** * Whether animations {@link Phaser.GameObjects.Particles.ParticleEmitter#anims} are selected at random. * @@ -903,13 +820,15 @@ var ParticleEmitter = new Class({ * 5 - _completeFlag - Has the COMPLETE event been emitted? * 6 - _emitIndex - The emit zone index counter. * 7 - _zoneTotal - The emit zone total counter. + * 8 - currentFrame - The current texture frame, as an index of {@link Phaser.GameObjects.Particles.ParticleEmitter#frames}. + * 9 - currentAnim - The current animation, as an index of {@link Phaser.GameObjects.Particles.ParticleEmitter#anims}. * * @name Phaser.GameObjects.Particles.ParticleEmitter#counters * @type {Float32Array} * @private * @since 3.60.0 */ - this.counters = new Float32Array(8); + this.counters = new Float32Array(10); /** * Cached amount of frames in the `ParticleEmitter.frames` array. @@ -996,9 +915,47 @@ var ParticleEmitter = new Class({ */ this.sortCallback = this.depthSortCallback; + /** + * A list of Particle Processors being managed by this Emitter Manager. + * + * @name Phaser.GameObjects.Particles.ParticleEmitterManager#processors + * @type {Phaser.Structs.List.} + * @since 3.0.0 + */ + this.processors = new List(this); + + this.setTexture(texture); + + this.initPipeline(); + this.fromJSON(config); }, + // Overrides Game Object method + addedToScene: function () + { + this.scene.sys.updateList.add(this); + }, + + // Overrides Game Object method + removedFromScene: function () + { + this.scene.sys.updateList.remove(this); + }, + + /** + * Gets all active Particle Processors. + * + * @method Phaser.GameObjects.Particles.ParticleEmitterManager#getProcessors + * @since 3.0.0 + * + * @return {Phaser.GameObjects.Particles.ParticleProcessor[]} - An array of active Particle Processors. + */ + getProcessors: function () + { + return this.processors.getAll('active', true); + }, + /** * Merges configuration settings into the emitter's current settings. * @@ -1096,7 +1053,7 @@ var ParticleEmitter = new Class({ if (HasValue(config, 'frame')) { - this.setFrame(config.frame); + this.setEmitterFrame(config.frame); } else if (HasValue(config, 'anim')) { @@ -1113,11 +1070,11 @@ var ParticleEmitter = new Class({ this.fastForward(config.advance); } - this.resetCounters(this.frequency, this.on); + this.resetCounters(this.frequency, this.emitting); - if (this.on) + if (this.emitting) { - this.manager.emit(Events.START, this); + this.emit(Events.START, this); } return this; @@ -1254,28 +1211,33 @@ var ParticleEmitter = new Class({ */ getFrame: function () { - if (this.frames.length === 1) + var frames = this.frames; + var current; + + if (frames.length === 1) { - return this.defaultFrame; + current = frames[0]; } else if (this.randomFrame) { - return GetRandom(this.frames); + current = GetRandom(frames); } else { - var frame = this.frames[this.currentFrame]; + var counters = this.counters; + + current = frames[counters[8]]; this.frameCounter++; if (this.frameCounter >= this.frameQuantity) { this.frameCounter = 0; - this.currentFrame = Wrap(this.currentFrame + 1, 0, this._frameLength); + counters[8] = Wrap(counters[8] + 1, 0, this._frameLength); } - - return frame; } + + return this.texture.get(current); }, /** @@ -1287,7 +1249,7 @@ var ParticleEmitter = new Class({ * frame: [ 'red', 'green', 'blue', 'pink', 'white' ] * frame: { frames: [ 'red', 'green', 'blue', 'pink', 'white' ], [cycle: bool], [quantity: int] } * - * @method Phaser.GameObjects.Particles.ParticleEmitter#setFrame + * @method Phaser.GameObjects.Particles.ParticleEmitter#setEmitterFrame * @since 3.0.0 * * @param {(array|string|number|Phaser.Types.GameObjects.Particles.ParticleEmitterFrameConfig)} frames - One or more texture frames, or a configuration object. @@ -1296,20 +1258,28 @@ var ParticleEmitter = new Class({ * * @return {this} This Particle Emitter. */ - setFrame: function (frames, pickRandom, quantity) + setEmitterFrame: function (frames, pickRandom, quantity) { if (pickRandom === undefined) { pickRandom = true; } if (quantity === undefined) { quantity = 1; } this.randomFrame = pickRandom; this.frameQuantity = quantity; - this.currentFrame = 0; + + // currentFrame + this.counters[8] = 0; var t = typeof (frames); - if (Array.isArray(frames) || t === 'string' || t === 'number') + this.frames.length = 0; + + if (Array.isArray(frames)) { - this.manager.setEmitterFrames(frames, this); + this.frames = this.frames.concat(frames); + } + else if (t === 'string' || t === 'number') + { + this.frames.push(frames); } else if (t === 'object') { @@ -1319,7 +1289,7 @@ var ParticleEmitter = new Class({ if (frames) { - this.manager.setEmitterFrames(frames, this); + this.frames = this.frames.concat(frames); } var isCycle = GetFastValue(frameConfig, 'cycle', false); @@ -1358,7 +1328,7 @@ var ParticleEmitter = new Class({ } else if (anims.length === 1) { - return this.defaultAnim; + return anims[0]; } else if (this.randomAnim) { @@ -1366,14 +1336,16 @@ var ParticleEmitter = new Class({ } else { - var anim = anims[this.currentAnim]; + var counters = this.counters; + + var anim = anims[counters[9]]; this.animCounter++; if (this.animCounter >= this.animQuantity) { this.animCounter = 0; - this.currentAnim = Wrap(this.currentAnim + 1, 0, this._animLength); + counters[9] = Wrap(counters[9] + 1, 0, this._animLength); } return anim; @@ -1403,13 +1375,21 @@ var ParticleEmitter = new Class({ this.randomAnim = pickRandom; this.animQuantity = quantity; - this.currentAnim = 0; + + // currentAnim + this.counters[9] = 0; var t = typeof (anims); - if (Array.isArray(anims) || t === 'string') + this.anims.length = 0; + + if (Array.isArray(anims)) { - this.manager.setEmitterAnims(anims, this); + this.anims = this.anims.concat(anims); + } + else if (t === 'string') + { + this.anims.push(anims); } else if (t === 'object') { @@ -1419,7 +1399,7 @@ var ParticleEmitter = new Class({ if (anims) { - this.manager.setEmitterAnims(anims, this); + this.anims = this.anims.concat(anims); } var isCycle = GetFastValue(animConfig, 'cycle', false); @@ -1459,26 +1439,6 @@ var ParticleEmitter = new Class({ return this; }, - /** - * Sets the position of the emitter's particle origin. - * New particles will be emitted here. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#setPosition - * @since 3.0.0 - * - * @param {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} x - The x-coordinate of the particle origin. - * @param {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} y - The y-coordinate of the particle origin. - * - * @return {this} This Particle Emitter. - */ - setPosition: function (x, y) - { - this.ops.x.onChange(x); - this.ops.y.onChange(y); - - return this; - }, - /** * Sets or modifies a rectangular boundary constraining the particles. * @@ -1616,24 +1576,6 @@ var ParticleEmitter = new Class({ return this; }, - /** - * Sets the scale of emitted particles. This updates both the scaleX and scaleY values. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#setScale - * @since 3.0.0 - * - * @param {(Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType|Phaser.Types.GameObjects.Particles.EmitterOpOnUpdateType)} value - The scale, relative to 1. - * - * @return {this} This Particle Emitter. - */ - setScale: function (value) - { - this.ops.scaleX.onChange(value); - this.ops.scaleY.onChange(value); - - return this; - }, - /** * Sets the horizontal gravity applied to emitted particles. * @@ -1739,23 +1681,6 @@ var ParticleEmitter = new Class({ return this; }, - /** - * Sets the angle of a {@link Phaser.GameObjects.Particles.ParticleEmitter#radial} particle stream. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#setAngle - * @since 3.0.0 - * - * @param {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} value - The angle of the initial velocity of emitted particles. - * - * @return {this} This Particle Emitter. - */ - setAngle: function (value) - { - this.ops.angle.onChange(value); - - return this; - }, - /** * Sets the lifespan of newly emitted particles. * @@ -1768,7 +1693,7 @@ var ParticleEmitter = new Class({ */ setLifespan: function (value) { - this.ops.lifespan.onChange(value); + this.lifespan.onChange(value); return this; }, @@ -1785,7 +1710,7 @@ var ParticleEmitter = new Class({ */ setQuantity: function (quantity) { - this.ops.quantity.onChange(quantity); + this.quantity = quantity; return this; }, @@ -2068,7 +1993,7 @@ var ParticleEmitter = new Class({ { if (zones[i].willKill(particle)) { - this.manager.emit(Events.DEATH_ZONE, this, particle, zones[i]); + this.emit(Events.DEATH_ZONE, this, particle, zones[i]); return true; } @@ -2117,6 +2042,78 @@ var ParticleEmitter = new Class({ return this; }, + /** + * Adds a Particle Processor, such as a Gravity Well, to this Emitter Manager. + * + * It will start processing particles from the next update as long as its `active` + * property is set. + * + * @method Phaser.GameObjects.Particles.ParticleEmitterManager#addParticleProcessor + * @since 3.60.0 + * + * @param {Phaser.GameObjects.Particles.ParticleProcessor} processor - The Particle Processor to add to this Emitter Manager. + * + * @return {Phaser.GameObjects.Particles.ParticleProcessor} The Particle Processor that was added to this Emitter Manager. + */ + addParticleProcessor: function (processor) + { + if (!this.processors.exists(processor)) + { + if (processor.emitter) + { + processor.emitter.removeParticleProcessor(processor); + } + + this.processors.add(processor); + + processor.emitter = this; + } + + return processor; + }, + + /** + * Removes a Particle Processor from this Emitter Manager. + * + * The Processor must belong to this Manager. + * + * It is not destroyed when removed, allowing you to move it to another Emitter Manager, + * so if you no longer require it you should call its `destroy` method directly. + * + * @method Phaser.GameObjects.Particles.ParticleEmitterManager#removeParticleProcessor + * @since 3.60.0 + * + * @param {Phaser.GameObjects.Particles.ParticleProcessor} processor - The Particle Processor to remove from this Emitter Manager. + * + * @return {?Phaser.GameObjects.Particles.ParticleProcessor} The Particle Processor that was removed, or null if it could not be found. + */ + removeParticleProcessor: function (processor) + { + if (this.processors.exists(processor)) + { + this.processors.remove(processor, true); + + processor.emitter = null; + } + + return processor; + }, + + /** + * Creates a new Gravity Well, adds it to this Emitter Manager and returns a reference to it. + * + * @method Phaser.GameObjects.Particles.ParticleEmitterManager#createGravityWell + * @since 3.0.0 + * + * @param {Phaser.Types.GameObjects.Particles.GravityWellConfig} config - Configuration settings for the Gravity Well to create. + * + * @return {Phaser.GameObjects.Particles.GravityWell} The Gravity Well that was created. + */ + createGravityWell: function (config) + { + return this.addParticleProcessor(new GravityWell(config)); + }, + /** * Creates inactive particles and adds them to this emitter's pool. * @@ -2277,6 +2274,8 @@ var ParticleEmitter = new Class({ /** * Deactivates every particle in this emitter immediately. * + * This particles are killed but do not emit an event or callback. + * * @method Phaser.GameObjects.Particles.ParticleEmitter#killAll * @since 3.0.0 * @@ -2303,7 +2302,7 @@ var ParticleEmitter = new Class({ * @since 3.0.0 * * @param {Phaser.Types.GameObjects.Particles.ParticleEmitterCallback} callback - The function. - * @param {*} context - The function's calling context. + * @param {*} context - The functions calling context. * * @return {this} This Particle Emitter. */ @@ -2328,7 +2327,7 @@ var ParticleEmitter = new Class({ * @since 3.0.0 * * @param {Phaser.Types.GameObjects.Particles.ParticleEmitterCallback} callback - The function. - * @param {*} context - The function's calling context. + * @param {*} context - The functions calling context. * * @return {this} This Particle Emitter. */ @@ -2339,7 +2338,6 @@ var ParticleEmitter = new Class({ for (var i = 0; i < length; i++) { - // Sends the Particle and the Emitter callback.call(context, dead[i], this); } @@ -2369,14 +2367,14 @@ var ParticleEmitter = new Class({ { if (advance === undefined) { advance = 0; } - if (!this.on) + if (!this.emitting) { if (advance > 0) { this.fastForward(advance); } - this.on = true; + this.emitting = true; this.resetCounters(this.frequency, true); @@ -2385,7 +2383,7 @@ var ParticleEmitter = new Class({ this.duration = Math.abs(duration); } - this.manager.emit(Events.START, this); + this.emit(Events.START, this); } return this; @@ -2411,16 +2409,16 @@ var ParticleEmitter = new Class({ { if (kill === undefined) { kill = false; } - if (this.on) + if (this.emitting) { - this.on = false; + this.emitting = false; if (kill) { this.killAll(); } - this.manager.emit(Events.STOP, this); + this.emit(Events.STOP, this); } return this; @@ -2456,24 +2454,6 @@ var ParticleEmitter = new Class({ return this; }, - /** - * Removes this Emitter from its Emitter Manager. - * - * Doing so does not destroy this Emitter. It's up to you to call the - * `destroy` method when you're ready to free-up the resources it's using. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#remove - * @since 3.22.0 - * - * @return {this} This Particle Emitter. - */ - remove: function () - { - this.manager.removeEmitter(this); - - return this; - }, - /** * Set the property by which active particles are sorted prior to be rendered. * @@ -2604,7 +2584,7 @@ var ParticleEmitter = new Class({ { if (count === undefined) { count = 1; } - this.on = false; + this.emitting = false; this.frequency = frequency; this.quantity = count; @@ -2638,7 +2618,7 @@ var ParticleEmitter = new Class({ var particle = this.emitParticle(count, x, y); - this.manager.emit(Events.EXPLODE, this, particle); + this.emit(Events.EXPLODE, this, particle); return particle; }, @@ -2662,7 +2642,7 @@ var ParticleEmitter = new Class({ }, /** - * Emits particles at a given position (or the emitter's current position). + * Emits particles at a given position (or the emitters current position). * * @method Phaser.GameObjects.Particles.ParticleEmitter#emitParticle * @since 3.0.0 @@ -2802,7 +2782,7 @@ var ParticleEmitter = new Class({ } // Any particle processors? - var processors = this.manager.getProcessors(); + var processors = this.getProcessors(); var particles = this.alive; var dead = this.dead; @@ -2852,14 +2832,14 @@ var ParticleEmitter = new Class({ var counters = this.counters; - if (!this.on && !this.skipping) + if (!this.emitting && !this.skipping) { if (counters[5] === 1 && particles.length === 0) { // completeFlag counters[5] = 0; - this.manager.emit(Events.COMPLETE, this); + this.emit(Events.COMPLETE, this); } return; @@ -2915,7 +2895,6 @@ var ParticleEmitter = new Class({ * @param {Phaser.GameObjects.Components.TransformMatrix} [parentMatrix] - A temporary matrix to hold parent values during the calculations. * * @return {Phaser.GameObjects.Components.TransformMatrix} The populated Transform Matrix. - */ getWorldTransformMatrix: function (tempMatrix, parentMatrix) { if (tempMatrix === undefined) { tempMatrix = this.tempMatrix1; } @@ -2936,6 +2915,7 @@ var ParticleEmitter = new Class({ return tempMatrix; }, + */ /** * Takes either a Rectangle Geometry object or an Arcade Physics Body and tests @@ -3078,15 +3058,16 @@ var ParticleEmitter = new Class({ /** * The x coordinate the particles are emitted from. * + * This is relative to the Emitters x coordinate and that of any parent. + * * Accessing this property should typically return a number. * However, it can be set to any valid EmitterOp onEmit type. * - * @name Phaser.GameObjects.Particles.ParticleEmitter#x + * @name Phaser.GameObjects.Particles.ParticleEmitter#particleX * @type {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} * @since 3.60.0 - * @see Phaser.GameObjects.Particles.ParticleEmitter#setPosition */ - x: { + particleX: { get: function () { @@ -3103,15 +3084,16 @@ var ParticleEmitter = new Class({ /** * The y coordinate the particles are emitted from. * + * This is relative to the Emitters x coordinate and that of any parent. + * * Accessing this property should typically return a number. * However, it can be set to any valid EmitterOp onEmit type. * - * @name Phaser.GameObjects.Particles.ParticleEmitter#y + * @name Phaser.GameObjects.Particles.ParticleEmitter#particleY * @type {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} * @since 3.60.0 - * @see Phaser.GameObjects.Particles.ParticleEmitter#setPosition */ - y: { + particleY: { get: function () { @@ -3224,7 +3206,12 @@ var ParticleEmitter = new Class({ }, /** - * The initial horizontal speed of emitted particles, in pixels per second. + * The initial speed of emitted particles, in pixels per second. + * + * If using this as a getter it will return the `speedX` value. + * + * If using it as a setter it will update both `speedX` and `speedY` to the + * given value. * * Accessing this property should typically return a number. * However, it can be set to any valid EmitterOp onEmit type. @@ -3373,14 +3360,16 @@ var ParticleEmitter = new Class({ /** * The horizontal scale of emitted particles. * + * This is relative to the Emitters scale and that of any parent. + * * Accessing this property should typically return a number. * However, it can be set to any valid EmitterOp onEmit type. * - * @name Phaser.GameObjects.Particles.ParticleEmitter#scaleX + * @name Phaser.GameObjects.Particles.ParticleEmitter#particleScaleX * @type {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} * @since 3.60.0 */ - scaleX: { + particleScaleX: { get: function () { @@ -3397,14 +3386,16 @@ var ParticleEmitter = new Class({ /** * The vertical scale of emitted particles. * + * This is relative to the Emitters scale and that of any parent. + * * Accessing this property should typically return a number. * However, it can be set to any valid EmitterOp onEmit type. * - * @name Phaser.GameObjects.Particles.ParticleEmitter#scaleY + * @name Phaser.GameObjects.Particles.ParticleEmitter#particleScaleY * @type {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} * @since 3.60.0 */ - scaleY: { + particleScaleY: { get: function () { @@ -3431,11 +3422,11 @@ var ParticleEmitter = new Class({ * Accessing this property should typically return a number. * However, it can be set to any valid EmitterOp onEmit type. * - * @name Phaser.GameObjects.Particles.ParticleEmitter#tint + * @name Phaser.GameObjects.Particles.ParticleEmitter#particleTint * @type {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} * @since 3.60.0 */ - tint: { + particleTint: { get: function () { @@ -3458,11 +3449,11 @@ var ParticleEmitter = new Class({ * Accessing this property should typically return a number. * However, it can be set to any valid EmitterOp onEmit type. * - * @name Phaser.GameObjects.Particles.ParticleEmitter#alpha + * @name Phaser.GameObjects.Particles.ParticleEmitter#particleAlpha * @type {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} * @since 3.60.0 */ - alpha: { + particleAlpha: { get: function () { @@ -3503,19 +3494,19 @@ var ParticleEmitter = new Class({ }, /** - * The angle in which the particles are emitted. The values are + * The angle at which the particles are emitted. The values are * given in degrees. This allows you to control the direction * of the emitter. If you wish instead to change the rotation - * of the particles themselves, see the `rotate` property. + * of the particles themselves, see the `particleRotate` property. * * Accessing this property should typically return a number. * However, it can be set to any valid EmitterOp onEmit type. * - * @name Phaser.GameObjects.Particles.ParticleEmitter#angle + * @name Phaser.GameObjects.Particles.ParticleEmitter#particleAngle * @type {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} * @since 3.60.0 */ - angle: { + particleAngle: { get: function () { @@ -3530,19 +3521,19 @@ var ParticleEmitter = new Class({ }, /** - * The rotation (or angle) of the particles when they are - * emitted. The value is given in degrees and uses a right-handed + * The rotation (or angle) of each particle when it is emitted. + * The value is given in degrees and uses a right-handed * coordinate system, where 0 degrees points to the right, 90 degrees * points down and -90 degrees points up. * * Accessing this property should typically return a number. * However, it can be set to any valid EmitterOp onEmit type. * - * @name Phaser.GameObjects.Particles.ParticleEmitter#rotate + * @name Phaser.GameObjects.Particles.ParticleEmitter#particleRotate * @type {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} * @since 3.60.0 */ - rotate: { + particleRotate: { get: function () { @@ -3720,16 +3711,14 @@ var ParticleEmitter = new Class({ /** * Destroys this Particle Emitter and all Particles it owns. * - * @method Phaser.GameObjects.Particles.ParticleEmitter#destroy + * @method Phaser.GameObjects.Particles.ParticleEmitter#preDestroy * @since 3.60.0 */ - destroy: function () + preDestroy: function () { - this.manager = null; this.texture = null; this.frames = null; this.anims = null; - this.defaultFrame = null; this.emitCallback = null; this.emitCallbackScope = null; this.deathCallback = null; @@ -3751,9 +3740,9 @@ var ParticleEmitter = new Class({ ops[key].destroy(); } - for (i = 0; i < this.particles.length; i++) + for (i = 0; i < this.alive.length; i++) { - this.particles[i].destroy(); + this.alive[i].destroy(); } for (i = 0; i < this.dead.length; i++) @@ -3762,7 +3751,7 @@ var ParticleEmitter = new Class({ } this.ops = null; - this.particles = []; + this.alive = []; this.dead = []; this.tempMatrix1.destroy(); this.tempMatrix2.destroy(); diff --git a/src/gameobjects/particles/ParticleEmitter2.js b/src/gameobjects/particles/ParticleEmitter2.js deleted file mode 100644 index 88c7f9d8c..000000000 --- a/src/gameobjects/particles/ParticleEmitter2.js +++ /dev/null @@ -1,3762 +0,0 @@ -/** - * @author Richard Davey - * @copyright 2013-2023 Photon Storm Ltd. - * @license {@link https://opensource.org/licenses/MIT|MIT License} - */ - -var Add = require('../../utils/array/Add'); -var Class = require('../../utils/Class'); -var Components = require('../components'); -var CopyFrom = require('../../geom/rectangle/CopyFrom'); -var DeathZone = require('./zones/DeathZone'); -var EdgeZone = require('./zones/EdgeZone'); -var EmitterOp = require('./EmitterOp'); -var Events = require('./events'); -var GameObject = require('../GameObject'); -var GetFastValue = require('../../utils/object/GetFastValue'); -var GetRandom = require('../../utils/array/GetRandom'); -var GravityWell = require('./GravityWell'); -var HasAny = require('../../utils/object/HasAny'); -var HasValue = require('../../utils/object/HasValue'); -var Inflate = require('../../geom/rectangle/Inflate'); -var List = require('../../structs/List'); -var MergeRect = require('../../geom/rectangle/MergeRect'); -var Particle2 = require('./Particle2'); -var RandomZone = require('./zones/RandomZone'); -var Rectangle = require('../../geom/rectangle/Rectangle'); -var RectangleToRectangle = require('../../geom/intersects/RectangleToRectangle'); -var Remove = require('../../utils/array/Remove'); -var Render = require('./ParticleEmitterRender'); -var StableSort = require('../../utils/array/StableSort'); -var TransformMatrix = require('../components/TransformMatrix'); -var Vector2 = require('../../math/Vector2'); -var Wrap = require('../../math/Wrap'); - -/** - * Names of simple configuration properties. - * - * @ignore - */ -var configFastMap = [ - 'active', - 'advance', - 'blendMode', - 'collideBottom', - 'collideLeft', - 'collideRight', - 'collideTop', - 'deathCallback', - 'deathCallbackScope', - 'duration', - 'emitCallback', - 'emitCallbackScope', - 'follow', - 'frequency', - 'gravityX', - 'gravityY', - 'maxAliveParticles', - 'maxParticles', - 'name', - 'emitting', - 'particleBringToTop', - 'particleClass', - 'radial', - 'sortCallback', - 'sortOrderAsc', - 'sortProperty', - 'stopAfter', - 'timeScale', - 'trackVisible', - 'visible' -]; - -/** - * Names of complex configuration properties. - * - * @ignore - */ -var configOpMap = [ - 'accelerationX', - 'accelerationY', - 'alpha', - 'angle', - 'bounce', - 'delay', - 'lifespan', - 'maxVelocityX', - 'maxVelocityY', - 'moveToX', - 'moveToY', - 'quantity', - 'rotate', - 'scaleX', - 'scaleY', - 'speedX', - 'speedY', - 'tint', - 'x', - 'y' -]; - -/** - * @classdesc - * A Particle Emitter is a special kind of Game Object that manages a single stream of Particles. - * - * It controls a pool of {@link Phaser.GameObjects.Particles.Particle Particles} and is controlled by a {@link Phaser.GameObjects.Particles.ParticleEmitterManager Particle Emitter Manager}. - * - * Lots of emitter properties can be specified in a variety of formats, giving you plenty - * of control over the values they return. Here are the different variations: - * - * ## An explicit static value: - * - * ```js - * x: 400 - * ``` - * - * The x value will always be 400 when the particle is spawned. - * - * ## A random value: - * - * ```js - * x: [ 100, 200, 300, 400 ] - * ``` - * - * The x value will be one of the 4 elements in the given array, picked at random on emission. - * - * ## A custom callback: - * - * ```js - * x: (particle, key, t, value) => { - * return value + 50; - * } - * ``` - * - * The x value is the result of calling this function. This is only used when the - * particle is emitted, so it provides it's initial starting value. It is not used - * when the particle is updated (see the onUpdate callback for that) - * - * ## A start / end object: - * - * This allows you to control the change in value between the given start and - * end parameters over the course of the particles lifetime: - * - * ```js - * scale: { start: 0, end: 1 } - * ``` - * - * The particle scale will start at 0 when emitted and ease to a scale of 1 - * over the course of its lifetime. You can also specify the ease function - * used for this change (the default is Linear): - * - * ```js - * scale: { start: 0, end: 1, ease: 'bounce.out' } - * ``` - * - * ## A start / end random object: - * - * The start and end object can have an optional `random` parameter. - * This forces it to pick a random value between the two values and use - * this as the starting value, then easing to the 'end' parameter over - * its lifetime. - * - * ```js - * scale: { start: 4, end: 0.5, random: true } - * ``` - * - * The particle will start with a random scale between 0.5 and 4 and then - * scale to the end value over its lifetime. You can combine the above - * with the `ease` parameter as well to control the value easing. - * - * ## An interpolation object: - * - * You can provide an array of values which will be used for interpolation - * during the particles lifetime. You can also define the interpolation - * function to be used. There are three provided: `linear` (the default), - * `bezier` and `catmull`, or you can provide your own function. - * - * ```js - * x: { values: [ 50, 500, 200, 800 ], interpolation: 'catmull' } - * ``` - * - * The particle scale will interpolate from 50 when emitted to 800 via the other - * points over the course of its lifetime. You can also specify an ease function - * used to control the rate of change through the values (the default is Linear): - * - * ```js - * x: { values: [ 50, 500, 200, 800 ], interpolation: 'catmull', ease: 'bounce.out } - * ``` - * - * ## A stepped emitter object: - * - * The `steps` parameter allows you to control the placement of sequential - * particles across the start-end range: - * - * ```js - * x: { steps: 32, start: 0, end: 576 } - * ``` - * - * Here we have a range of 576 (start to end). This is divided into 32 steps. - * - * The first particle will emit at the x position of 0. The next will emit - * at the next 'step' along, which would be 18. The following particle will emit - * at the next step, which is 36, and so on. Because the range of 576 has been - * divided by 32, creating 18 pixels steps. When a particle reaches the 'end' - * value the next one will start from the beginning again. - * - * ## A stepped emitter object with yoyo: - * - * You can add the optional `yoyo` property to a stepped object: - * - * ```js - * x: { steps: 32, start: 0, end: 576, yoyo: true } - * ``` - * - * As with the stepped emitter, particles are emitted in sequence, from 'start' - * to 'end' in step sized jumps. Normally, when a stepped emitter reaches the - * end it snaps around to the start value again. However, if you provide the 'yoyo' - * parameter then when it reaches the end it will reverse direction and start - * emitting back down to 'start' again. Depending on the effect you require this - * can often look better. - * - * ## A min / max object: - * - * This allows you to pick a random float value between the min and max properties: - * - * ```js - * x: { min: 100, max: 700 } - * ``` - * - * The x value will be a random float between min and max. - * - * You can force it select an integer by setting the 'int' flag: - * - * ```js - * x: { min: 100, max: 700, int: true } - * ``` - * - * Or, you could use the 'random' array approach (see below) - * - * ## A random object: - * - * This allows you to pick a random integer value between the first and second array elements: - * - * ```js - * x: { random: [ 100, 700 ] } - * ``` - * - * The x value will be a random integer between 100 and 700 as it takes the first - * element in the 'random' array as the 'min' value and the 2nd element as the 'max' value. - * - * ## Custom onEmit and onUpdate callbacks: - * - * If the above won't give you the effect you're after, you can provide your own - * callbacks that will be used when the particle is both emitted and updated: - * - * ```js - * x: { - * onEmit: (particle, key, t, value) => { - * return value; - * }, - * onUpdate: (particle, key, t, value) => { - * return value; - * } - * } - * ``` - * - * You can provide either one or both functions. The `onEmit` is called at the - * start of the particles life and defines the value of the property on birth. - * - * The `onUpdate` function is called every time the Particle Emitter updates - * until the particle dies. Both must return a value. - * - * The properties are: - * - * particle - A reference to the Particle instance. - * key - The string based key of the property, i.e. 'x' or 'lifespan'. - * t - The current normalized lifetime of the particle, between 0 (birth) and 1 (death). - * value - The current property value. At a minimum you should return this. - * - * By using the above configuration options you have an unlimited about of - * control over how your particles behave. - * - * @class ParticleEmitter - * @extends Phaser.GameObjects.GameObject - * @memberof Phaser.GameObjects.Particles - * @constructor - * @since 3.60.0 - * - * @extends Phaser.GameObjects.Components.AlphaSingle - * @extends Phaser.GameObjects.Components.BlendMode - * @extends Phaser.GameObjects.Components.Depth - * @extends Phaser.GameObjects.Components.FX - * @extends Phaser.GameObjects.Components.Mask - * @extends Phaser.GameObjects.Components.Pipeline - * @extends Phaser.GameObjects.Components.ScrollFactor - * @extends Phaser.GameObjects.Components.Texture - * @extends Phaser.GameObjects.Components.Transform - * @extends Phaser.GameObjects.Components.Visible - * - * @param {Phaser.Types.GameObjects.Particles.ParticleEmitterConfig} config - Settings for this emitter. - */ -var ParticleEmitter2 = new Class({ - - Extends: GameObject, - - Mixins: [ - Components.AlphaSingle, - Components.BlendMode, - Components.Depth, - Components.FX, - Components.Mask, - Components.Pipeline, - Components.ScrollFactor, - Components.Texture, - Components.Transform, - Components.Visible, - Render - ], - - initialize: - - function ParticleEmitter2 (scene, texture, config) - { - GameObject.call(this, scene, 'ParticleEmitter'); - - /** - * The Particle Class which will be emitted by this Emitter. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#particleClass - * @type {function} - * @default Phaser.GameObjects.Particles.Particle - * @since 3.0.0 - * @see Phaser.Types.GameObjects.Particles.ParticleClassConstructor - */ - this.particleClass = Particle2; - - /** - * An internal object holding all of the EmitterOp instances. - * - * These are populated as part of the Emitter configuration parsing. - * - * You typically do not access them directly, but instead use the - * provided getters and setters on this class, such as `ParticleEmitter.speedX` etc. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#ops - * @type {Phaser.Types.GameObjects.Particles.ParticleEmitterOps} - * @since 3.60.0 - */ - this.ops = { - x: new EmitterOp('x', 0), - y: new EmitterOp('y', 0), - accelerationX: new EmitterOp('accelerationX', 0), - accelerationY: new EmitterOp('accelerationY', 0), - maxVelocityX: new EmitterOp('maxVelocityX', 10000), - maxVelocityY: new EmitterOp('maxVelocityY', 10000), - speedX: new EmitterOp('speedX', 0, true), - speedY: new EmitterOp('speedY', 0, true), - moveToX: new EmitterOp('moveToX', 0), - moveToY: new EmitterOp('moveToY', 0), - bounce: new EmitterOp('bounce', 0), - scaleX: new EmitterOp('scaleX', 1), - scaleY: new EmitterOp('scaleY', 1), - tint: new EmitterOp('tint', 0xffffff), - alpha: new EmitterOp('alpha', 1), - lifespan: new EmitterOp('lifespan', 1000, true), - angle: new EmitterOp('angle', { min: 0, max: 360 }, true), - rotate: new EmitterOp('rotate', 0), - quantity: new EmitterOp('quantity', 1, true), - delay: new EmitterOp('delay', 0, true) - }; - - /** - * A radial emitter will emit particles in all directions between angle min and max, - * using {@link Phaser.GameObjects.Particles.ParticleEmitter#speed} as the value. If set to false then this acts as a point Emitter. - * A point emitter will emit particles only in the direction derived from the speedX and speedY values. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#radial - * @type {boolean} - * @default true - * @since 3.0.0 - * @see Phaser.GameObjects.Particles.ParticleEmitter#setRadial - */ - this.radial = true; - - /** - * Horizontal acceleration applied to emitted particles, in pixels per second squared. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#gravityX - * @type {number} - * @default 0 - * @since 3.0.0 - * @see Phaser.GameObjects.Particles.ParticleEmitter#setGravity - */ - this.gravityX = 0; - - /** - * Vertical acceleration applied to emitted particles, in pixels per second squared. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#gravityY - * @type {number} - * @default 0 - * @since 3.0.0 - * @see Phaser.GameObjects.Particles.ParticleEmitter#setGravity - */ - this.gravityY = 0; - - /** - * Whether accelerationX and accelerationY are non-zero. Set automatically during configuration. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#acceleration - * @type {boolean} - * @default false - * @since 3.0.0 - */ - this.acceleration = false; - - /** - * Whether moveToX and moveToY are set. Set automatically during configuration. - * - * When true the particles move toward the moveToX and moveToY coordinates and arrive at the end of their life. - * Emitter angle, speedX, and speedY are ignored. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#moveTo - * @type {boolean} - * @default false - * @since 3.0.0 - */ - this.moveTo = false; - - /** - * A function to call when a particle is emitted. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#emitCallback - * @type {?Phaser.Types.GameObjects.Particles.ParticleEmitterCallback} - * @default null - * @since 3.0.0 - */ - this.emitCallback = null; - - /** - * The calling context for {@link Phaser.GameObjects.Particles.ParticleEmitter#emitCallback}. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#emitCallbackScope - * @type {?*} - * @default null - * @since 3.0.0 - */ - this.emitCallbackScope = null; - - /** - * A function to call when a particle dies. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#deathCallback - * @type {?Phaser.Types.GameObjects.Particles.ParticleDeathCallback} - * @default null - * @since 3.0.0 - */ - this.deathCallback = null; - - /** - * The calling context for {@link Phaser.GameObjects.Particles.ParticleEmitter#deathCallback}. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#deathCallbackScope - * @type {?*} - * @default null - * @since 3.0.0 - */ - this.deathCallbackScope = null; - - /** - * Set to hard limit the amount of particle objects this emitter is allowed to create - * in total. This is the number of `Particle` instances it can create, not the number - * of 'alive' particles. - * - * 0 means unlimited. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#maxParticles - * @type {number} - * @default 0 - * @since 3.0.0 - */ - this.maxParticles = 0; - - /** - * The maximum number of alive and rendering particles this emitter will update. - * When this limit is reached, a particle needs to die before another can be emitted. - * - * 0 means no limits. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#maxAliveParticles - * @type {number} - * @default 0 - * @since 3.60.0 - */ - this.maxAliveParticles = 0; - - /** - * If set, either via the Emitter config, or by directly setting this property, - * the Particle Emitter will stop emitting particles once this total has been - * reached. It will then enter a 'stopped' state, firing the `STOP` - * event. Note that entering a stopped state doesn't mean all the particles - * have finished, just that it's not emitting any further ones. - * - * To know when the final particle expires, listen for the COMPLETE event. - * - * Use this if you wish to launch an exact number of particles and then stop - * your emitter afterwards. - * - * The counter is reset each time the `ParticleEmitter.start` method is called. - * - * 0 means the emitter will not stop based on total emitted particles. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#stopAfter - * @type {number} - * @default 0 - * @since 3.60.0 - */ - this.stopAfter = 0; - - /** - * The number of milliseconds this emitter will emit particles for when in flow mode, - * before it stops emission. A value of 0 (the default) means there is no duration. - * - * When the duration expires the `STOP` event is emitted. Note that entering a - * stopped state doesn't mean all the particles have finished, just that it's - * not emitting any further ones. - * - * To know when the final particle expires, listen for the COMPLETE event. - * - * The counter is reset each time the `ParticleEmitter.start` method is called. - * - * 0 means the emitter will not stop based on duration. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#duration - * @type {number} - * @default 0 - * @since 3.60.0 - */ - this.duration = 0; - - /** - * For a flow emitter, the time interval (>= 0) between particle flow cycles in ms. - * A value of 0 means there is one particle flow cycle for each logic update (the maximum flow frequency). This is the default setting. - * For an exploding emitter, this value will be -1. - * Calling {@link Phaser.GameObjects.Particles.ParticleEmitter#flow} also puts the emitter in flow mode (frequency >= 0). - * Calling {@link Phaser.GameObjects.Particles.ParticleEmitter#explode} also puts the emitter in explode mode (frequency = -1). - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#frequency - * @type {number} - * @default 0 - * @since 3.0.0 - * @see Phaser.GameObjects.Particles.ParticleEmitter#setFrequency - */ - this.frequency = 0; - - /** - * Controls if the emitter is currently emitting a particle flow (when frequency >= 0). - * Already alive particles will continue to update until they expire. - * Controlled by {@link Phaser.GameObjects.Particles.ParticleEmitter#start} and {@link Phaser.GameObjects.Particles.ParticleEmitter#stop}. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#on - * @type {boolean} - * @default true - * @since 3.0.0 - */ - this.emitting = 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. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#particleBringToTop - * @type {boolean} - * @default true - * @since 3.0.0 - */ - this.particleBringToTop = true; - - /** - * The time rate applied to active particles, affecting lifespan, movement, and tweens. Values larger than 1 are faster than normal. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#timeScale - * @type {number} - * @default 1 - * @since 3.0.0 - */ - this.timeScale = 1; - - /** - * An array containing Particle Emission Zones. These can be either EdgeZones or RandomZones. - * - * Particles are emitted from a randomly selected zone from this array. - * - * Prior to Phaser v3.60 an Emitter could only have one single Emission Zone. - * In 3.60 they can now have an array of Emission Zones. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#emitZones - * @type {Phaser.GameObjects.Particles.Zones.EdgeZone[]|Phaser.GameObjects.Particles.Zones.RandomZone[]} - * @since 3.60.0 - * @see Phaser.GameObjects.Particles.ParticleEmitter#setEmitZone - */ - this.emitZones = []; - - /** - * An array containing Particle Death Zone objects. A particle is immediately killed as soon as its x/y coordinates - * intersect with any of the configured Death Zones. - * - * Prior to Phaser v3.60 an Emitter could only have one single Death Zone. - * In 3.60 they can now have an array of Death Zones. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#deathZones - * @type {Phaser.GameObjects.Particles.Zones.DeathZone[]} - * @since 3.60.0 - * @see Phaser.GameObjects.Particles.ParticleEmitter#setDeathZone - */ - this.deathZones = []; - - /** - * An optional Rectangle object that is used during rendering to cull Particles from - * display. For example, if your particles are limited to only move within a 300x300 - * sized area from their origin, then you can set this Rectangle to those dimensions. - * - * The renderer will check to see if the `viewBounds` Rectangle intersects with the - * Camera bounds during the render step and if not it will skip rendering the Emitter - * entirely. - * - * This allows you to create many emitters in a Scene without the cost of - * rendering if the contents aren't visible. - * - * Note that the Emitter will not perform any checks to see if the Particles themselves - * are outside of these bounds, or not. It will simply check the bounds against the - * camera. Use the `getBounds` method with the `advance` parameter to help define - * the location and placement of the view bounds. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#viewBounds - * @type {?Phaser.Geom.Rectangle} - * @default null - * @since 3.60.0 - * @see Phaser.GameObjects.Particles.ParticleEmitter#setViewBounds - */ - this.viewBounds = null; - - /** - * A rectangular boundary constraining particle movement. Use the Emitter properties `collideLeft`, - * `collideRight`, `collideTop` and `collideBottom` to control if a particle will rebound off - * the sides of this boundary, or not. This happens when the particles x/y coordinate hits - * the boundary. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#bounds - * @type {?Phaser.Geom.Rectangle} - * @default null - * @since 3.0.0 - * @see Phaser.GameObjects.Particles.ParticleEmitter#setBounds - */ - this.bounds = null; - - /** - * Whether particles interact with the left edge of the emitter {@link Phaser.GameObjects.Particles.ParticleEmitter#bounds}. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#collideLeft - * @type {boolean} - * @default true - * @since 3.0.0 - */ - this.collideLeft = true; - - /** - * Whether particles interact with the right edge of the emitter {@link Phaser.GameObjects.Particles.ParticleEmitter#bounds}. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#collideRight - * @type {boolean} - * @default true - * @since 3.0.0 - */ - this.collideRight = true; - - /** - * Whether particles interact with the top edge of the emitter {@link Phaser.GameObjects.Particles.ParticleEmitter#bounds}. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#collideTop - * @type {boolean} - * @default true - * @since 3.0.0 - */ - this.collideTop = true; - - /** - * Whether particles interact with the bottom edge of the emitter {@link Phaser.GameObjects.Particles.ParticleEmitter#bounds}. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#collideBottom - * @type {boolean} - * @default true - * @since 3.0.0 - */ - this.collideBottom = true; - - /** - * A Game Object whose position is used as the particle origin. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#follow - * @type {?Phaser.GameObjects.GameObject} - * @default null - * @since 3.0.0 - * @see Phaser.GameObjects.Particles.ParticleEmitter#startFollow - * @see Phaser.GameObjects.Particles.ParticleEmitter#stopFollow - */ - this.follow = null; - - /** - * The offset of the particle origin from the {@link Phaser.GameObjects.Particles.ParticleEmitter#follow} target. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#followOffset - * @type {Phaser.Math.Vector2} - * @since 3.0.0 - * @see Phaser.GameObjects.Particles.ParticleEmitter#startFollow - */ - this.followOffset = new Vector2(); - - /** - * Whether the emitter's {@link Phaser.GameObjects.Particles.ParticleEmitter#visible} state will track - * the {@link Phaser.GameObjects.Particles.ParticleEmitter#follow} target's visibility state. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#trackVisible - * @type {boolean} - * @default false - * @since 3.0.0 - * @see Phaser.GameObjects.Particles.ParticleEmitter#startFollow - */ - this.trackVisible = false; - - /** - * The texture frames assigned to particles. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#frames - * @type {Phaser.Textures.Frame[]} - * @since 3.0.0 - */ - this.frames = []; - - /** - * Whether texture {@link Phaser.GameObjects.Particles.ParticleEmitter#frames} are selected at random. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#randomFrame - * @type {boolean} - * @default true - * @since 3.0.0 - * @see Phaser.GameObjects.Particles.ParticleEmitter#setFrame - */ - this.randomFrame = true; - - /** - * The number of consecutive particles that receive a single texture frame (per frame cycle). - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#frameQuantity - * @type {number} - * @default 1 - * @since 3.0.0 - * @see Phaser.GameObjects.Particles.ParticleEmitter#setFrame - */ - this.frameQuantity = 1; - - /** - * The animations assigned to particles. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#anims - * @type {string[]} - * @since 3.60.0 - */ - this.anims = []; - - /** - * Whether animations {@link Phaser.GameObjects.Particles.ParticleEmitter#anims} are selected at random. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#randomAnim - * @type {boolean} - * @default true - * @since 3.60.0 - * @see Phaser.GameObjects.Particles.ParticleEmitter#setAnim - */ - this.randomAnim = true; - - /** - * The number of consecutive particles that receive a single animation (per frame cycle). - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#animQuantity - * @type {number} - * @default 1 - * @since 3.60.0 - * @see Phaser.GameObjects.Particles.ParticleEmitter#setAnim - */ - this.animQuantity = 1; - - /** - * An array containing all currently inactive Particle instances. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#dead - * @type {Phaser.GameObjects.Particles.Particle[]} - * @private - * @since 3.0.0 - */ - this.dead = []; - - /** - * An array containing all currently live and rendering Particle instances. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#alive - * @type {Phaser.GameObjects.Particles.Particle[]} - * @private - * @since 3.0.0 - */ - this.alive = []; - - /** - * Internal array that holds counter data: - * - * 0 - _counter - The time until next flow cycle. - * 1 - _frameCounter - Counts up to {@link Phaser.GameObjects.Particles.ParticleEmitter#frameQuantity}. - * 2 - _animCounter (counts up to animQuantity) - * 3 - _elapsed - The time remaining until the `duration` limit is reached. - * 4 - _stopCounter - The number of particles remaining until `stopAfter` limit is reached. - * 5 - _completeFlag - Has the COMPLETE event been emitted? - * 6 - _emitIndex - The emit zone index counter. - * 7 - _zoneTotal - The emit zone total counter. - * 8 - currentFrame - The current texture frame, as an index of {@link Phaser.GameObjects.Particles.ParticleEmitter#frames}. - * 9 - currentAnim - The current animation, as an index of {@link Phaser.GameObjects.Particles.ParticleEmitter#anims}. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#counters - * @type {Float32Array} - * @private - * @since 3.60.0 - */ - this.counters = new Float32Array(10); - - /** - * Cached amount of frames in the `ParticleEmitter.frames` array. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#_frameLength - * @type {number} - * @private - * @default 0 - * @since 3.0.0 - */ - this._frameLength = 0; - - /** - * Cached amount of animations in the `ParticleEmitter.anims` array. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#_animLength - * @type {number} - * @private - * @default 0 - * @since 3.60.0 - */ - this._animLength = 0; - - /** - * An internal property used to tell when the emitter is in fast-forwarc mode. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#skipping - * @type {boolean} - * @default true - * @since 3.60.0 - */ - this.skipping = false; - - /** - * An internal Transform Matrix used for bounds calculations. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#tempMatrix1 - * @type {Phaser.GameObjects.Components.TransformMatrix} - * @since 3.60.0 - */ - this.tempMatrix1 = new TransformMatrix(); - - /** - * An internal Transform Matrix used for bounds calculations. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#tempMatrix2 - * @type {Phaser.GameObjects.Components.TransformMatrix} - * @since 3.60.0 - */ - this.tempMatrix2 = new TransformMatrix(); - - /** - * Optionally sort the particles before they render based on this - * property. The property must exist on the `Particle` class, such - * as `y`, `lifeT`, `scaleX`, etc. - * - * When set this overrides the `particleBringToTop` setting. - * - * To reset this and disable sorting, so this property to an empty string. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#sortProperty - * @type {string} - * @since 3.60.0 - */ - this.sortProperty = ''; - - /** - * When `sortProperty` is defined this controls the sorting order, - * either ascending or descending. Toggle to control the visual effect. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#sortOrderAsc - * @type {boolean} - * @since 3.60.0 - */ - this.sortOrderAsc = true; - - /** - * The callback used to sort the particles. Only used if `sortProperty` - * has been set. Set this via the `setSortCallback` method. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#sortCallback - * @type {?Phaser.Types.GameObjects.Particles.ParticleSortCallback} - * @since 3.60.0 - */ - this.sortCallback = this.depthSortCallback; - - /** - * A list of Particle Processors being managed by this Emitter Manager. - * - * @name Phaser.GameObjects.Particles.ParticleEmitterManager#processors - * @type {Phaser.Structs.List.} - * @since 3.0.0 - */ - this.processors = new List(this); - - this.setTexture(texture); - - this.initPipeline(); - - this.fromJSON(config); - }, - - // Overrides Game Object method - addedToScene: function () - { - this.scene.sys.updateList.add(this); - }, - - // Overrides Game Object method - removedFromScene: function () - { - this.scene.sys.updateList.remove(this); - }, - - /** - * Gets all active Particle Processors. - * - * @method Phaser.GameObjects.Particles.ParticleEmitterManager#getProcessors - * @since 3.0.0 - * - * @return {Phaser.GameObjects.Particles.ParticleProcessor[]} - An array of active Particle Processors. - */ - getProcessors: function () - { - return this.processors.getAll('active', true); - }, - - /** - * Merges configuration settings into the emitter's current settings. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#fromJSON - * @since 3.0.0 - * - * @param {Phaser.Types.GameObjects.Particles.ParticleEmitterConfig} config - Settings for this emitter. - * - * @return {this} This Particle Emitter. - */ - fromJSON: function (config) - { - if (!config) - { - return this; - } - - // Only update properties from their current state if they exist in the given config - - var i = 0; - var key = ''; - - for (i = 0; i < configFastMap.length; i++) - { - key = configFastMap[i]; - - if (HasValue(config, key)) - { - this[key] = GetFastValue(config, key); - } - } - - var ops = this.ops; - - for (i = 0; i < configOpMap.length; i++) - { - key = configOpMap[i]; - - ops[key].loadConfig(config); - } - - this.acceleration = (this.accelerationX !== 0 || this.accelerationY !== 0); - - this.moveTo = (this.moveToX !== 0 && this.moveToY !== 0); - - // Special 'speed' override - - if (HasValue(config, 'speed')) - { - ops.speedX.loadConfig(config, 'speed'); - ops.speedY.active = false; - } - - // If you specify speedX, speedY or moveTo then it changes the emitter from radial to a point emitter - if (HasAny(config, [ 'speedX', 'speedY' ]) || this.moveTo) - { - this.radial = false; - } - - // Special 'scale' override - - if (HasValue(config, 'scale')) - { - ops.scaleX.loadConfig(config, 'scale'); - ops.scaleY.active = false; - } - - if (HasValue(config, 'callbackScope')) - { - var callbackScope = GetFastValue(config, 'callbackScope', null); - - this.emitCallbackScope = callbackScope; - this.deathCallbackScope = callbackScope; - } - - if (HasValue(config, 'emitZone')) - { - this.addEmitZone(config.emitZone); - } - - if (HasValue(config, 'deathZone')) - { - this.addDeathZone(config.deathZone); - } - - if (HasValue(config, 'bounds')) - { - this.setBounds(config.bounds); - } - - if (HasValue(config, 'followOffset')) - { - this.followOffset.setFromObject(GetFastValue(config, 'followOffset', 0)); - } - - if (HasValue(config, 'frame')) - { - this.setEmitterFrame(config.frame); - } - else if (HasValue(config, 'anim')) - { - this.setAnim(config.anim); - } - - if (HasValue(config, 'reserve')) - { - this.reserve(config.reserve); - } - - if (HasValue(config, 'advance')) - { - this.fastForward(config.advance); - } - - this.resetCounters(this.frequency, this.emitting); - - if (this.emitting) - { - this.emit(Events.START, this); - } - - return this; - }, - - /** - * Creates a description of this emitter suitable for JSON serialization. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#toJSON - * @since 3.0.0 - * - * @param {object} [output] - An object to copy output into. - * - * @return {object} - The output object. - */ - toJSON: function (output) - { - if (output === undefined) { output = {}; } - - var i = 0; - var key = ''; - - for (i = 0; i < configFastMap.length; i++) - { - key = configFastMap[i]; - - output[key] = this[key]; - } - - var ops = this.ops; - - for (i = 0; i < configOpMap.length; i++) - { - key = configOpMap[i]; - - if (ops[key]) - { - output[key] = ops[key].toJSON(); - } - } - - // special handlers - if (!ops.speedY.active) - { - delete output.speedX; - output.speed = ops.speedX.toJSON(); - } - - if (this.scaleX === this.scaleY) - { - delete output.scaleX; - delete output.scaleY; - output.scale = ops.scaleX.toJSON(); - } - - return output; - }, - - /** - * Resets the internal counter trackers. - * - * You shouldn't ever need to call this directly. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#resetCounters - * @since 3.60.0 - * - * @param {number} frequency - The frequency counter. - * @param {boolean} on - Set the complete flag. - */ - resetCounters: function (frequency, on) - { - var counters = this.counters; - - counters.fill(0); - - counters[0] = frequency; - - if (on) - { - counters[5] = 1; - } - }, - - /** - * Continuously moves the particle origin to follow a Game Object's position. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#startFollow - * @since 3.0.0 - * - * @param {Phaser.GameObjects.GameObject} target - The Game Object to follow. - * @param {number} [offsetX=0] - Horizontal offset of the particle origin from the Game Object. - * @param {number} [offsetY=0] - Vertical offset of the particle origin from the Game Object. - * @param {boolean} [trackVisible=false] - Whether the emitter's visible state will track the target's visible state. - * - * @return {this} This Particle Emitter. - */ - startFollow: function (target, offsetX, offsetY, trackVisible) - { - if (offsetX === undefined) { offsetX = 0; } - if (offsetY === undefined) { offsetY = 0; } - if (trackVisible === undefined) { trackVisible = false; } - - this.follow = target; - this.followOffset.set(offsetX, offsetY); - this.trackVisible = trackVisible; - - return this; - }, - - /** - * Stops following a Game Object. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#stopFollow - * @since 3.0.0 - * - * @return {this} This Particle Emitter. - */ - stopFollow: function () - { - this.follow = null; - this.followOffset.set(0, 0); - this.trackVisible = false; - - return this; - }, - - /** - * Chooses a texture frame from {@link Phaser.GameObjects.Particles.ParticleEmitter#frames}. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#getFrame - * @since 3.0.0 - * - * @return {Phaser.Textures.Frame} The texture frame. - */ - getFrame: function () - { - var frames = this.frames; - var current; - - if (frames.length === 1) - { - current = frames[0]; - } - else if (this.randomFrame) - { - current = GetRandom(frames); - } - else - { - var counters = this.counters; - - current = frames[counters[8]]; - - this.frameCounter++; - - if (this.frameCounter >= this.frameQuantity) - { - this.frameCounter = 0; - counters[8] = Wrap(counters[8] + 1, 0, this._frameLength); - } - } - - return this.texture.get(current); - }, - - /** - * Sets a pattern for assigning texture frames to emitted particles. The `frames` configuration can be any of: - * - * frame: 0 - * frame: 'red' - * frame: [ 0, 1, 2, 3 ] - * frame: [ 'red', 'green', 'blue', 'pink', 'white' ] - * frame: { frames: [ 'red', 'green', 'blue', 'pink', 'white' ], [cycle: bool], [quantity: int] } - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#setEmitterFrame - * @since 3.0.0 - * - * @param {(array|string|number|Phaser.Types.GameObjects.Particles.ParticleEmitterFrameConfig)} frames - One or more texture frames, or a configuration object. - * @param {boolean} [pickRandom=true] - Whether frames should be assigned at random from `frames`. - * @param {number} [quantity=1] - The number of consecutive particles that will receive each frame. - * - * @return {this} This Particle Emitter. - */ - setEmitterFrame: function (frames, pickRandom, quantity) - { - if (pickRandom === undefined) { pickRandom = true; } - if (quantity === undefined) { quantity = 1; } - - this.randomFrame = pickRandom; - this.frameQuantity = quantity; - - // currentFrame - this.counters[8] = 0; - - var t = typeof (frames); - - this.frames.length = 0; - - if (Array.isArray(frames)) - { - this.frames = this.frames.concat(frames); - } - else if (t === 'string' || t === 'number') - { - this.frames.push(frames); - } - else if (t === 'object') - { - var frameConfig = frames; - - frames = GetFastValue(frameConfig, 'frames', null); - - if (frames) - { - this.frames = this.frames.concat(frames); - } - - var isCycle = GetFastValue(frameConfig, 'cycle', false); - - this.randomFrame = (isCycle) ? false : true; - - this.frameQuantity = GetFastValue(frameConfig, 'quantity', quantity); - } - - this._frameLength = this.frames.length; - - if (this._frameLength === 1) - { - this.frameQuantity = 1; - this.randomFrame = false; - } - - return this; - }, - - /** - * Chooses an animation from {@link Phaser.GameObjects.Particles.ParticleEmitter#anims}, if populated. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#getAnim - * @since 3.60.0 - * - * @return {string} The animation to play, or `null` if there aren't any. - */ - getAnim: function () - { - var anims = this.anims; - - if (anims.length === 0) - { - return null; - } - else if (anims.length === 1) - { - return anims[0]; - } - else if (this.randomAnim) - { - return GetRandom(anims); - } - else - { - var counters = this.counters; - - var anim = anims[counters[9]]; - - this.animCounter++; - - if (this.animCounter >= this.animQuantity) - { - this.animCounter = 0; - counters[9] = Wrap(counters[9] + 1, 0, this._animLength); - } - - return anim; - } - }, - - /** - * Sets a pattern for assigning animations to emitted particles. The `anims` configuration can be any of: - * - * anim: 'red' - * anim: [ 'red', 'green', 'blue', 'pink', 'white' ] - * anim: { anims: [ 'red', 'green', 'blue', 'pink', 'white' ], [cycle: bool], [quantity: int] } - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#setAnim - * @since 3.60.0 - * - * @param {(array|string|Phaser.Types.GameObjects.Particles.ParticleEmitterFrameConfig)} anims - One or more animations, or a configuration object. - * @param {boolean} [pickRandom=true] - Whether animations should be assigned at random from `anims`. - * @param {number} [quantity=1] - The number of consecutive particles that will receive each animation. - * - * @return {this} This Particle Emitter. - */ - setAnim: function (anims, pickRandom, quantity) - { - if (pickRandom === undefined) { pickRandom = true; } - if (quantity === undefined) { quantity = 1; } - - this.randomAnim = pickRandom; - this.animQuantity = quantity; - - // currentAnim - this.counters[9] = 0; - - var t = typeof (anims); - - this.anims.length = 0; - - if (Array.isArray(anims)) - { - this.anims = this.anims.concat(anims); - } - else if (t === 'string') - { - this.anims.push(anims); - } - else if (t === 'object') - { - var animConfig = anims; - - anims = GetFastValue(animConfig, 'anims', null); - - if (anims) - { - this.anims = this.anims.concat(anims); - } - - var isCycle = GetFastValue(animConfig, 'cycle', false); - - this.randomAnim = (isCycle) ? false : true; - - this.animQuantity = GetFastValue(animConfig, 'quantity', quantity); - } - - this._animLength = this.anims.length; - - if (this._animLength === 1) - { - this.animQuantity = 1; - this.randomAnim = false; - } - - return this; - }, - - /** - * Turns {@link Phaser.GameObjects.Particles.ParticleEmitter#radial} particle movement on or off. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#setRadial - * @since 3.0.0 - * - * @param {boolean} [value=true] - Radial mode (true) or point mode (true). - * - * @return {this} This Particle Emitter. - */ - setRadial: function (value) - { - if (value === undefined) { value = true; } - - this.radial = value; - - return this; - }, - - /** - * Sets or modifies a rectangular boundary constraining the particles. - * - * To remove the boundary, set {@link Phaser.GameObjects.Particles.ParticleEmitter#bounds} to null. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#setBounds - * @since 3.0.0 - * - * @param {(number|Phaser.Types.GameObjects.Particles.ParticleEmitterBounds|Phaser.Types.GameObjects.Particles.ParticleEmitterBoundsAlt)} x - The x-coordinate of the left edge of the boundary, or an object representing a rectangle. - * @param {number} y - The y-coordinate of the top edge of the boundary. - * @param {number} width - The width of the boundary. - * @param {number} height - The height of the boundary. - * - * @return {this} This Particle Emitter. - */ - setBounds: function (x, y, width, height) - { - if (typeof x === 'object') - { - var obj = x; - - x = obj.x; - y = obj.y; - width = (HasValue(obj, 'w')) ? obj.w : obj.width; - height = (HasValue(obj, 'h')) ? obj.h : obj.height; - } - - if (this.bounds) - { - this.bounds.setTo(x, y, width, height); - } - else - { - this.bounds = new Rectangle(x, y, width, height); - } - - return this; - }, - - /** - * Sets the initial horizontal speed of emitted particles. - * Changes the emitter to point mode. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#setSpeedX - * @since 3.0.0 - * - * @param {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} value - The speed, in pixels per second. - * - * @return {this} This Particle Emitter. - */ - setSpeedX: function (value) - { - this.ops.speedX.onChange(value); - - // If you specify speedX and Y then it changes the emitter from radial to a point emitter - this.radial = false; - - return this; - }, - - /** - * Sets the initial vertical speed of emitted particles. - * Changes the emitter to point mode. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#setSpeedY - * @since 3.0.0 - * - * @param {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} value - The speed, in pixels per second. - * - * @return {this} This Particle Emitter. - */ - setSpeedY: function (value) - { - this.ops.speedY.onChange(value); - - // If you specify speedX and Y then it changes the emitter from radial to a point emitter - this.radial = false; - - return this; - }, - - /** - * Sets the initial radial speed of emitted particles. - * Changes the emitter to radial mode. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#setSpeed - * @since 3.0.0 - * - * @param {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} value - The speed, in pixels per second. - * - * @return {this} This Particle Emitter. - */ - setSpeed: function (value) - { - this.ops.speedX.onChange(value); - this.ops.speedY.active = false; - - // If you specify speedX and Y then it changes the emitter from radial to a point emitter - this.radial = true; - - return this; - }, - - /** - * Sets the horizontal scale of emitted particles. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#setScaleX - * @since 3.0.0 - * - * @param {(Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType|Phaser.Types.GameObjects.Particles.EmitterOpOnUpdateType)} value - The scale, relative to 1. - * - * @return {this} This Particle Emitter. - */ - setScaleX: function (value) - { - this.ops.scaleX.onChange(value); - - return this; - }, - - /** - * Sets the vertical scale of emitted particles. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#setScaleY - * @since 3.0.0 - * - * @param {(Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType|Phaser.Types.GameObjects.Particles.EmitterOpOnUpdateType)} value - The scale, relative to 1. - * - * @return {this} This Particle Emitter. - */ - setScaleY: function (value) - { - this.ops.scaleY.onChange(value); - - return this; - }, - - /** - * Sets the horizontal gravity applied to emitted particles. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#setGravityX - * @since 3.0.0 - * - * @param {number} value - Acceleration due to gravity, in pixels per second squared. - * - * @return {this} This Particle Emitter. - */ - setGravityX: function (value) - { - this.gravityX = value; - - return this; - }, - - /** - * Sets the vertical gravity applied to emitted particles. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#setGravityY - * @since 3.0.0 - * - * @param {number} value - Acceleration due to gravity, in pixels per second squared. - * - * @return {this} This Particle Emitter. - */ - setGravityY: function (value) - { - this.gravityY = value; - - return this; - }, - - /** - * Sets the gravity applied to emitted particles. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#setGravity - * @since 3.0.0 - * - * @param {number} x - Horizontal acceleration due to gravity, in pixels per second squared. - * @param {number} y - Vertical acceleration due to gravity, in pixels per second squared. - * - * @return {this} This Particle Emitter. - */ - setGravity: function (x, y) - { - this.gravityX = x; - this.gravityY = y; - - return this; - }, - - /** - * Sets the opacity of emitted particles. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#setAlpha - * @since 3.0.0 - * - * @param {(Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType|Phaser.Types.GameObjects.Particles.EmitterOpOnUpdateType)} value - A value between 0 (transparent) and 1 (opaque). - * - * @return {this} This Particle Emitter. - */ - setAlpha: function (value) - { - this.ops.alpha.onChange(value); - - return this; - }, - - /** - * Sets the color tint of emitted particles. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#setTint - * @since 3.22.0 - * @webglOnly - * - * @param {(Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType|Phaser.Types.GameObjects.Particles.EmitterOpOnUpdateType)} value - A value between 0 and 0xffffff. - * - * @return {this} This Particle Emitter. - */ - setTint: function (value) - { - this.ops.tint.onChange(value); - - return this; - }, - - /** - * Sets the angle of a {@link Phaser.GameObjects.Particles.ParticleEmitter#radial} particle stream. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#setEmitterAngle - * @since 3.0.0 - * - * @param {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} value - The angle of the initial velocity of emitted particles. - * - * @return {this} This Particle Emitter. - */ - setEmitterAngle: function (value) - { - this.ops.angle.onChange(value); - - return this; - }, - - /** - * Sets the lifespan of newly emitted particles. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#setLifespan - * @since 3.0.0 - * - * @param {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} value - The particle lifespan, in ms. - * - * @return {this} This Particle Emitter. - */ - setLifespan: function (value) - { - this.lifespan.onChange(value); - - return this; - }, - - /** - * Sets the number of particles released at each flow cycle or explosion. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#setQuantity - * @since 3.0.0 - * - * @param {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} quantity - The number of particles to release at each flow cycle or explosion. - * - * @return {this} This Particle Emitter. - */ - setQuantity: function (quantity) - { - this.quantity = quantity; - - return this; - }, - - /** - * Sets the emitter's {@link Phaser.GameObjects.Particles.ParticleEmitter#frequency} - * and {@link Phaser.GameObjects.Particles.ParticleEmitter#quantity}. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#setFrequency - * @since 3.0.0 - * - * @param {number} frequency - The time interval (>= 0) of each flow cycle, in ms; or -1 to put the emitter in explosion mode. - * @param {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} [quantity] - The number of particles to release at each flow cycle or explosion. - * - * @return {this} This Particle Emitter. - */ - setFrequency: function (frequency, quantity) - { - this.frequency = frequency; - - this.flowCounter = (frequency > 0) ? frequency : 0; - - if (quantity) - { - this.quantity = quantity; - } - - return this; - }, - - /** - * Adds a new Particle Death Zone to this Particle Emitter. - * - * This method is an alias for `ParticleEmitter#addDeathZone` and is retained for - * backward API compatibility only. Please note that calling this method multiple - * times will add multiple death zones to this Emitter. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#setDeathZone - * @since 3.0.0 - * - * @param {Phaser.GameObjects.Particles.Zones.DeathZone|Phaser.Types.GameObjects.Particles.ParticleEmitterDeathZoneConfig} zone - A Death Zone configuration object, or a Death Zone instance. - * - * @return {this} This Particle Emitter. - */ - setDeathZone: function (zoneConfig) - { - this.addDeathZone(zoneConfig); - - return this; - }, - - /** - * Adds a new Particle Death Zone to this Emitter. - * - * A particle is immediately killed as soon as its x/y coordinates intersect - * with any of the configured Death Zones. - * - * The `source` can be a Geometry Shape, such as a Circle, Rectangle or Triangle. - * Any valid object from the `Phaser.Geometry` namespace is allowed, as long as - * it supports a `contains` function. You can set the `type` to be either `onEnter` - * or `onLeave`. - * - * A Death Zone can only exist once within this Emitter. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#addDeathZone - * @since 3.60.0 - * - * @param {Phaser.Types.GameObjects.Particles.DeathZoneObject|Phaser.Types.GameObjects.Particles.DeathZoneObject[]} config - A Death Zone configuration object, a Death Zone instance, a valid Geometry object or an array of them. - * - * @return {Phaser.GameObjects.Particles.Zones.DeathZone} The Death Zone that was added to this Emitter. - */ - addDeathZone: function (config) - { - if (!Array.isArray(config)) - { - config = [ config ]; - } - - var zone; - - for (var i = 0; i < config.length; i++) - { - zone = config[i]; - - if (zone instanceof DeathZone) - { - Add(this.deathZones, zone); - } - else if (typeof zone.contains === 'function') - { - zone = new DeathZone(zone, true); - - Add(this.deathZones, zone); - } - else - { - var type = GetFastValue(zone, 'type', 'onEnter'); - var source = GetFastValue(zone, 'source', null); - - if (source && typeof source.contains === 'function') - { - var killOnEnter = (type === 'onEnter') ? true : false; - - zone = new DeathZone(source, killOnEnter); - - Add(this.deathZones, zone); - } - } - } - - return zone; - }, - - /** - * Removes the given Particle Death Zone from this Emitter. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#removeDeathZone - * @since 3.60.0 - * - * @param {Phaser.GameObjects.Particles.Zones.DeathZone} zone - The Death Zone that should be removed from this Emitter. - * - * @return {this} This Particle Emitter. - */ - removeDeathZone: function (zone) - { - Remove(this.deathZones, zone); - - return this; - }, - - /** - * Adds a new Particle Emission Zone to this Emitter. - * - * An {@link Phaser.Types.GameObjects.Particles.ParticleEmitterEdgeZoneConfig EdgeZone} places particles on its edges. - * Its {@link Phaser.Types.GameObjects.Particles.EdgeZoneSource source} can be a Curve, Path, Circle, Ellipse, Line, Polygon, Rectangle, or Triangle; - * or any object with a suitable {@link Phaser.Types.GameObjects.Particles.EdgeZoneSourceCallback getPoints} method. - * - * A {@link Phaser.Types.GameObjects.Particles.ParticleEmitterRandomZoneConfig RandomZone} places the particles randomly within its interior. - * Its {@link RandomZoneSource source} can be a Circle, Ellipse, Line, Polygon, Rectangle, or Triangle; or any object with a suitable {@link Phaser.Types.GameObjects.Particles.RandomZoneSourceCallback getRandomPoint} method. - * - * An Emission Zone can only exist once within this Emitter. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#addEmitZone - * @since 3.60.0 - * - * @param {Phaser.Types.GameObjects.Particles.EmitZoneObject|Phaser.Types.GameObjects.Particles.EmitZoneObject[]} zone - An Emission Zone configuration object, a RandomZone or EdgeZone instance, or an array of them. - * - * @return {Phaser.GameObjects.Particles.Zones.EdgeZone|Phaser.GameObjects.Particles.Zones.RandomZone} The Emission Zone that was added to this Emitter. - */ - addEmitZone: function (config) - { - if (!Array.isArray(config)) - { - config = [ config ]; - } - - var zone; - - for (var i = 0; i < config.length; i++) - { - zone = config[i]; - - if (zone instanceof RandomZone || zone instanceof EdgeZone) - { - Add(this.emitZones, zone); - } - else - { - // Where source = Geom like Circle, or a Path or Curve - // emitZone: { type: 'random', source: X } - // emitZone: { type: 'edge', source: X, quantity: 32, [stepRate=0], [yoyo=false], [seamless=true], [total=1] } - - var type = GetFastValue(zone, 'type', 'random'); - var source = GetFastValue(zone, 'source', null); - - if (type === 'random') - { - zone = new RandomZone(source); - } - else if (type === 'edge') - { - var quantity = GetFastValue(zone, 'quantity', 1); - var stepRate = GetFastValue(zone, 'stepRate', 0); - var yoyo = GetFastValue(zone, 'yoyo', false); - var seamless = GetFastValue(zone, 'seamless', true); - var total = GetFastValue(zone, 'total', -1); - - zone = new EdgeZone(source, quantity, stepRate, yoyo, seamless, total); - } - - if (zone) - { - Add(this.emitZones, zone); - } - } - } - - return zone; - }, - - /** - * Removes the given Particle Emission Zone from this Emitter. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#removeEmitZone - * @since 3.60.0 - * - * @param {Phaser.GameObjects.Particles.Zones.EdgeZone|Phaser.GameObjects.Particles.Zones.RandomZone} zone - The Emission Zone that should be removed from this Emitter. - * - * @return {this} This Particle Emitter. - */ - removeEmitZone: function (zone) - { - Remove(this.emitZones, zone); - - this.zoneIndex = 0; - - return this; - }, - - /** - * Takes the given particle and sets its x/y coordinates to match the next available - * emission zone, if any have been configured. This method is called automatically - * as part of the `Particle.fire` process. - * - * The Emit Zones are iterated in sequence. Once a zone has had a particle emitted - * from it, then the next zone is used and so on, in a loop. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#getEmitZone - * @since 3.60.0 - * - * @param {Phaser.GameObjects.Particles.Particle} particle - The particle to set the emission zone for. - */ - getEmitZone: function (particle) - { - var zones = this.emitZones; - var len = zones.length; - - if (len === 0) - { - return; - } - else - { - var zone = zones[this.zoneIndex]; - - zone.getPoint(particle); - - if (zone.total > -1) - { - this.zoneTotal++; - - if (this.zoneTotal >= zone.total) - { - this.zoneTotal = 0; - - this.zoneIndex = Wrap(this.zoneIndex + 1, 0, len); - } - } - } - }, - - /** - * Takes the given particle and checks to see if any of the configured Death Zones - * will kill it and returns the result. This method is called automatically as part - * of the `Particle.update` process. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#getDeathZone - * @fires Phaser.GameObjects.Particles.Events#DEATH_ZONE - * @since 3.60.0 - * - * @param {Phaser.GameObjects.Particles.Particle} particle - The particle to test against the Death Zones. - * - * @return {boolean} `true` if the particle should be killed, otherwise `false`. - */ - getDeathZone: function (particle) - { - var zones = this.deathZones; - - for (var i = 0; i < zones.length; i++) - { - if (zones[i].willKill(particle)) - { - this.emit(Events.DEATH_ZONE, this, particle, zones[i]); - - return true; - } - } - - return false; - }, - - /** - * Changes the currently active Emission Zone. The zones should have already - * been added to this Emitter either via the emitter config, or the - * `addEmitZone` method. - * - * Call this method by passing either a numeric zone index value, or - * the zone instance itself. - * - * Prior to v3.60 an Emitter could only have a single Emit Zone and this - * method was how you set it. From 3.60 and up it now performs a different - * function and swaps between all available active zones. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#setEmitZone - * @since 3.0.0 - * - * @param {number|Phaser.GameObjects.Particles.Zones.EdgeZone|Phaser.GameObjects.Particles.Zones.RandomZone} zone - The Emit Zone to set as the active zone. - * - * @return {this} This Particle Emitter. - */ - setEmitZone: function (zone) - { - var index; - - if (isFinite(zone)) - { - index = zone; - } - else - { - index = this.emitZones.indexOf(zone); - } - - if (index >= 0) - { - this.zoneIndex = index; - } - - return this; - }, - - /** - * Adds a Particle Processor, such as a Gravity Well, to this Emitter Manager. - * - * It will start processing particles from the next update as long as its `active` - * property is set. - * - * @method Phaser.GameObjects.Particles.ParticleEmitterManager#addParticleProcessor - * @since 3.60.0 - * - * @param {Phaser.GameObjects.Particles.ParticleProcessor} processor - The Particle Processor to add to this Emitter Manager. - * - * @return {Phaser.GameObjects.Particles.ParticleProcessor} The Particle Processor that was added to this Emitter Manager. - */ - addParticleProcessor: function (processor) - { - if (!this.processors.exists(processor)) - { - if (processor.emitter) - { - processor.emitter.removeParticleProcessor(processor); - } - - this.processors.add(processor); - - processor.emitter = this; - } - - return processor; - }, - - /** - * Removes a Particle Processor from this Emitter Manager. - * - * The Processor must belong to this Manager. - * - * It is not destroyed when removed, allowing you to move it to another Emitter Manager, - * so if you no longer require it you should call its `destroy` method directly. - * - * @method Phaser.GameObjects.Particles.ParticleEmitterManager#removeParticleProcessor - * @since 3.60.0 - * - * @param {Phaser.GameObjects.Particles.ParticleProcessor} processor - The Particle Processor to remove from this Emitter Manager. - * - * @return {?Phaser.GameObjects.Particles.ParticleProcessor} The Particle Processor that was removed, or null if it could not be found. - */ - removeParticleProcessor: function (processor) - { - if (this.processors.exists(processor)) - { - this.processors.remove(processor, true); - - processor.emitter = null; - } - - return processor; - }, - - /** - * Creates a new Gravity Well, adds it to this Emitter Manager and returns a reference to it. - * - * @method Phaser.GameObjects.Particles.ParticleEmitterManager#createGravityWell - * @since 3.0.0 - * - * @param {Phaser.Types.GameObjects.Particles.GravityWellConfig} config - Configuration settings for the Gravity Well to create. - * - * @return {Phaser.GameObjects.Particles.GravityWell} The Gravity Well that was created. - */ - createGravityWell: function (config) - { - return this.addParticleProcessor(new GravityWell(config)); - }, - - /** - * Creates inactive particles and adds them to this emitter's pool. - * - * If `ParticleEmitter.maxParticles` is set it will limit the - * value passed to this method to make sure it's not exceeded. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#reserve - * @since 3.0.0 - * - * @param {number} particleCount - The number of particles to create. - * - * @return {this} This Particle Emitter. - */ - reserve: function (particleCount) - { - var dead = this.dead; - - if (this.maxParticles > 0) - { - var total = this.getParticleCount(); - - if (total + particleCount > this.maxParticles) - { - particleCount = this.maxParticles - (total + particleCount); - } - } - - for (var i = 0; i < particleCount; i++) - { - dead.push(new this.particleClass(this)); - } - - return this; - }, - - /** - * Gets the number of active (in-use) particles in this emitter. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#getAliveParticleCount - * @since 3.0.0 - * - * @return {number} The number of particles with `active=true`. - */ - getAliveParticleCount: function () - { - return this.alive.length; - }, - - /** - * Gets the number of inactive (available) particles in this emitter. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#getDeadParticleCount - * @since 3.0.0 - * - * @return {number} The number of particles with `active=false`. - */ - getDeadParticleCount: function () - { - return this.dead.length; - }, - - /** - * Gets the total number of particles in this emitter. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#getParticleCount - * @since 3.0.0 - * - * @return {number} The number of particles, including both alive and dead. - */ - getParticleCount: function () - { - return this.getAliveParticleCount() + this.getDeadParticleCount(); - }, - - /** - * Whether this emitter is at either its hard-cap limit (maxParticles), if set, or - * the max allowed number of 'alive' particles (maxAliveParticles). - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#atLimit - * @since 3.0.0 - * - * @return {boolean} Returns `true` if this Emitter is at its limit, or `false` if no limit, or below the `maxParticles` level. - */ - atLimit: function () - { - if (this.maxParticles > 0 && this.getParticleCount() >= this.maxParticles) - { - return true; - } - - return (this.maxAliveParticles > 0 && this.getAliveParticleCount() >= this.maxAliveParticles); - }, - - /** - * Sets a function to call for each newly emitted particle. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#onParticleEmit - * @since 3.0.0 - * - * @param {Phaser.Types.GameObjects.Particles.ParticleEmitterCallback} callback - The function. - * @param {*} [context] - The calling context. - * - * @return {this} This Particle Emitter. - */ - onParticleEmit: function (callback, context) - { - if (callback === undefined) - { - // Clear any previously set callback - this.emitCallback = null; - this.emitCallbackScope = null; - } - else if (typeof callback === 'function') - { - this.emitCallback = callback; - - if (context) - { - this.emitCallbackScope = context; - } - } - - return this; - }, - - /** - * Sets a function to call for each particle death. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#onParticleDeath - * @since 3.0.0 - * - * @param {Phaser.Types.GameObjects.Particles.ParticleDeathCallback} callback - The function. - * @param {*} [context] - The function's calling context. - * - * @return {this} This Particle Emitter. - */ - 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; - }, - - /** - * Deactivates every particle in this emitter immediately. - * - * This particles are killed but do not emit an event or callback. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#killAll - * @since 3.0.0 - * - * @return {this} This Particle Emitter. - */ - killAll: function () - { - var dead = this.dead; - var alive = this.alive; - - while (alive.length > 0) - { - dead.push(alive.pop()); - } - - return this; - }, - - /** - * Calls a function for each active particle in this emitter. The function is - * sent two parameters: a reference to the Particle instance and to this Emitter. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#forEachAlive - * @since 3.0.0 - * - * @param {Phaser.Types.GameObjects.Particles.ParticleEmitterCallback} callback - The function. - * @param {*} context - The functions calling context. - * - * @return {this} This Particle Emitter. - */ - forEachAlive: function (callback, context) - { - var alive = this.alive; - var length = alive.length; - - for (var i = 0; i < length; i++) - { - // Sends the Particle and the Emitter - callback.call(context, alive[i], this); - } - - return this; - }, - - /** - * Calls a function for each inactive particle in this emitter. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#forEachDead - * @since 3.0.0 - * - * @param {Phaser.Types.GameObjects.Particles.ParticleEmitterCallback} callback - The function. - * @param {*} context - The functions calling context. - * - * @return {this} This Particle Emitter. - */ - forEachDead: function (callback, context) - { - var dead = this.dead; - var length = dead.length; - - for (var i = 0; i < length; i++) - { - callback.call(context, dead[i], this); - } - - return this; - }, - - /** - * Turns {@link Phaser.GameObjects.Particles.ParticleEmitter#on} the emitter and resets the flow counter. - * - * If this emitter is in flow mode (frequency >= 0; the default), the particle flow will start (or restart). - * - * If this emitter is in explode mode (frequency = -1), nothing will happen. - * Use {@link Phaser.GameObjects.Particles.ParticleEmitter#explode} or {@link Phaser.GameObjects.Particles.ParticleEmitter#flow} instead. - * - * Calling this method will emit the `START` event. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#start - * @fires Phaser.GameObjects.Particles.Events#START - * @since 3.0.0 - * - * @param {number} [advance=0] - Advance this number of ms in time through the emitter. - * @param {number} [duration=0] - Limit this emitter to only emit particles for the given number of ms. Setting this parameter will override any duration already set in the Emitter configuration object. - * - * @return {this} This Particle Emitter. - */ - start: function (advance, duration) - { - if (advance === undefined) { advance = 0; } - - if (!this.emitting) - { - if (advance > 0) - { - this.fastForward(advance); - } - - this.emitting = true; - - this.resetCounters(this.frequency, true); - - if (duration !== undefined) - { - this.duration = Math.abs(duration); - } - - this.emit(Events.START, this); - } - - return this; - }, - - /** - * Turns {@link Phaser.GameObjects.Particles.ParticleEmitter#on off} the emitter and - * stops it from emitting further particles. Currently alive particles will remain - * active until they naturally expire unless you set the `kill` parameter to `true`. - * - * Calling this method will emit the `STOP` event. When the final particle has - * expired the `COMPLETE` event will be emitted. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#stop - * @fires Phaser.GameObjects.Particles.Events#STOP - * @since 3.11.0 - * - * @param {boolean} [kill=false] - Kill all particles immediately (true), or leave them to die after their lifespan expires? (false, the default) - * - * @return {this} This Particle Emitter. - */ - stop: function (kill) - { - if (kill === undefined) { kill = false; } - - if (this.emitting) - { - this.emitting = false; - - if (kill) - { - this.killAll(); - } - - this.emit(Events.STOP, this); - } - - return this; - }, - - /** - * {@link Phaser.GameObjects.Particles.ParticleEmitter#active Deactivates} the emitter. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#pause - * @since 3.0.0 - * - * @return {this} This Particle Emitter. - */ - pause: function () - { - this.active = false; - - return this; - }, - - /** - * {@link Phaser.GameObjects.Particles.ParticleEmitter#active Activates} the emitter. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#resume - * @since 3.0.0 - * - * @return {this} This Particle Emitter. - */ - resume: function () - { - this.active = true; - - return this; - }, - - /** - * Set the property by which active particles are sorted prior to be rendered. - * - * It allows you to control the rendering order of the particles. - * - * This can be any valid property of the `Particle` class, such as `y`, `alpha` - * or `lifeT`. - * - * The 'alive' particles array is sorted in place each game frame. Setting a - * sort property will override the `particleBringToTop` setting. - * - * If you wish to use your own sorting function, see `setSortCallback` instead. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#setSortProperty - * @since 3.60.0 - * - * @param {string} [property] - The property on the `Particle` class to sort by. - * @param {boolean} [ascending=true] - Should the particles be sorted in ascending or descending order? - * - * @return {this} This Particle Emitter. - */ - setSortProperty: function (property, ascending) - { - if (property === undefined) { property = ''; } - if (ascending === undefined) { ascending = this.true; } - - this.sortProperty = property; - this.sortOrderAsc = ascending; - this.sortCallback = this.depthSortCallback; - - return this; - }, - - /** - * Sets a callback to be used to sort the particles before rendering each frame. - * - * This allows you to define your own logic and behavior in the callback. - * - * The callback will be sent two parameters: the two Particles being compared, - * and must adhere to the criteria of the `compareFn` in `Array.sort`: - * - * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#description - * - * Call this method with no parameters to reset the sort callback. - * - * Setting your own callback will override both the `particleBringToTop` and - * `sortProperty` settings of this Emitter. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#setSortCallback - * @since 3.60.0 - * - * @param {Phaser.Types.GameObjects.Particles.ParticleSortCallback} [callback] - The callback to invoke when the particles are sorted. Leave undefined to reset to the default. - * - * @return {this} This Particle Emitter. - */ - setSortCallback: function (callback) - { - if (this.sortProperty !== '') - { - callback = this.depthSortCallback; - } - else - { - callback = null; - } - - this.sortCallback = callback; - - return this; - }, - - /** - * Sorts active particles with {@link Phaser.GameObjects.Particles.ParticleEmitter#depthSortCallback}. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#depthSort - * @since 3.0.0 - * - * @return {this} This Particle Emitter. - */ - depthSort: function () - { - StableSort(this.alive, this.sortCallback.bind(this)); - - return this; - }, - - /** - * Calculates the difference of two particles, for sorting them by depth. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#depthSortCallback - * @since 3.0.0 - * - * @param {object} a - The first particle. - * @param {object} b - The second particle. - * - * @return {number} The difference of a and b's y coordinates. - */ - depthSortCallback: function (a, b) - { - var key = this.sortProperty; - - if (this.sortOrderAsc) - { - return a[key] - b[key]; - } - else - { - return b[key] - a[key]; - } - }, - - /** - * Puts the emitter in flow mode (frequency >= 0) and starts (or restarts) a particle flow. - * - * To resume a flow at the current frequency and quantity, use {@link Phaser.GameObjects.Particles.ParticleEmitter#start} instead. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#flow - * @fires Phaser.GameObjects.Particles.Events#START - * @since 3.0.0 - * - * @param {number} frequency - The time interval (>= 0) of each flow cycle, in ms. - * @param {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} [count=1] - The number of particles to emit at each flow cycle. - * @param {number} [stopAfter] - Stop this emitter from firing any more particles once this value is reached. Set to zero for unlimited. Setting this parameter will override any `stopAfter` value already set in the Emitter configuration object. - * - * @return {this} This Particle Emitter. - */ - flow: function (frequency, count, stopAfter) - { - if (count === undefined) { count = 1; } - - this.emitting = false; - - this.frequency = frequency; - this.quantity = count; - - if (stopAfter !== undefined) - { - this.stopAfter = stopAfter; - } - - return this.start(); - }, - - /** - * Puts the emitter in explode mode (frequency = -1), stopping any current particle flow, and emits several particles all at once. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#explode - * @fires Phaser.GameObjects.Particles.Events#EXPLODE - * @since 3.0.0 - * - * @param {number} [count=this.quantity] - The number of Particles to emit. - * @param {number} [x=this.x] - The x coordinate to emit the Particles from. - * @param {number} [y=this.x] - The y coordinate to emit the Particles from. - * - * @return {Phaser.GameObjects.Particles.Particle} The most recently emitted Particle. - */ - explode: function (count, x, y) - { - this.frequency = -1; - - this.resetCounters(-1, true); - - var particle = this.emitParticle(count, x, y); - - this.emit(Events.EXPLODE, this, particle); - - return particle; - }, - - /** - * Emits particles at the given position. If no position is given, it will - * emit from this Emitters current location. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#emitParticleAt - * @since 3.0.0 - * - * @param {number} [x=this.x] - The x coordinate to emit the Particles from. - * @param {number} [y=this.x] - The y coordinate to emit the Particles from. - * @param {number} [count=this.quantity] - The number of Particles to emit. - * - * @return {Phaser.GameObjects.Particles.Particle} The most recently emitted Particle. - */ - emitParticleAt: function (x, y, count) - { - return this.emitParticle(count, x, y); - }, - - /** - * Emits particles at a given position (or the emitters current position). - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#emitParticle - * @since 3.0.0 - * - * @param {number} [count=this.quantity] - The number of Particles to emit. - * @param {number} [x=this.x] - The x coordinate to emit the Particles from. - * @param {number} [y=this.x] - The y coordinate to emit the Particles from. - * - * @return {Phaser.GameObjects.Particles.Particle} The most recently emitted Particle. - * - * @see Phaser.GameObjects.Particles.Particle#fire - */ - emitParticle: function (count, x, y) - { - if (this.atLimit()) - { - return; - } - - if (count === undefined) - { - count = this.ops.quantity.onEmit(); - } - - var dead = this.dead; - var counters = this.counters; - var stopAfter = this.stopAfter; - - var followX = (this.follow) ? this.follow.x + this.followOffset.x : x; - var followY = (this.follow) ? this.follow.y + this.followOffset.y : y; - - for (var i = 0; i < count; i++) - { - var particle = dead.pop(); - - if (!particle) - { - particle = new this.particleClass(this); - } - - particle.fire(followX, followY); - - if (this.particleBringToTop) - { - this.alive.push(particle); - } - else - { - this.alive.unshift(particle); - } - - if (this.emitCallback) - { - this.emitCallback.call(this.emitCallbackScope, particle, this); - } - - if (stopAfter > 0) - { - counters[4]++; - - if (counters[4] >= stopAfter) - { - break; - } - } - - if (this.atLimit()) - { - break; - } - } - - return particle; - }, - - /** - * Fast forwards this Particle Emitter and all of its particles. - * - * Works by running the Emitter `preUpdate` handler in a loop until the `time` - * has been reached at `delta` steps per loop. - * - * All callbacks and emitter related events that would normally be fired - * will still be invoked. - * - * You can make an emitter 'fast forward' via the emitter config using the - * `advance` property. Set this value to the number of ms you wish the - * emitter to be fast-forwarded by. Or, call this method post-creation. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#fastForward - * @since 3.60.0 - * - * @param {number} time - The number of ms to advance the Particle Emitter by. - * @param {number} [delta] - The amount of delta to use for each step. Defaults to 1000 / 60. - * - * @return {this} This Particle Emitter. - */ - fastForward: function (time, delta) - { - if (delta === undefined) { delta = 1000 / 60; } - - var total = 0; - - this.skipping = true; - - while (total < Math.abs(time)) - { - this.preUpdate(0, delta); - - total += delta; - } - - this.skipping = false; - - return this; - }, - - /** - * Updates this emitter and its particles. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#preUpdate - * @fires Phaser.GameObjects.Particles.Events#COMPLETE - * @since 3.0.0 - * - * @param {number} time - The current timestamp as generated by the Request Animation Frame or SetTimeout. - * @param {number} delta - The delta time, in ms, elapsed since the last frame. - */ - preUpdate: function (time, delta) - { - // Scale the delta - delta *= this.timeScale; - - var step = (delta / 1000); - - if (this.trackVisible) - { - this.visible = this.follow.visible; - } - - // Any particle processors? - var processors = this.getProcessors(); - - var particles = this.alive; - var dead = this.dead; - - var i = 0; - var rip = []; - var length = particles.length; - - for (i = 0; i < length; i++) - { - var particle = particles[i]; - - // update returns `true` if the particle is now dead (lifeCurrent <= 0) - if (particle.update(delta, step, processors)) - { - rip.push({ index: i, particle: particle }); - } - } - - // Move dead particles to the dead array - length = rip.length; - - if (length > 0) - { - var deathCallback = this.deathCallback; - var deathCallbackScope = this.deathCallbackScope; - - for (i = length - 1; i >= 0; i--) - { - var entry = rip[i]; - - // Remove from particles array - particles.splice(entry.index, 1); - - // Add to dead array - dead.push(entry.particle); - - // Callback - if (deathCallback) - { - deathCallback.call(deathCallbackScope, entry.particle); - } - - entry.particle.setPosition(); - } - } - - var counters = this.counters; - - if (!this.emitting && !this.skipping) - { - if (counters[5] === 1 && particles.length === 0) - { - // completeFlag - counters[5] = 0; - - this.emit(Events.COMPLETE, this); - } - - return; - } - - if (this.frequency === 0) - { - this.emitParticle(); - } - else if (this.frequency > 0) - { - // counter - this.flowCounter -= delta; - - if (this.flowCounter <= 0) - { - // Emits the 'quantity' number of particles - this.emitParticle(); - - // counter = frequency - remainder from previous delta - this.flowCounter += this.frequency; - } - } - - // Duration or stopAfter set? - if (!this.skipping) - { - if (this.duration > 0) - { - // elapsed - counters[3] += delta; - - if (counters[3] >= this.duration) - { - this.stop(); - } - } - - if (this.stopAfter > 0 && counters[4] >= this.stopAfter) - { - this.stop(); - } - } - }, - - /** - * Gets the world transform matrix for this Particle Emitter, factoring in any parents. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#getWorldTransformMatrix - * @since 3.60.0 - * - * @param {Phaser.GameObjects.Components.TransformMatrix} [tempMatrix] - The matrix to populate with the values from this Game Object. - * @param {Phaser.GameObjects.Components.TransformMatrix} [parentMatrix] - A temporary matrix to hold parent values during the calculations. - * - * @return {Phaser.GameObjects.Components.TransformMatrix} The populated Transform Matrix. - getWorldTransformMatrix: function (tempMatrix, parentMatrix) - { - if (tempMatrix === undefined) { tempMatrix = this.tempMatrix1; } - if (parentMatrix === undefined) { parentMatrix = this.tempMatrix2; } - - var parent = this.manager; - - tempMatrix.applyITRS(0, 0, 0, 1, 1); - - while (parent) - { - parentMatrix.applyITRS(parent.x, parent.y, parent._rotation, parent._scaleX, parent._scaleY); - - parentMatrix.multiply(tempMatrix, tempMatrix); - - parent = parent.parentContainer; - } - - return tempMatrix; - }, - */ - - /** - * Takes either a Rectangle Geometry object or an Arcade Physics Body and tests - * to see if it intersects with any currently alive Particle in this Emitter. - * - * Overlapping particles are returned in an array, where you can perform further - * processing on them. If nothing overlaps then the array will be empty. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#overlap - * @since 3.60.0 - * - * @param {(Phaser.Geom.Rectangle|Phaser.Physics.Arcade.Body)} target - A Rectangle or Arcade Physics Body to check for intersection against all alive particles. - * - * @return {Phaser.GameObjects.Particles.Particle[]} An array of Particles that overlap with the given target. - */ - overlap: function (target) - { - var matrix = this.getWorldTransformMatrix(); - - var alive = this.alive; - var length = alive.length; - - var output = []; - - for (var i = 0; i < length; i++) - { - var particle = alive[i]; - - if (RectangleToRectangle(target, particle.getBounds(matrix))) - { - output.push(particle); - } - } - - return output; - }, - - /** - * Returns a bounds Rectangle calculated from the bounds of all currently - * _active_ Particles in this Emitter. If this Emitter has only just been - * created and not yet rendered, then calling this method will return a Rectangle - * with a max safe integer for dimensions. Use the `advance` parameter to - * avoid this. - * - * Typically it takes a few seconds for a flow Emitter to 'warm up'. You can - * use the `advance` and `delta` parameters to force the Emitter to - * 'fast forward' in time to try and allow the bounds to be more accurate, - * as it will calculate the bounds based on the particle bounds across all - * timesteps, giving a better result. - * - * You can also use the `padding` parameter to increase the size of the - * bounds. Emitters with a lot of randomness in terms of direction or lifespan - * can often return a bounds smaller than their possible maximum. By using - * the `padding` (and `advance` if needed) you can help limit this. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#getBounds - * @since 3.60.0 - * - * @param {number} [padding] - The amount of padding, in pixels, to add to the bounds Rectangle. - * @param {number} [advance] - The number of ms to advance the Particle Emitter by. Defaults to 0, i.e. not used. - * @param {number} [delta] - The amount of delta to use for each step. Defaults to 1000 / 60. - * @param {Phaser.Geom.Rectangle} [output] - The Rectangle to store the results in. If not given a new one will be created. - * - * @return {Phaser.Geom.Rectangle} A Rectangle containing the calculated bounds of this Emitter. - */ - getBounds: function (padding, advance, delta, output) - { - if (padding === undefined) { padding = 0; } - if (advance === undefined) { advance = 0; } - if (delta === undefined) { delta = 1000 / 60; } - if (output === undefined) { output = new Rectangle(); } - - var matrix = this.getWorldTransformMatrix(); - - var i; - var bounds; - var alive = this.alive; - var setFirst = false; - - output.setTo(0, 0, 0, 0); - - if (advance > 0) - { - var total = 0; - - this.skipping = true; - - while (total < Math.abs(advance)) - { - this.preUpdate(0, delta); - - for (i = 0; i < alive.length; i++) - { - bounds = alive[i].getBounds(matrix); - - if (!setFirst) - { - setFirst = true; - - CopyFrom(bounds, output); - } - else - { - MergeRect(output, bounds); - } - } - - total += delta; - } - - this.skipping = false; - } - else - { - for (i = 0; i < alive.length; i++) - { - bounds = alive[i].getBounds(matrix); - - if (!setFirst) - { - setFirst = true; - - CopyFrom(bounds, output); - } - else - { - MergeRect(output, bounds); - } - } - } - - if (padding > 0) - { - Inflate(output, padding, padding); - } - - return output; - }, - - /** - * The x coordinate the particles are emitted from. - * - * This is relative to the Emitters x coordinate and that of any parent. - * - * Accessing this property should typically return a number. - * However, it can be set to any valid EmitterOp onEmit type. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#particleX - * @type {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} - * @since 3.60.0 - */ - particleX: { - - get: function () - { - return this.ops.x.current; - }, - - set: function (value) - { - this.ops.x.onChange(value); - } - - }, - - /** - * The y coordinate the particles are emitted from. - * - * This is relative to the Emitters x coordinate and that of any parent. - * - * Accessing this property should typically return a number. - * However, it can be set to any valid EmitterOp onEmit type. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#particleY - * @type {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} - * @since 3.60.0 - */ - particleY: { - - get: function () - { - return this.ops.y.current; - }, - - set: function (value) - { - this.ops.y.onChange(value); - } - - }, - - /** - * The horizontal acceleration applied to emitted particles, in pixels per second squared. - * - * Accessing this property should typically return a number. - * However, it can be set to any valid EmitterOp onEmit type. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#accelerationX - * @type {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} - * @since 3.60.0 - */ - accelerationX: { - - get: function () - { - return this.ops.accelerationX.current; - }, - - set: function (value) - { - this.ops.accelerationX.onChange(value); - } - - }, - - /** - * The vertical acceleration applied to emitted particles, in pixels per second squared. - * - * Accessing this property should typically return a number. - * However, it can be set to any valid EmitterOp onEmit type. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#accelerationY - * @type {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} - * @since 3.60.0 - */ - accelerationY: { - - get: function () - { - return this.ops.accelerationY.current; - }, - - set: function (value) - { - this.ops.accelerationY.onChange(value); - } - - }, - - /** - * The maximum horizontal velocity emitted particles can reach, in pixels per second squared. - * - * Accessing this property should typically return a number. - * However, it can be set to any valid EmitterOp onEmit type. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#maxVelocityX - * @type {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} - * @since 3.60.0 - * @default 10000 - */ - maxVelocityX: { - - get: function () - { - return this.ops.maxVelocityX.current; - }, - - set: function (value) - { - this.ops.maxVelocityX.onChange(value); - } - - }, - - /** - * The maximum vertical velocity emitted particles can reach, in pixels per second squared. - * - * Accessing this property should typically return a number. - * However, it can be set to any valid EmitterOp onEmit type. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#maxVelocityY - * @type {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} - * @since 3.60.0 - * @default 10000 - */ - maxVelocityY: { - - get: function () - { - return this.ops.maxVelocityY.current; - }, - - set: function (value) - { - this.ops.maxVelocityY.onChange(value); - } - - }, - - /** - * The initial speed of emitted particles, in pixels per second. - * - * If using this as a getter it will return the `speedX` value. - * - * If using it as a setter it will update both `speedX` and `speedY` to the - * given value. - * - * Accessing this property should typically return a number. - * However, it can be set to any valid EmitterOp onEmit type. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#speed - * @type {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} - * @since 3.60.0 - */ - speed: { - - get: function () - { - return this.ops.speedX.current; - }, - - set: function (value) - { - this.ops.speedX.onChange(value); - this.ops.speedY.onChange(value); - } - - }, - - /** - * The initial horizontal speed of emitted particles, in pixels per second. - * - * Accessing this property should typically return a number. - * However, it can be set to any valid EmitterOp onEmit type. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#speedX - * @type {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} - * @since 3.60.0 - */ - speedX: { - - get: function () - { - return this.ops.speedX.current; - }, - - set: function (value) - { - this.ops.speedX.onChange(value); - } - - }, - - /** - * The initial vertical speed of emitted particles, in pixels per second. - * - * Accessing this property should typically return a number. - * However, it can be set to any valid EmitterOp onEmit type. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#speedY - * @type {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} - * @since 3.60.0 - */ - speedY: { - - get: function () - { - return this.ops.speedY.current; - }, - - set: function (value) - { - this.ops.speedY.onChange(value); - } - - }, - - /** - * The x coordinate emitted particles move toward, when {@link Phaser.GameObjects.Particles.ParticleEmitter#moveTo} is true. - * - * Accessing this property should typically return a number. - * However, it can be set to any valid EmitterOp onEmit type. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#moveToX - * @type {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} - * @since 3.60.0 - */ - moveToX: { - - get: function () - { - return this.ops.moveToX.current; - }, - - set: function (value) - { - this.ops.moveToX.onChange(value); - } - - }, - - /** - * The y coordinate emitted particles move toward, when {@link Phaser.GameObjects.Particles.ParticleEmitter#moveTo} is true. - * - * Accessing this property should typically return a number. - * However, it can be set to any valid EmitterOp onEmit type. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#moveToY - * @type {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} - * @since 3.60.0 - */ - moveToY: { - - get: function () - { - return this.ops.moveToY.current; - }, - - set: function (value) - { - this.ops.moveToY.onChange(value); - } - - }, - - /** - * The amount of velocity particles will use when rebounding off the - * emitter bounds, if set. A value of 0 means no bounce. A value of 1 - * means a full rebound. - * - * Accessing this property should typically return a number. - * However, it can be set to any valid EmitterOp onEmit type. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#bounce - * @type {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} - * @since 3.60.0 - */ - bounce: { - - get: function () - { - return this.ops.bounce.current; - }, - - set: function (value) - { - this.ops.bounce.onChange(value); - } - - }, - - /** - * The horizontal scale of emitted particles. - * - * This is relative to the Emitters scale and that of any parent. - * - * Accessing this property should typically return a number. - * However, it can be set to any valid EmitterOp onEmit type. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#particleScaleX - * @type {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} - * @since 3.60.0 - */ - particleScaleX: { - - get: function () - { - return this.ops.scaleX.current; - }, - - set: function (value) - { - this.ops.scaleX.onChange(value); - } - - }, - - /** - * The vertical scale of emitted particles. - * - * This is relative to the Emitters scale and that of any parent. - * - * Accessing this property should typically return a number. - * However, it can be set to any valid EmitterOp onEmit type. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#particleScaleY - * @type {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} - * @since 3.60.0 - */ - particleScaleY: { - - get: function () - { - return this.ops.scaleY.current; - }, - - set: function (value) - { - this.ops.scaleY.onChange(value); - } - - }, - - /** - * A color tint value that is applied to the texture of the emitted - * particle. The value should be given in hex format, i.e. 0xff0000 - * for a red tint, and should not include the alpha channel. - * - * Tints are additive, meaning a tint value of white (0xffffff) will - * effectively reset the tint to nothing. - * - * This is a WebGL only feature. - * - * Accessing this property should typically return a number. - * However, it can be set to any valid EmitterOp onEmit type. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#particleTint - * @type {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} - * @since 3.60.0 - */ - particleTint: { - - get: function () - { - return this.ops.tint.current; - }, - - set: function (value) - { - this.ops.tint.onChange(value); - } - - }, - - /** - * The alpha value of the emitted particles. This is a value - * between 0 and 1. Particles with alpha zero are invisible - * and are therefore not rendered, but are still processed - * by the Emitter. - * - * Accessing this property should typically return a number. - * However, it can be set to any valid EmitterOp onEmit type. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#particleAlpha - * @type {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} - * @since 3.60.0 - */ - particleAlpha: { - - get: function () - { - return this.ops.alpha.current; - }, - - set: function (value) - { - this.ops.alpha.onChange(value); - } - - }, - - /** - * The lifespan of the emitted particles. This value is given - * in milliseconds and defaults to 1000ms (1 second). When a - * particle reaches this amount it is killed. - * - * Accessing this property should typically return a number. - * However, it can be set to any valid EmitterOp onEmit type. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#lifespan - * @type {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} - * @since 3.60.0 - */ - lifespan: { - - get: function () - { - return this.ops.lifespan.current; - }, - - set: function (value) - { - this.ops.lifespan.onChange(value); - } - - }, - - /** - * The angle at which the particles are emitted. The values are - * given in degrees. This allows you to control the direction - * of the emitter. If you wish instead to change the rotation - * of the particles themselves, see the `particleRotate` property. - * - * Accessing this property should typically return a number. - * However, it can be set to any valid EmitterOp onEmit type. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#particleAngle - * @type {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} - * @since 3.60.0 - */ - particleAngle: { - - get: function () - { - return this.ops.angle.current; - }, - - set: function (value) - { - this.ops.angle.onChange(value); - } - - }, - - /** - * The rotation (or angle) of each particle when it is emitted. - * The value is given in degrees and uses a right-handed - * coordinate system, where 0 degrees points to the right, 90 degrees - * points down and -90 degrees points up. - * - * Accessing this property should typically return a number. - * However, it can be set to any valid EmitterOp onEmit type. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#particleRotate - * @type {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} - * @since 3.60.0 - */ - particleRotate: { - - get: function () - { - return this.ops.rotate.current; - }, - - set: function (value) - { - this.ops.rotate.onChange(value); - } - - }, - - /** - * The number of particles that are emitted each time an emission - * occurs, i.e. from one 'explosion' or each frame in a 'flow' cycle. - * - * The default is 1. - * - * Accessing this property should typically return a number. - * However, it can be set to any valid EmitterOp onEmit type. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#quantity - * @type {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} - * @see Phaser.GameObjects.Particles.ParticleEmitter#setFrequency - * @see Phaser.GameObjects.Particles.ParticleEmitter#setQuantity - * @since 3.60.0 - */ - quantity: { - - get: function () - { - return this.ops.quantity.current; - }, - - set: function (value) - { - this.ops.quantity.onChange(value); - } - - }, - - /** - * The number of milliseconds to wait after emission before - * the particles start updating. This allows you to emit particles - * that appear 'static' or still on-screen and then, after this value, - * begin to move. - * - * Accessing this property should typically return a number. - * However, it can be set to any valid EmitterOp onEmit type. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#delay - * @type {Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType} - * @since 3.60.0 - */ - delay: { - - get: function () - { - return this.ops.delay.current; - }, - - set: function (value) - { - this.ops.delay.onChange(value); - } - - }, - - /** - * The internal flow counter. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#flowCounter - * @type {number} - * @since 3.60.0 - */ - flowCounter: { - - get: function () - { - return this.counters[0]; - }, - - set: function (value) - { - this.counters[0] = value; - } - - }, - - /** - * The internal frame counter. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#frameCounter - * @type {number} - * @since 3.60.0 - */ - frameCounter: { - - get: function () - { - return this.counters[1]; - }, - - set: function (value) - { - this.counters[1] = value; - } - - }, - - /** - * The internal anim counter. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#animCounter - * @type {number} - * @since 3.60.0 - */ - animCounter: { - - get: function () - { - return this.counters[2]; - }, - - set: function (value) - { - this.counters[2] = value; - } - - }, - - /** - * The internal zone counter. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#zoneIndex - * @type {number} - * @since 3.60.0 - */ - zoneIndex: { - - get: function () - { - return this.counters[6]; - }, - - set: function (value) - { - this.counters[6] = value; - } - - }, - - /** - * The current emission zone index. - * - * @name Phaser.GameObjects.Particles.ParticleEmitter#zoneTotal - * @type {number} - * @since 3.60.0 - */ - zoneTotal: { - - get: function () - { - return this.counters[7]; - }, - - set: function (value) - { - this.counters[7] = value; - } - - }, - - /** - * Destroys this Particle Emitter and all Particles it owns. - * - * @method Phaser.GameObjects.Particles.ParticleEmitter#preDestroy - * @since 3.60.0 - */ - preDestroy: function () - { - this.texture = null; - this.frames = null; - this.anims = null; - this.emitCallback = null; - this.emitCallbackScope = null; - this.deathCallback = null; - this.deathCallbackScope = null; - this.emitZones = null; - this.deathZones = null; - this.bounds = null; - this.follow = null; - this.counters = null; - - var i; - - var ops = this.ops; - - for (i = 0; i < configOpMap.length; i++) - { - var key = configOpMap[i]; - - ops[key].destroy(); - } - - for (i = 0; i < this.alive.length; i++) - { - this.alive[i].destroy(); - } - - for (i = 0; i < this.dead.length; i++) - { - this.dead[i].destroy(); - } - - this.ops = null; - this.alive = []; - this.dead = []; - this.tempMatrix1.destroy(); - this.tempMatrix2.destroy(); - } - -}); - -module.exports = ParticleEmitter2; diff --git a/src/gameobjects/particles/ParticleManagerCanvasRenderer.js b/src/gameobjects/particles/ParticleEmitterCanvasRenderer.js similarity index 58% rename from src/gameobjects/particles/ParticleManagerCanvasRenderer.js rename to src/gameobjects/particles/ParticleEmitterCanvasRenderer.js index 5ae5e4d94..eb5430f5f 100644 --- a/src/gameobjects/particles/ParticleManagerCanvasRenderer.js +++ b/src/gameobjects/particles/ParticleEmitterCanvasRenderer.js @@ -17,17 +17,118 @@ var tempMatrix4 = new TransformMatrix(); * The object will not render if any of its renderFlags are set or it is being actively filtered out by the Camera. * This method should not be called directly. It is a utility function of the Render module. * - * @method Phaser.GameObjects.Particles.EmitterManager#renderCanvas - * @since 3.0.0 + * @method Phaser.GameObjects.Particles.Emitter#renderCanvas + * @since 3.60.0 * @private * * @param {Phaser.Renderer.Canvas.CanvasRenderer} renderer - A reference to the current active Canvas renderer. - * @param {Phaser.GameObjects.Particles.ParticleEmitterManager} emitterManager - The Game Object being rendered in this call. + * @param {Phaser.GameObjects.Particles.ParticleEmitter} emitter - The Game Object being rendered in this call. * @param {Phaser.Cameras.Scene2D.Camera} camera - The Camera that is rendering the Game Object. * @param {Phaser.GameObjects.Components.TransformMatrix} parentMatrix - This transform matrix is defined if the game object is nested */ -var ParticleManagerCanvasRenderer = function (renderer, emitterManager, camera, parentMatrix) +var ParticleEmitterCanvasRenderer = function (renderer, emitter, camera, parentMatrix) { + var camMatrix = tempMatrix1; + var calcMatrix = tempMatrix2; + var particleMatrix = tempMatrix3; + var managerMatrix = tempMatrix4; + + if (parentMatrix) + { + managerMatrix.loadIdentity(); + managerMatrix.multiply(parentMatrix); + managerMatrix.translate(emitter.x, emitter.y); + managerMatrix.rotate(emitter.rotation); + managerMatrix.scale(emitter.scaleX, emitter.scaleY); + } + else + { + managerMatrix.applyITRS(emitter.x, emitter.y, emitter.rotation, emitter.scaleX, emitter.scaleY); + } + + var ctx = renderer.currentContext; + var roundPixels = camera.roundPixels; + var camerAlpha = camera.alpha; + var emitterAlpha = emitter.alpha; + + var particles = emitter.alive; + var particleCount = particles.length; + var viewBounds = emitter.viewBounds; + + if (!emitter.visible || particleCount === 0 || (viewBounds && !RectangleToRectangle(viewBounds, camera.worldView))) + { + return; + } + + if (emitter.sortCallback) + { + emitter.depthSort(); + } + + camera.addToRenderList(emitter); + + var scrollFactorX = emitter.scrollFactorX; + var scrollFactorY = emitter.scrollFactorY; + + ctx.save(); + + ctx.globalCompositeOperation = renderer.blendModes[emitter.blendMode]; + + for (var i = 0; i < particleCount; i++) + { + var particle = particles[i]; + + var alpha = particle.alpha * emitterAlpha * camerAlpha; + + if (alpha <= 0 || particle.scaleX === 0 || particle.scaleY === 0) + { + continue; + } + + particleMatrix.applyITRS(particle.x, particle.y, particle.rotation, particle.scaleX, particle.scaleY); + + camMatrix.copyFrom(camera.matrix); + + camMatrix.multiplyWithOffset(managerMatrix, -camera.scrollX * scrollFactorX, -camera.scrollY * scrollFactorY); + + // Undo the camera scroll + particleMatrix.e = particle.x; + particleMatrix.f = particle.y; + + // Multiply by the particle matrix, store result in calcMatrix + camMatrix.multiply(particleMatrix, calcMatrix); + + var frame = particle.frame; + var cd = frame.canvasData; + + if (cd.width > 0 && cd.height > 0) + { + var x = -(frame.halfWidth); + var y = -(frame.halfHeight); + + ctx.globalAlpha = alpha; + + ctx.save(); + + calcMatrix.setToContext(ctx); + + if (roundPixels) + { + x = Math.round(x); + y = Math.round(y); + } + + ctx.imageSmoothingEnabled = !frame.source.scaleMode; + + ctx.drawImage(frame.source.image, cd.x, cd.y, cd.width, cd.height, x, y, cd.width, cd.height); + + ctx.restore(); + } + } + + ctx.restore(); + + /* var emitters = emitterManager.emitters.list; var emittersLength = emitters.length; @@ -137,6 +238,7 @@ var ParticleManagerCanvasRenderer = function (renderer, emitterManager, camera, ctx.restore(); } + */ }; -module.exports = ParticleManagerCanvasRenderer; +module.exports = ParticleEmitterCanvasRenderer; diff --git a/src/gameobjects/particles/ParticleManagerCreator.js b/src/gameobjects/particles/ParticleEmitterCreator.js similarity index 82% rename from src/gameobjects/particles/ParticleManagerCreator.js rename to src/gameobjects/particles/ParticleEmitterCreator.js index 5eee22173..277141010 100644 --- a/src/gameobjects/particles/ParticleManagerCreator.js +++ b/src/gameobjects/particles/ParticleEmitterCreator.js @@ -7,10 +7,10 @@ var GameObjectCreator = require('../GameObjectCreator'); var GetAdvancedValue = require('../../utils/object/GetAdvancedValue'); var GetFastValue = require('../../utils/object/GetFastValue'); -var ParticleEmitterManager = require('./ParticleEmitterManager'); +var ParticleEmitter = require('./ParticleEmitter'); /** - * Creates a new Particle Emitter Manager Game Object and returns it. + * Creates a new Particle Emitter Game Object and returns it. * * Note: This method will only be available if the Particles Game Object has been built into Phaser. * @@ -20,7 +20,7 @@ var ParticleEmitterManager = require('./ParticleEmitterManager'); * @param {Phaser.Types.GameObjects.Particles.ParticleEmitterManagerConfig} config - The configuration object this Game Object will use to create itself. * @param {boolean} [addToScene] - Add this Game Object to the Scene after creating it? If set this argument overrides the `add` property in the config object. * - * @return {Phaser.GameObjects.Particles.ParticleEmitterManager} The Game Object that was created. + * @return {Phaser.GameObjects.Particles.ParticleEmitter} The Game Object that was created. */ GameObjectCreator.register('particles', function (config, addToScene) { @@ -31,7 +31,7 @@ GameObjectCreator.register('particles', function (config, addToScene) var emitters = GetFastValue(config, 'emitters', null); // frame is optional and can contain the emitters array or object if skipped - var manager = new ParticleEmitterManager(this.scene, key, frame, emitters); + var manager = new ParticleEmitter(this.scene, key, frame, emitters); if (addToScene !== undefined) { diff --git a/src/gameobjects/particles/ParticleEmitterFactory.js b/src/gameobjects/particles/ParticleEmitterFactory.js new file mode 100644 index 000000000..75f26b8a7 --- /dev/null +++ b/src/gameobjects/particles/ParticleEmitterFactory.js @@ -0,0 +1,26 @@ +/** + * @author Richard Davey + * @copyright 2013-2023 Photon Storm Ltd. + * @license {@link https://opensource.org/licenses/MIT|MIT License} + */ + +var GameObjectFactory = require('../GameObjectFactory'); +var ParticleEmitter = require('./ParticleEmitter'); + +/** + * Creates a new Particle Emitter Game Object and adds it to the Scene. + * + * Note: This method will only be available if the Particles Game Object has been built into Phaser. + * + * @method Phaser.GameObjects.GameObjectFactory#particles + * @since 3.60.0 + * + * @param {(string|Phaser.Textures.Texture)} texture - The key, or instance of the Texture this Game Object will use to render with, as stored in the Texture Manager. + * @param {Phaser.Types.GameObjects.Particles.ParticleEmitterConfig} [config] - Configuration settings for the Particle Emitter. + * + * @return {Phaser.GameObjects.Particles.ParticleEmitter} The Game Object that was created. + */ +GameObjectFactory.register('particles', function (key, config) +{ + return this.displayList.add(new ParticleEmitter(this.scene, key, config)); +}); diff --git a/src/gameobjects/particles/ParticleEmitterManager.js b/src/gameobjects/particles/ParticleEmitterManager.js deleted file mode 100644 index 906c3046f..000000000 --- a/src/gameobjects/particles/ParticleEmitterManager.js +++ /dev/null @@ -1,637 +0,0 @@ -/** - * @author Richard Davey - * @copyright 2013-2023 Photon Storm Ltd. - * @license {@link https://opensource.org/licenses/MIT|MIT License} - */ - -var Class = require('../../utils/Class'); -var Components = require('../components'); -var GameObject = require('../GameObject'); -var GravityWell = require('./GravityWell'); -var List = require('../../structs/List'); -var ParticleEmitter = require('./ParticleEmitter'); -var Render = require('./ParticleManagerRender'); - -/** - * @classdesc - * A Particle Emitter Manager creates and controls {@link Phaser.GameObjects.Particles.ParticleEmitter Particle Emitters} and {@link Phaser.GameObjects.Particles.GravityWell Gravity Wells}. - * - * @class ParticleEmitterManager - * @extends Phaser.GameObjects.GameObject - * @memberof Phaser.GameObjects.Particles - * @constructor - * @since 3.0.0 - * - * @extends Phaser.GameObjects.Components.AlphaSingle - * @extends Phaser.GameObjects.Components.Depth - * @extends Phaser.GameObjects.Components.FX - * @extends Phaser.GameObjects.Components.Mask - * @extends Phaser.GameObjects.Components.Pipeline - * @extends Phaser.GameObjects.Components.Transform - * @extends Phaser.GameObjects.Components.Visible - * - * @param {Phaser.Scene} scene - The Scene to which this Emitter Manager belongs. - * @param {string} [texture] - The key of the Texture this Emitter Manager will use to render particles, as stored in the Texture Manager. - * @param {(string|number)} [frame] - An optional frame from the Texture this Emitter Manager will use to render particles. - * @param {Phaser.Types.GameObjects.Particles.ParticleEmitterConfig|Phaser.Types.GameObjects.Particles.ParticleEmitterConfig[]} [emitters] - Configuration settings for one or more emitters to create. - */ -var ParticleEmitterManager = new Class({ - - Extends: GameObject, - - Mixins: [ - Components.AlphaSingle, - Components.Depth, - Components.FX, - Components.Mask, - Components.Pipeline, - Components.Transform, - Components.Visible, - Render - ], - - initialize: - - // frame is optional and can contain the emitters array or object if skipped - function ParticleEmitterManager (scene, texture, frame, emitters) - { - GameObject.call(this, scene, 'ParticleEmitterManager'); - - /** - * The blend mode applied to all emitters and particles. - * - * @name Phaser.GameObjects.Particles.ParticleEmitterManager#blendMode - * @type {number} - * @default -1 - * @private - * @since 3.0.0 - */ - this.blendMode = -1; - - /** - * The time scale applied to all emitters and particles, affecting flow rate, lifespan, and movement. - * Values larger than 1 are faster than normal. - * This is multiplied with any timeScale set on each individual emitter. - * - * @name Phaser.GameObjects.Particles.ParticleEmitterManager#timeScale - * @type {number} - * @default 1 - * @since 3.0.0 - */ - this.timeScale = 1; - - /** - * The texture used by all Particle Emitters, and by extension all Particles, that this Emitter Manager creates. - * - * @name Phaser.GameObjects.Particles.ParticleEmitterManager#texture - * @type {Phaser.Textures.Texture} - * @default null - * @since 3.0.0 - */ - this.texture = null; - - /** - * The texture frame used by all Particle Emitters, and by extension all Particles, that this Emitter Manager creates. - * - * @name Phaser.GameObjects.Particles.ParticleEmitterManager#frame - * @type {Phaser.Textures.Frame} - * @default null - * @since 3.0.0 - */ - this.frame = null; - - /** - * The names of all of Texture Frames this Particle Emitter Manager uses. - * - * @name Phaser.GameObjects.Particles.ParticleEmitterManager#frameNames - * @type {string[]} - * @since 3.0.0 - */ - this.frameNames = []; - - // frame is optional and can contain the emitters array or object if skipped - if (frame !== null && (typeof frame === 'object' || Array.isArray(frame))) - { - emitters = frame; - frame = null; - } - - /** - * The names of all of animations this Particle Emitter Manager uses. - * - * @name Phaser.GameObjects.Particles.ParticleEmitterManager#animNames - * @type {string[]} - * @since 3.60.0 - */ - this.animNames = []; - - this.setTexture(texture, frame); - - /** - * The default texture frame used by all Particle Emitters. - * - * @name Phaser.GameObjects.Particles.ParticleEmitterManager#defaultFrame - * @type {Phaser.Textures.Frame} - * @since 3.0.0 - */ - this.defaultFrame = this.frame; - - /** - * A list of Emitters being managed by this Emitter Manager. - * - * @name Phaser.GameObjects.Particles.ParticleEmitterManager#emitters - * @type {Phaser.Structs.List.} - * @since 3.0.0 - */ - this.emitters = new List(this); - - /** - * A list of Particle Processors being managed by this Emitter Manager. - * - * @name Phaser.GameObjects.Particles.ParticleEmitterManager#processors - * @type {Phaser.Structs.List.} - * @since 3.0.0 - */ - this.processors = new List(this); - - if (emitters) - { - // An array of emitter configs? - if (!Array.isArray(emitters)) - { - emitters = [ emitters ]; - } - - for (var i = 0; i < emitters.length; i++) - { - this.createEmitter(emitters[i]); - } - } - - this.initPipeline(); - }, - - // Overrides Game Object method - addedToScene: function () - { - this.scene.sys.updateList.add(this); - }, - - // Overrides Game Object method - removedFromScene: function () - { - this.scene.sys.updateList.remove(this); - }, - - /** - * Sets the texture and frame this Emitter Manager will use to render with. - * - * Textures are referenced by their string-based keys, as stored in the Texture Manager. - * - * @method Phaser.GameObjects.Particles.ParticleEmitterManager#setTexture - * @since 3.0.0 - * - * @param {string} key - The key of the texture to be used, as stored in the Texture Manager. - * @param {(string|number)} [frame] - The name or index of the frame within the Texture. - * - * @return {this} This Emitter Manager. - */ - setTexture: function (key, frame) - { - this.texture = this.scene.sys.textures.get(key); - - return this.setFrame(frame); - }, - - /** - * Sets the frame this Emitter Manager will use to render with. - * - * The Frame has to belong to the current Texture being used. - * - * It can be either a string or an index. - * - * @method Phaser.GameObjects.Particles.ParticleEmitterManager#setFrame - * @since 3.0.0 - * - * @param {(string|number)} [frame] - The name or index of the frame within the Texture. - * - * @return {this} This Emitter Manager. - */ - setFrame: function (frame) - { - this.frame = this.texture.get(frame); - - var frames = this.texture.getFramesFromTextureSource(this.frame.sourceIndex); - - var names = []; - - frames.forEach(function (sourceFrame) - { - names.push(sourceFrame.name); - }); - - this.frameNames = names; - - this.defaultFrame = this.frame; - - this.animNames = this.scene.sys.anims.getAnimsFromTexture(this.texture); - - if (this.animNames.length > 0) - { - this.defaultAnim = this.animNames[0]; - } - - return this; - }, - - /** - * Assigns texture frames to an emitter. - * - * @method Phaser.GameObjects.Particles.ParticleEmitterManager#setEmitterFrames - * @since 3.0.0 - * - * @param {(Phaser.Textures.Frame|Phaser.Textures.Frame[])} frames - The texture frames. - * @param {Phaser.GameObjects.Particles.ParticleEmitter} emitter - The particle emitter to modify. - * - * @return {this} This Emitter Manager. - */ - setEmitterFrames: function (frames, emitter) - { - if (!Array.isArray(frames)) - { - frames = [ frames ]; - } - - var out = emitter.frames; - - out.length = 0; - - for (var i = 0; i < frames.length; i++) - { - var frame = frames[i]; - - if (this.frameNames.indexOf(frame) !== -1) - { - out.push(this.texture.get(frame)); - } - else - { - console.warn('Texture "%s" has no frame "%s"', this.texture.key, frame); - } - } - - if (out.length > 0) - { - emitter.defaultFrame = out[0]; - } - else - { - console.warn('No texture frames were set'); - - emitter.defaultFrame = this.defaultFrame; - } - - return this; - }, - - /** - * Assigns texture frames to an emitter. - * - * @method Phaser.GameObjects.Particles.ParticleEmitterManager#setEmitterAnims - * @since 3.60.0 - * - * @param {(Phaser.Textures.Frame|Phaser.Textures.Frame[])} anims - The animations. - * @param {Phaser.GameObjects.Particles.ParticleEmitter} emitter - The particle emitter to modify. - * - * @return {this} This Emitter Manager. - */ - setEmitterAnims: function (anims, emitter) - { - if (!Array.isArray(anims)) - { - anims = [ anims ]; - } - - var out = emitter.anims; - - out.length = 0; - - for (var i = 0; i < anims.length; i++) - { - var anim = anims[i]; - - if (this.animNames.indexOf(anim) !== -1) - { - out.push(anim); - } - else - { - console.warn('Emitter has no animation "%s"', anim); - } - } - - if (out.length > 0) - { - emitter.defaultAnim = out[0]; - } - - return this; - }, - - /** - * Adds an existing Particle Emitter to this Emitter Manager. - * - * @method Phaser.GameObjects.Particles.ParticleEmitterManager#addEmitter - * @since 3.0.0 - * - * @param {Phaser.GameObjects.Particles.ParticleEmitter} emitter - The Particle Emitter to add to this Emitter Manager. - * - * @return {Phaser.GameObjects.Particles.ParticleEmitter} The Particle Emitter that was added to this Emitter Manager. - */ - addEmitter: function (emitter) - { - return this.emitters.add(emitter); - }, - - /** - * Creates a new Particle Emitter object, adds it to this Emitter Manager and returns a reference to it. - * - * @method Phaser.GameObjects.Particles.ParticleEmitterManager#createEmitter - * @since 3.0.0 - * - * @param {Phaser.Types.GameObjects.Particles.ParticleEmitterConfig} config - Configuration settings for the Particle Emitter to create. - * - * @return {Phaser.GameObjects.Particles.ParticleEmitter} The Particle Emitter that was created. - */ - createEmitter: function (config) - { - return this.addEmitter(new ParticleEmitter(this, config)); - }, - - /** - * Removes a Particle Emitter from this Emitter Manager. - * - * The Emitter must belong to this Manager. - * - * @method Phaser.GameObjects.Particles.ParticleEmitterManager#removeEmitter - * @since 3.22.0 - * - * @param {Phaser.GameObjects.Particles.ParticleEmitter} emitter - The Particle Emitter to remove from this Emitter Manager. - * - * @return {?Phaser.GameObjects.Particles.ParticleEmitter} The Particle Emitter that was removed, or null if it could not be found. - */ - removeEmitter: function (emitter) - { - return this.emitters.remove(emitter, true); - }, - - /** - * Adds a Particle Processor, such as a Gravity Well, to this Emitter Manager. - * - * It will start processing particles from the next update as long as its `active` - * property is set. - * - * @method Phaser.GameObjects.Particles.ParticleEmitterManager#addParticleProcessor - * @since 3.60.0 - * - * @param {Phaser.GameObjects.Particles.ParticleProcessor} processor - The Particle Processor to add to this Emitter Manager. - * - * @return {Phaser.GameObjects.Particles.ParticleProcessor} The Particle Processor that was added to this Emitter Manager. - */ - addParticleProcessor: function (processor) - { - if (!this.processors.exists(processor)) - { - if (processor.manager) - { - processor.manager.removeParticleProcessor(processor); - } - - this.processors.add(processor); - - processor.manager = this; - } - - return processor; - }, - - /** - * Removes a Particle Processor from this Emitter Manager. - * - * The Processor must belong to this Manager. - * - * It is not destroyed when removed, allowing you to move it to another Emitter Manager, - * so if you no longer require it you should call its `destroy` method directly. - * - * @method Phaser.GameObjects.Particles.ParticleEmitterManager#removeParticleProcessor - * @since 3.60.0 - * - * @param {Phaser.GameObjects.Particles.ParticleProcessor} processor - The Particle Processor to remove from this Emitter Manager. - * - * @return {?Phaser.GameObjects.Particles.ParticleProcessor} The Particle Processor that was removed, or null if it could not be found. - */ - removeParticleProcessor: function (processor) - { - if (this.processors.exists(processor)) - { - this.processors.remove(processor, true); - - processor.manager = null; - } - - return processor; - }, - - /** - * Creates a new Gravity Well, adds it to this Emitter Manager and returns a reference to it. - * - * @method Phaser.GameObjects.Particles.ParticleEmitterManager#createGravityWell - * @since 3.0.0 - * - * @param {Phaser.Types.GameObjects.Particles.GravityWellConfig} config - Configuration settings for the Gravity Well to create. - * - * @return {Phaser.GameObjects.Particles.GravityWell} The Gravity Well that was created. - */ - createGravityWell: function (config) - { - return this.addParticleProcessor(new GravityWell(config)); - }, - - /** - * Emits particles from each active emitter. - * - * @method Phaser.GameObjects.Particles.ParticleEmitterManager#emitParticle - * @since 3.0.0 - * - * @param {number} [count] - The number of particles to release from each emitter. The default is the emitter's own {@link Phaser.GameObjects.Particles.ParticleEmitter#quantity}. - * @param {number} [x] - The x-coordinate to to emit particles from. The default is the x-coordinate of the emitter's current location. - * @param {number} [y] - The y-coordinate to to emit particles from. The default is the y-coordinate of the emitter's current location. - * - * @return {this} This Emitter Manager. - */ - emitParticle: function (count, x, y) - { - var emitters = this.emitters.list; - - for (var i = 0; i < emitters.length; i++) - { - var emitter = emitters[i]; - - if (emitter.active) - { - emitter.emitParticle(count, x, y); - } - } - - return this; - }, - - /** - * Emits particles from each active emitter. - * - * @method Phaser.GameObjects.Particles.ParticleEmitterManager#emitParticleAt - * @since 3.0.0 - * - * @param {number} [x] - The x-coordinate to to emit particles from. The default is the x-coordinate of the emitter's current location. - * @param {number} [y] - The y-coordinate to to emit particles from. The default is the y-coordinate of the emitter's current location. - * @param {number} [count] - The number of particles to release from each emitter. The default is the emitter's own {@link Phaser.GameObjects.Particles.ParticleEmitter#quantity}. - * - * @return {this} This Emitter Manager. - */ - emitParticleAt: function (x, y, count) - { - return this.emitParticle(count, x, y); - }, - - /** - * Pauses this Emitter Manager. - * - * This has the effect of pausing all emitters, and all particles of those emitters, currently under its control. - * - * The particles will still render, but they will not have any of their logic updated. - * - * @method Phaser.GameObjects.Particles.ParticleEmitterManager#pause - * @since 3.0.0 - * - * @return {this} This Emitter Manager. - */ - pause: function () - { - this.active = false; - - return this; - }, - - /** - * Resumes this Emitter Manager, should it have been previously paused. - * - * @method Phaser.GameObjects.Particles.ParticleEmitterManager#resume - * @since 3.0.0 - * - * @return {this} This Emitter Manager. - */ - resume: function () - { - this.active = true; - - return this; - }, - - /** - * Gets all active Particle Processors. - * - * @method Phaser.GameObjects.Particles.ParticleEmitterManager#getProcessors - * @since 3.0.0 - * - * @return {Phaser.GameObjects.Particles.ParticleProcessor[]} - An array of active Particle Processors. - */ - getProcessors: function () - { - return this.processors.getAll('active', true); - }, - - /** - * Updates all active emitters. - * - * @method Phaser.GameObjects.Particles.ParticleEmitterManager#preUpdate - * @since 3.0.0 - * - * @param {number} time - The current timestamp as generated by the Request Animation Frame or SetTimeout. - * @param {number} delta - The delta time, in ms, elapsed since the last frame. - */ - preUpdate: function (time, delta) - { - if (this.active) - { - // Scale the delta - delta *= this.timeScale; - - var emitters = this.emitters.list; - - for (var i = 0; i < emitters.length; i++) - { - var emitter = emitters[i]; - - if (emitter.active) - { - emitter.preUpdate(time, delta); - } - } - } - }, - - /** - * A NOOP method so you can pass an EmitterManager to a Container. - * Calling this method will do nothing. It is intentionally empty. - * - * @method Phaser.GameObjects.Particles.ParticleEmitterManager#setScrollFactor - * @private - * @since 3.10.0 - */ - setScrollFactor: function () - { - }, - - /** - * A NOOP method so you can pass an EmitterManager to a Container. - * Calling this method will do nothing. It is intentionally empty. - * - * @method Phaser.GameObjects.Particles.ParticleEmitterManager#setBlendMode - * @private - * @since 3.15.0 - */ - setBlendMode: function () - { - }, - - /** - * Handles the pre-destroy step for the Particle Emitter Manager, which destroys all emitters and processors. - * - * @method Phaser.GameObjects.Particles.ParticleEmitterManager#preDestroy - * @private - * @since 3.60.0 - */ - preDestroy: function () - { - var i; - var emitters = this.emitters.list; - var processors = this.processors.list; - - for (i = 0; i < emitters.length; i++) - { - emitters[i].destroy(); - } - - for (i = 0; i < processors.length; i++) - { - processors[i].destroy(); - } - - this.frameNames = []; - this.defaultAnim = null; - this.defaultFrame = null; - } - -}); - -module.exports = ParticleEmitterManager; diff --git a/src/gameobjects/particles/ParticleEmitterRender.js b/src/gameobjects/particles/ParticleEmitterRender.js index 753edc607..a7ae404dd 100644 --- a/src/gameobjects/particles/ParticleEmitterRender.js +++ b/src/gameobjects/particles/ParticleEmitterRender.js @@ -15,7 +15,7 @@ if (typeof WEBGL_RENDERER) if (typeof CANVAS_RENDERER) { - // renderCanvas = require('./ParticleManagerCanvasRenderer'); + renderCanvas = require('./ParticleEmitterCanvasRenderer'); } module.exports = { diff --git a/src/gameobjects/particles/ParticleEmitterWebGLRenderer.js b/src/gameobjects/particles/ParticleEmitterWebGLRenderer.js index 2f7894665..4c1ba3f2b 100644 --- a/src/gameobjects/particles/ParticleEmitterWebGLRenderer.js +++ b/src/gameobjects/particles/ParticleEmitterWebGLRenderer.js @@ -18,12 +18,12 @@ var tempMatrix4 = new TransformMatrix(); * The object will not render if any of its renderFlags are set or it is being actively filtered out by the Camera. * This method should not be called directly. It is a utility function of the Render module. * - * @method Phaser.GameObjects.Particles.EmitterManager#renderWebGL - * @since 3.0.0 + * @method Phaser.GameObjects.Particles.Emitter#renderWebGL + * @since 3.60.0 * @private * * @param {Phaser.Renderer.WebGL.WebGLRenderer} renderer - A reference to the current active WebGL renderer. - * @param {Phaser.GameObjects.Particles.ParticleEmitterManager} emitterManager - The Game Object being rendered in this call. + * @param {Phaser.GameObjects.Particles.ParticleEmitter} emitter - The Game Object being rendered in this call. * @param {Phaser.Cameras.Scene2D.Camera} camera - The Camera that is rendering the Game Object. * @param {Phaser.GameObjects.Components.TransformMatrix} parentMatrix - This transform matrix is defined if the game object is nested */ diff --git a/src/gameobjects/particles/ParticleManagerFactory.js b/src/gameobjects/particles/ParticleManagerFactory.js deleted file mode 100644 index 388550fb8..000000000 --- a/src/gameobjects/particles/ParticleManagerFactory.js +++ /dev/null @@ -1,33 +0,0 @@ -/** - * @author Richard Davey - * @copyright 2013-2023 Photon Storm Ltd. - * @license {@link https://opensource.org/licenses/MIT|MIT License} - */ - -var GameObjectFactory = require('../GameObjectFactory'); -var ParticleEmitterManager = require('./ParticleEmitterManager'); -var ParticleEmitter2 = require('./ParticleEmitter2'); - -/** - * Creates a new Particle Emitter Manager Game Object and adds it to the Scene. - * - * Note: This method will only be available if the Particles Game Object has been built into Phaser. - * - * @method Phaser.GameObjects.GameObjectFactory#particles - * @since 3.0.0 - * - * @param {(string|Phaser.Textures.Texture)} texture - The key, or instance of the Texture this Game Object will use to render with, as stored in the Texture Manager. - * @param {(string|number|object)} [frame] - An optional frame from the Texture this Game Object is rendering with. - * @param {Phaser.Types.GameObjects.Particles.ParticleEmitterConfig|Phaser.Types.GameObjects.Particles.ParticleEmitterConfig[]} [emitters] - Configuration settings for one or more emitters to create. - * - * @return {Phaser.GameObjects.Particles.ParticleEmitterManager} The Game Object that was created. - */ -GameObjectFactory.register('particles', function (key, frame, emitters) -{ - return this.displayList.add(new ParticleEmitterManager(this.scene, key, frame, emitters)); -}); - -GameObjectFactory.register('particles2', function (key, config) -{ - return this.displayList.add(new ParticleEmitter2(this.scene, key, config)); -}); diff --git a/src/gameobjects/particles/ParticleManagerRender.js b/src/gameobjects/particles/ParticleManagerRender.js deleted file mode 100644 index 3d56a8565..000000000 --- a/src/gameobjects/particles/ParticleManagerRender.js +++ /dev/null @@ -1,26 +0,0 @@ -/** - * @author Richard Davey - * @copyright 2013-2023 Photon Storm Ltd. - * @license {@link https://opensource.org/licenses/MIT|MIT License} - */ - -var NOOP = require('../../utils/NOOP'); -var renderWebGL = NOOP; -var renderCanvas = NOOP; - -if (typeof WEBGL_RENDERER) -{ - renderWebGL = require('./ParticleManagerWebGLRenderer'); -} - -if (typeof CANVAS_RENDERER) -{ - renderCanvas = require('./ParticleManagerCanvasRenderer'); -} - -module.exports = { - - renderWebGL: renderWebGL, - renderCanvas: renderCanvas - -}; diff --git a/src/gameobjects/particles/ParticleManagerWebGLRenderer.js b/src/gameobjects/particles/ParticleManagerWebGLRenderer.js deleted file mode 100644 index d79b86eb5..000000000 --- a/src/gameobjects/particles/ParticleManagerWebGLRenderer.js +++ /dev/null @@ -1,149 +0,0 @@ -/** - * @author Richard Davey - * @copyright 2013-2023 Photon Storm Ltd. - * @license {@link https://opensource.org/licenses/MIT|MIT License} - */ - -var RectangleToRectangle = require('../../geom/intersects/RectangleToRectangle'); -var TransformMatrix = require('../components/TransformMatrix'); -var Utils = require('../../renderer/webgl/Utils'); - -var tempMatrix1 = new TransformMatrix(); -var tempMatrix2 = new TransformMatrix(); -var tempMatrix3 = new TransformMatrix(); -var tempMatrix4 = new TransformMatrix(); - -/** - * Renders this Game Object with the WebGL Renderer to the given Camera. - * The object will not render if any of its renderFlags are set or it is being actively filtered out by the Camera. - * This method should not be called directly. It is a utility function of the Render module. - * - * @method Phaser.GameObjects.Particles.EmitterManager#renderWebGL - * @since 3.0.0 - * @private - * - * @param {Phaser.Renderer.WebGL.WebGLRenderer} renderer - A reference to the current active WebGL renderer. - * @param {Phaser.GameObjects.Particles.ParticleEmitterManager} emitterManager - The Game Object being rendered in this call. - * @param {Phaser.Cameras.Scene2D.Camera} camera - The Camera that is rendering the Game Object. - * @param {Phaser.GameObjects.Components.TransformMatrix} parentMatrix - This transform matrix is defined if the game object is nested - */ -var ParticleManagerWebGLRenderer = function (renderer, emitterManager, camera, parentMatrix) -{ - var emitters = emitterManager.emitters.list; - var emittersLength = emitters.length; - - if (emittersLength === 0) - { - return; - } - - var pipeline = renderer.pipelines.set(emitterManager.pipeline); - - var camMatrix = tempMatrix1; - var calcMatrix = tempMatrix2; - var particleMatrix = tempMatrix3; - var managerMatrix = tempMatrix4; - - if (parentMatrix) - { - managerMatrix.loadIdentity(); - managerMatrix.multiply(parentMatrix); - managerMatrix.translate(emitterManager.x, emitterManager.y); - managerMatrix.rotate(emitterManager.rotation); - managerMatrix.scale(emitterManager.scaleX, emitterManager.scaleY); - } - else - { - managerMatrix.applyITRS(emitterManager.x, emitterManager.y, emitterManager.rotation, emitterManager.scaleX, emitterManager.scaleY); - } - - var roundPixels = camera.roundPixels; - var texture = emitterManager.defaultFrame.glTexture; - var getTint = Utils.getTintAppendFloatAlpha; - var camerAlpha = camera.alpha; - var managerAlpha = emitterManager.alpha; - - renderer.pipelines.preBatch(emitterManager); - - for (var e = 0; e < emittersLength; e++) - { - var emitter = emitters[e]; - - var particles = emitter.alive; - var particleCount = particles.length; - var viewBounds = emitter.viewBounds; - - if (!emitter.visible || particleCount === 0 || (viewBounds && !RectangleToRectangle(viewBounds, camera.worldView))) - { - continue; - } - - if (emitter.sortCallback) - { - emitter.depthSort(); - } - - var textureUnit = pipeline.setGameObject(emitter, emitter.defaultFrame); - - camera.addToRenderList(emitter); - - var scrollFactorX = emitter.scrollFactorX; - var scrollFactorY = emitter.scrollFactorY; - - renderer.setBlendMode(emitter.blendMode); - - if (emitter.mask) - { - emitter.mask.preRenderWebGL(renderer, emitter, camera); - - renderer.pipelines.set(emitterManager.pipeline); - } - - var tintEffect = 0; - - for (var i = 0; i < particleCount; i++) - { - var particle = particles[i]; - - var alpha = particle.alpha * managerAlpha * camerAlpha; - - if (alpha <= 0 || particle.scaleX === 0 || particle.scaleY === 0) - { - continue; - } - - particleMatrix.applyITRS(particle.x, particle.y, particle.rotation, particle.scaleX, particle.scaleY); - - camMatrix.copyFrom(camera.matrix); - - camMatrix.multiplyWithOffset(managerMatrix, -camera.scrollX * scrollFactorX, -camera.scrollY * scrollFactorY); - - // Undo the camera scroll - particleMatrix.e = particle.x; - particleMatrix.f = particle.y; - - // Multiply by the particle matrix, store result in calcMatrix - camMatrix.multiply(particleMatrix, calcMatrix); - - var frame = particle.frame; - - var x = -frame.halfWidth; - var y = -frame.halfHeight; - - var quad = calcMatrix.setQuad(x, y, x + frame.width, y + frame.height, roundPixels); - - var tint = getTint(particle.tint, alpha); - - pipeline.batchQuad(emitter, quad[0], quad[1], quad[2], quad[3], quad[4], quad[5], quad[6], quad[7], frame.u0, frame.v0, frame.u1, frame.v1, tint, tint, tint, tint, tintEffect, texture, textureUnit); - } - - if (emitter.mask) - { - emitter.mask.postRenderWebGL(renderer, camera); - } - } - - renderer.pipelines.postBatch(emitterManager); -}; - -module.exports = ParticleManagerWebGLRenderer; diff --git a/src/gameobjects/particles/index.js b/src/gameobjects/particles/index.js index b7a5ad8ee..5dded288b 100644 --- a/src/gameobjects/particles/index.js +++ b/src/gameobjects/particles/index.js @@ -15,7 +15,6 @@ module.exports = { GravityWell: require('./GravityWell'), Particle: require('./Particle'), ParticleEmitter: require('./ParticleEmitter'), - ParticleEmitterManager: require('./ParticleEmitterManager'), ParticleProcessor: require('./ParticleProcessor'), Zones: require('./zones')