1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
|
-- Class: Animation
-- An animation displays a sequence of frames. If you do not specify
-- a width and height for the sprite, it will size itself so that
-- it is a square, where each side is as tall as the source image's height.
--
--
-- Event: onEndSequence
-- Called whenever an animation sequence ends. It is passed the name
-- of the sequence that just ended.
--
-- Extends:
-- <Sprite>
Animation = Sprite:extend{
-- Property: paused
-- Set this to true to freeze the animation on the current frame.
paused = false,
-- Property: sequences
-- A lookup table of sequences. Each one is stored by name and has
-- the following properties:
-- * name - string name for the sequence.
-- * frames - table of frames to display. The first frame in the sheet is at index 1.
-- * fps - frames per second.
-- * loops - does the animation loop? defaults to true
sequences = {},
-- Property: image
-- A string filename to the image to use as a sprite strip. A sprite
-- strip can have multiple rows of frames.
-- Property: currentSequence
-- A reference to the current animation sequence table.
-- Property: currentName
-- The name of the current animation sequence.
-- Property: currentFrame
-- The current frame being displayed; starts at 1.
-- Property: frameIndex
-- Numeric index of the current frame in the current sequence; starts at 1.
-- Property: frameTimer
-- Time left before the animation changes to the next frame in seconds.
-- Normally you shouldn't need to change this directly.
-- private property: used to check whether the source image
-- for our quad is up-to-date
_set = {},
-- private property imageObj: actual Image instance used to draw
-- this is normally set via the image property, but you may set it directly
-- so long as you never change that image property afterwards.
new = function (self, obj)
obj = obj or {}
self:extend(obj)
obj:updateQuad()
if obj.onNew then obj:onNew() end
return obj
end,
-- Method: play
-- Begins playing an animation in the sprite's library.
-- If the animation is already playing, this has no effect.
--
-- Arguments:
-- name - name of the animation
--
-- Returns:
-- nothing
play = function (self, name)
if self.currentName == name and not self.paused then
return
end
assert(self.sequences[name], 'no animation sequence named "' .. name .. '"')
self.currentName = name
self.currentSequence = self.sequences[name]
self.frameIndex = 0
self.frameTimer = 0
self.paused = false
end,
-- Method: freeze
-- Freezes the animation on the specified frame.
--
-- Arguments:
-- * index - integer frame index relative to the entire sprite sheet,
-- starts at 1. If omitted, this freezes the current frame.
-- If there is no current frame, this freezes on the first frame.
--
-- Returns:
-- nothing
freeze = function (self, index)
if self.currentSequence then
index = index or self.currentSequence[self.frameIndex]
end
index = index or self.currentFrame or 1
if self._set.image ~= self.image then
self:updateQuad()
end
self.currentFrame = index
self:updateFrame(index)
self.paused = true
end,
-- private method: updateQuad
-- sets up the sprite's quad property based on the image;
-- needs to be called whenever the sprite's image property changes.
--
-- Arguments:
-- none
--
-- Returns:
-- nothing
updateQuad = function (self)
if self.image then
self._imageObj = Cached:image(self.image)
if not self.width then self.width = self._imageObj:getHeight() end
if not self.height then self.height = self.width end
self._quad = love.graphics.newQuad(0, 0, self.width, self.height,
self._imageObj:getWidth(), self._imageObj:getHeight())
self._imageWidth = self._imageObj:getWidth()
self._set.image = self.image
end
end,
-- private method: updateFrame
-- changes the sprite's quad property based on the current frame;
-- needs to be called whenever the sprite's currentFrame property changes.
--
-- Arguments:
-- none
--
-- Returns:
-- nothing
updateFrame = function (self)
assert(type(self.currentFrame) == 'number', "current frame is not a number")
assert(self.image, "asked to set the frame of a nil image")
if self._set.image ~= self.image then
self:updateQuad()
end
local frameX = (self.currentFrame - 1) * self.width
local viewportX = frameX % self._imageWidth
local viewportY = self.height * math.floor(frameX / self._imageWidth)
self._quad:setViewport(viewportX, viewportY, self.width, self.height)
end,
update = function (self, elapsed)
-- move the animation frame forward
if self.currentSequence and not self.paused then
self.frameTimer = self.frameTimer - elapsed
if self.frameTimer <= 0 then
self.frameIndex = self.frameIndex + 1
if self.frameIndex > #self.currentSequence.frames then
if self.onEndSequence then self:onEndSequence(self.currentName) end
if self.currentSequence.loops ~= false then
self.frameIndex = 1
else
self.frameIndex = self.frameIndex - 1
self.paused = true
end
end
self.currentFrame = self.currentSequence.frames[self.frameIndex]
self:updateFrame()
self.frameTimer = 1 / self.currentSequence.fps
end
end
Sprite.update(self, elapsed)
end,
draw = function (self, x, y)
x = math.floor(x or self.x)
y = math.floor(y or self.y)
if STRICT then
assert(type(x) == 'number', 'visible animation does not have a numeric x property')
assert(type(y) == 'number', 'visible animation does not have a numeric y property')
assert(type(self.width) == 'number', 'visible animation does not have a numeric width property')
assert(type(self.height) == 'number', 'visible animation does not have a numeric height property')
end
if not self.visible or not self.image or self.alpha <= 0 then return end
-- if our image changed, update the quad
if self._set.image ~= self.image then
self:updateQuad()
end
-- set color if needed
local colored = self.alpha ~= 1 or self.tint[1] ~= 1 or self.tint[2] ~= 1 or self.tint[3] ~= 1
if colored then
love.graphics.setColor(self.tint[1] * 255, self.tint[2] * 255, self.tint[3] * 255, self.alpha * 255)
end
-- draw the quad
local scaleX = self.scale * self.distort.x
local scaleY = self.scale * self.distort.y
if self.flipX then scaleX = scaleX * -1 end
if self.flipY then scaleY = scaleY * -1 end
love.graphics.drawq(self._imageObj, self._quad, x + self.width / 2, y + self.height / 2, self.rotation,
scaleX, scaleY, self.width / 2, self.height / 2)
-- reset color
if colored then
love.graphics.setColor(255, 255, 255, 255)
end
end,
__tostring = function (self)
local result = 'Animation (x: ' .. self.x .. ', y: ' .. self.y ..
', w: ' .. self.width .. ', h: ' .. self.height .. ', '
if self.currentName then
result = result .. 'playing ' .. self.currentName .. ', '
end
result = result .. ' frame ' .. self.currentFrame .. ', '
if self.active then
result = result .. 'active, '
else
result = result .. 'inactive, '
end
if self.visible then
result = result .. 'visible, '
else
result = result .. 'invisible, '
end
if self.solid then
result = result .. 'solid'
else
result = result .. 'not solid'
end
return result .. ')'
end
}
|