2
-- An emitter periodically emits sprites with varying properties --
3
-- for example, velocity. These are set with the emitter's min and
4
-- max properties. For example, you could set the x velocity of
5
-- particles to range between -100 and 100 with these statements:
7
-- > emitter.min.velocity.x = -100
8
-- > emitter.max.velocity.x = 100
10
-- Properties can descend two levels deep at most.
12
-- You can specify any property in min and max, and it will be set
13
-- on sprites as they are emitted. Mins and maxes can only be used
14
-- with numeric properties.
16
-- Particles when emitted will appear at a random spot inside the
17
-- rectangle defined by the emitter's x, y, width, and height
20
-- Because emitters are <Group> subclasses, all particles appear at
21
-- the same z index onscreen. This also means that setting active,
22
-- visible, and solid properties on the emitter will affect all particles.
24
-- Any sprite may be used as a particle. When a sprite is added as
25
-- a particle, its die() method is called. When emitted, revive() is
26
-- called on it. If you want a particle to remain invisible after being
27
-- emitted, for example, then write an onEmit method on your sprite to do so.
33
-- Called on both the parent emitter and the emitted sprite
34
-- when it is emitted. If multiple particles are emitted at once, the
35
-- emitter will receive multiple onEmit events.
37
Emitter = Group:extend{
39
-- The x coordinate of the upper-left corner of the rectangle where particles may appear.
43
-- The y coordinate of the upper-left corner of the rectangle where particles may appear.
47
-- The width of the rectangle where particles may appear.
51
-- The height of the retangle where particles may appear.
55
-- Boolean whether this emitter is actually emitting particles.
59
-- How long, in seconds, the emitter should wait before emitting.
62
-- Property: emitCount
63
-- How many particles to emit at once.
67
-- Minimum numeric properties for particles.
71
-- Maximum numeric properties for particles.
74
-- Property: emitTimer
75
-- Used to keep track of when the next emit should take place.
76
-- To restart the timer, set it to 0. To immediately force a particle
77
-- to be emitted, set it to the emitter's period property. (Although
78
-- you should probably call emit() instead.)
81
-- which particle to emit next
84
-- Method: loadParticles
85
-- Creates a number of particles to use based on a class.
86
-- This calls new() on the particle class with no arguments.
89
-- class - class object to instantiate
90
-- count - number of particles to create
95
loadParticles = function (self, class, count)
102
-- Emits one or more particles. This ignores the emitting property.
103
-- If no particles are ready to be emitted, this does nothing.
106
-- count - how many particles to emit, default 1
111
emit = function (self, count)
114
if #self.sprites == 0 then return end
117
local emitted = self.sprites[self._emitIndex]
118
self._emitIndex = self._emitIndex + 1
120
if self._emitIndex > #self.sprites then self._emitIndex = 1 end
122
-- revive it and set properties
125
emitted.x = math.random(self.x, self.x + self.width)
126
emitted.y = math.random(self.y, self.y + self.height)
128
for key, _ in pairs(self.min) do
129
if self.max[key] then
130
-- simple case, single value
132
if type(self.min[key]) == 'number' then
133
emitted[key] = self.min[key] + math.random() * (self.max[key] - self.min[key])
136
-- complicated case, table
138
if type(self.min[key]) == 'table' then
139
for subkey, _ in pairs(self.min[key]) do
140
if type(self.min[key][subkey]) == 'number' then
141
emitted[key][subkey] = self.min[key][subkey] + math.random() *
142
(self.max[key][subkey] - self.min[key][subkey])
149
if emitted.onEmit then emitted:onEmit(self) end
150
if self.onEmit then self:onEmit(emitted) end
155
-- This emits many particles simultaneously then immediately stops any further
156
-- emissions. If you want to keep the emitter going, call emitter.emit(#emitter.sprites).
159
-- count - number of particles to emit, defaults to all of them
164
explode = function (self, count)
165
count = count or #self.sprites
168
self.emitting = false
171
-- Method: extinguish
172
-- This immediately calls die() on all particles, then the emitter itself.
173
-- This differs from a regular die() call in that if you call revive() on the
174
-- emitter later, particles will not appear where they last left off.
182
extinguish = function (self)
183
for _, spr in pairs(self.sprites) do
190
update = function (self, elapsed)
191
if not self.active then return end
193
if self.emitting then
194
self.emitTimer = self.emitTimer + elapsed
196
if self.emitTimer > self.period then
197
self:emit(self.emitCount)
198
self.emitTimer = self.emitTimer - self.period
202
Group.update(self, elapsed)
205
add = function (self, sprite)
207
Group.add(self, sprite)
210
__tostring = function (self)
211
local result = 'Emitter (x: ' .. self.x .. ', y: ' .. self.y ..
212
', w: ' .. self.width .. ', h: ' .. self.height .. ', '
214
if self.emitting then
215
result = result .. 'emitting with period ' .. self.period .. ', '
217
result = result .. 'not emitting, '