Particles
Particles are fun and not so difficult, but there are a lot of details. Read the docs and examples closely.
const emitter = this.add.particles(0, 0, 'textureKey', config);
You can reconfigure an emitter at any time with setConfig(config)
, or set individual properties by method.
To change ops values individually, use
emitter.ops.alpha.loadConfig({ alpha: { start: 0, end: 1 } });
Emitters are on when created unless you use { emitting: false }
.
Particles last for lifespan
and then disappear. The default lifespan is 1000 ms.
An emitter's emitCallback
and deathCallback
are called at the start and end of each particle's life.
Emitters in flow mode (the default mode) release particles periodically, based on frequency
and quantity
. The flow can be on or off. Phaser's frequency
is actually a time interval (period) so the true frequency is 1000 / frequency
cycles per second. The particle flow per second is
1000 * quantity / frequency
The default flow is { frequency: 0, quantity: 1 }
, which is 1 particle per game loop step. If you want a flow rate independent of the step rate, use a positive value for frequency
. { frequency: 16.666 }
is about 60 cycles per second.
The emitter creates particles on demand and then recycles the expired ones. When the flow is stable, the particle counts are approximately:
alive : quantity * lifespan / frequency
dead : quantity
-----
total : quantity * (1 + lifespan / frequency)
You can stop emitter flow with stop()
and restart with start()
, or toggle the emitter's emitting
property. start()
starts a new flow cycle immediately, resetting all the counters.
explode(count, x, y)
switches out of flow mode and releases particles immediately. It uses the passed arguments (if given) or the emitter's current quantity
, particleX
, and particleY
.
emitParticle(count, x, y)
works like explode()
but it doesn't switch the emitter out of flow mode.
emitParticleAt(x, y, count)
is the same but with a different argument order.
Particles are "fired" from the emitter with a velocity determined by
- the
moveToX
andmoveToY
coordinates if set; or -
speedX
andspeedY
if set; or -
angle
andspeed
With moveTo
the particles move continuously toward the target coordinates and arrive at the end of their life.
Emit Phase | Update Phase |
---|---|
accelerationX | accelerationX |
accelerationY | accelerationY |
alpha | alpha |
angle | — |
bounce | bounce |
delay | — |
lifespan | — |
maxVelocityX | maxVelocityX |
maxVelocityY | maxVelocityY |
moveToX | — |
moveToY | — |
quantity | — |
rotate | rotate |
scaleX | scaleX |
scaleY | scaleY |
speedX | — |
speedY | — |
tint | tint |
x | x |
y | y |
Emitter ops (operations? operators? — we will never know) can hold a single value or a rule for generating a value.
For "emit-only" ops you can give a single value, a random range, a stepped range, or a custom function.
For the other ops you can give a random range or a stepped range (for the emit phase); or a single value, an eased range, or a custom function (for the update phase); or an object with onEmit
and onUpdate
to work in both phases.
These will make more sense if you look at the examples.
Since speedX
and speedY
are emit only, you need to use a particle processor to change a particle's velocity after it's emitted.
Ops with an update phase can be eased, e.g.,
emitter.setAlpha({ start: 0, end: 1 });
The ease duration is the particle lifespan. You can use your own easing function for customization:
const Ease = Phaser.Math.Easing.Quadratic.InOut;
emitter.setAlpha({
start: 0,
end: 1,
ease: (t) => 0.5 * Ease(t > 0.5 ? 2 - 2 * t : 2 * t)
});
A stepped range advances at one step per particle emit, so the step cycle interval (start to end) in milliseconds is
frequency * steps / quantity
For example,
{ frequency: 10, quantity: 1, angle: { start: 0, end: 360, steps: 100 } }
steps the angle from 0 to 360° over 1 second, where each step is 3.6° per 10ms (1000 ms == 10 ms * 100 steps / 1).
When quantity
equals steps
, the whole range is encompassed at once, making a uniform spread. For example,
{ frequency: 1000, quantity: 100, angle: { start: 0, end: 360, steps: 100 } }
makes a burst of 100 particles every second.
Death zones kill particles when they enter or leave the zone, as you specify. Position the zone accordingly.
Random emit zones release each particle at a random position given by a source. The source can be a Geom shape, path, curve, or anything with a getRandomPoint(point)
method. Geom sources give a random position within their area. getRandomPoint(point)
is called each time a particle is released.
Edge emit zones release each particle from a sequence of positions given by a source. The source can be a Geom shape, path, curve, or anything with a getPoints(quantity, stepRate)
method. Geom sources place the points on their edges, start to end. The point sequence is generated once when the zone is created; you can call edgeZone.updateSource()
to create a new sequence.
When set the particles bounce off the interior edges, depending on the emitter bounce
.
Gravity wells can be added to the particle manager.
manager.createGravityWell({ x: 0, y: 0, power: 2, epsilon: 100, gravity: 50 });
You receive a particle in an emitter's deathCallback
and emitCallback
callbacks; an emitter op's onEmit
and onUpdate
callbacks; and a gravity well's update
callbacks.
-
life
is its lifespan (duration) in ms, from thelifespan
value of the emitter. -
lifeCurrent
is its remaining life in ms, decreasing fromlife
to 0. -
life - lifeCurrent
is its elapsed life in ms, increasing from 0 tolife
. -
lifeT
is(life - lifeCurrent) / life
, increasing from 0 to 1.