/spacey

To get this branch, use:
bzr branch /bzr/spacey

« back to all changes in this revision

Viewing changes to zoetrope/sprites/emitter.lua

  • Committer: Josh C
  • Date: 2013-05-04 20:45:17 UTC
  • Revision ID: josh@9ix.org-20130504204517-1rfp92svud12kg42
zoetrope 1.4

Show diffs side-by-side

added added

removed removed

 
1
-- Class: Emitter
 
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:
 
6
--
 
7
-- > emitter.min.velocity.x = -100
 
8
-- > emitter.max.velocity.x = 100
 
9
--
 
10
-- Properties can descend two levels deep at most.
 
11
--
 
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.
 
15
--
 
16
-- Particles when emitted will appear at a random spot inside the
 
17
-- rectangle defined by the emitter's x, y, width, and height
 
18
-- properties.
 
19
--
 
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.
 
23
--
 
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.
 
28
--
 
29
-- Extends:
 
30
--              <Group>
 
31
--
 
32
-- Event: onEmit
 
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.
 
36
 
 
37
Emitter = Group:extend{
 
38
        -- Property: x
 
39
        -- The x coordinate of the upper-left corner of the rectangle where particles may appear.
 
40
        x = 0,
 
41
 
 
42
        -- Property: y
 
43
        -- The y coordinate of the upper-left corner of the rectangle where particles may appear.
 
44
        y = 0,
 
45
 
 
46
        -- Property: width
 
47
        -- The width of the rectangle where particles may appear.
 
48
        width = 0,
 
49
 
 
50
        -- Property: height
 
51
        -- The height of the retangle where particles may appear.
 
52
        height = 0,
 
53
 
 
54
        -- Property: emitting
 
55
        -- Boolean whether this emitter is actually emitting particles.
 
56
        emitting = true,
 
57
        
 
58
        -- Property: period
 
59
        -- How long, in seconds, the emitter should wait before emitting.
 
60
        period = math.huge,
 
61
 
 
62
        -- Property: emitCount
 
63
        -- How many particles to emit at once.
 
64
        emitCount = 1,
 
65
 
 
66
        -- Property: min
 
67
        -- Minimum numeric properties for particles.
 
68
        min = {},
 
69
 
 
70
        -- Property: max
 
71
        -- Maximum numeric properties for particles.
 
72
        max = {},
 
73
 
 
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.)
 
79
        emitTimer = 0,
 
80
 
 
81
        -- which particle to emit next
 
82
        _emitIndex = 1,
 
83
 
 
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.
 
87
        --
 
88
        -- Arguments:
 
89
        --              class - class object to instantiate
 
90
        --              count - number of particles to create
 
91
        --
 
92
        -- Returns:
 
93
        --              nothing
 
94
 
 
95
        loadParticles = function (self, class, count)
 
96
                for i = 1, count do
 
97
                        self:add(class:new())
 
98
                end
 
99
        end,
 
100
 
 
101
        -- Method: emit
 
102
        -- Emits one or more particles. This ignores the emitting property.
 
103
        -- If no particles are ready to be emitted, this does nothing. 
 
104
        --
 
105
        -- Arguments:
 
106
        --              count - how many particles to emit, default 1
 
107
        --
 
108
        -- Returns:
 
109
        --              emitted particle
 
110
 
 
111
        emit = function (self, count)
 
112
                count = count or 1
 
113
 
 
114
                if #self.sprites == 0 then return end
 
115
 
 
116
                for i = 1, count do
 
117
                        local emitted = self.sprites[self._emitIndex]
 
118
                        self._emitIndex = self._emitIndex + 1
 
119
                        
 
120
                        if self._emitIndex > #self.sprites then self._emitIndex = 1 end
 
121
 
 
122
                        -- revive it and set properties
 
123
 
 
124
                        emitted:revive()
 
125
                        emitted.x = math.random(self.x, self.x + self.width)
 
126
                        emitted.y = math.random(self.y, self.y + self.height)
 
127
 
 
128
                        for key, _ in pairs(self.min) do
 
129
                                if self.max[key] then
 
130
                                        -- simple case, single value
 
131
                                        
 
132
                                        if type(self.min[key]) == 'number' then
 
133
                                                emitted[key] = self.min[key] + math.random() * (self.max[key] - self.min[key])
 
134
                                        end
 
135
 
 
136
                                        -- complicated case, table
 
137
 
 
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])
 
143
                                                        end
 
144
                                                end
 
145
                                        end
 
146
                                end
 
147
                        end
 
148
        
 
149
                        if emitted.onEmit then emitted:onEmit(self) end
 
150
                        if self.onEmit then self:onEmit(emitted) end
 
151
                end
 
152
        end,
 
153
 
 
154
        -- Method: explode
 
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).
 
157
        --
 
158
        -- Arguments:
 
159
        --              count - number of particles to emit, defaults to all of them
 
160
        --
 
161
        -- Returns:
 
162
        --              nothing
 
163
 
 
164
        explode = function (self, count)
 
165
                count = count or #self.sprites
 
166
 
 
167
                self:emit(count)
 
168
                self.emitting = false
 
169
        end,
 
170
 
 
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.
 
175
        --
 
176
        -- Arguments:
 
177
        --              none
 
178
        --
 
179
        -- Returns:
 
180
        --              nothing
 
181
 
 
182
        extinguish = function (self)
 
183
                for _, spr in pairs(self.sprites) do
 
184
                        spr:die()
 
185
                end
 
186
 
 
187
                self:die()
 
188
        end,
 
189
 
 
190
        update = function (self, elapsed)
 
191
                if not self.active then return end
 
192
 
 
193
                if self.emitting then
 
194
                        self.emitTimer = self.emitTimer + elapsed
 
195
 
 
196
                        if self.emitTimer > self.period then
 
197
                                self:emit(self.emitCount)
 
198
                                self.emitTimer = self.emitTimer - self.period
 
199
                        end
 
200
                end
 
201
 
 
202
                Group.update(self, elapsed)
 
203
        end,
 
204
 
 
205
        add = function (self, sprite)
 
206
                sprite:die()
 
207
                Group.add(self, sprite)
 
208
        end,
 
209
 
 
210
        __tostring = function (self)
 
211
                local result = 'Emitter (x: ' .. self.x .. ', y: ' .. self.y ..
 
212
                                           ', w: ' .. self.width .. ', h: ' .. self.height .. ', '
 
213
 
 
214
                if self.emitting then
 
215
                        result = result .. 'emitting with period ' .. self.period .. ', '
 
216
                else
 
217
                        result = result .. 'not emitting, '
 
218
                end
 
219
 
 
220
                return result
 
221
        end
 
222
}