2
-- An animation displays a sequence of frames. If you do not specify
3
-- a width and height for the sprite, it will size itself so that
4
-- it is a square, where each side is as tall as the source image's height.
7
-- Event: onEndSequence
8
-- Called whenever an animation sequence ends. It is passed the name
9
-- of the sequence that just ended.
14
Animation = Sprite:extend{
16
-- Set this to true to freeze the animation on the current frame.
19
-- Property: sequences
20
-- A lookup table of sequences. Each one is stored by name and has
21
-- the following properties:
22
-- * name - string name for the sequence.
23
-- * frames - table of frames to display. The first frame in the sheet is at index 1.
24
-- * fps - frames per second.
25
-- * loops - does the animation loop? defaults to true
29
-- A string filename to the image to use as a sprite strip. A sprite
30
-- strip can have multiple rows of frames.
32
-- Property: currentSequence
33
-- A reference to the current animation sequence table.
35
-- Property: currentName
36
-- The name of the current animation sequence.
38
-- Property: currentFrame
39
-- The current frame being displayed; starts at 1.
41
-- Property: frameIndex
42
-- Numeric index of the current frame in the current sequence; starts at 1.
44
-- Property: frameTimer
45
-- Time left before the animation changes to the next frame in seconds.
46
-- Normally you shouldn't need to change this directly.
48
-- private property: used to check whether the source image
49
-- for our quad is up-to-date
52
-- private property imageObj: actual Image instance used to draw
53
-- this is normally set via the image property, but you may set it directly
54
-- so long as you never change that image property afterwards.
56
new = function (self, obj)
61
if obj.onNew then obj:onNew() end
66
-- Begins playing an animation in the sprite's library.
67
-- If the animation is already playing, this has no effect.
70
-- name - name of the animation
75
play = function (self, name)
76
if self.currentName == name and not self.paused then
80
assert(self.sequences[name], 'no animation sequence named "' .. name .. '"')
82
self.currentName = name
83
self.currentSequence = self.sequences[name]
90
-- Freezes the animation on the specified frame.
93
-- * index - integer frame index relative to the entire sprite sheet,
94
-- starts at 1. If omitted, this freezes the current frame.
95
-- If there is no current frame, this freezes on the first frame.
100
freeze = function (self, index)
101
if self.currentSequence then
102
index = index or self.currentSequence[self.frameIndex]
105
index = index or self.currentFrame or 1
107
if self._set.image ~= self.image then
111
self.currentFrame = index
112
self:updateFrame(index)
116
-- private method: updateQuad
117
-- sets up the sprite's quad property based on the image;
118
-- needs to be called whenever the sprite's image property changes.
126
updateQuad = function (self)
128
self._imageObj = Cached:image(self.image)
129
if not self.width then self.width = self._imageObj:getHeight() end
130
if not self.height then self.height = self.width end
132
self._quad = love.graphics.newQuad(0, 0, self.width, self.height,
133
self._imageObj:getWidth(), self._imageObj:getHeight())
134
self._imageWidth = self._imageObj:getWidth()
135
self._set.image = self.image
139
-- private method: updateFrame
140
-- changes the sprite's quad property based on the current frame;
141
-- needs to be called whenever the sprite's currentFrame property changes.
149
updateFrame = function (self)
150
assert(type(self.currentFrame) == 'number', "current frame is not a number")
151
assert(self.image, "asked to set the frame of a nil image")
153
if self._set.image ~= self.image then
157
local frameX = (self.currentFrame - 1) * self.width
158
local viewportX = frameX % self._imageWidth
159
local viewportY = self.height * math.floor(frameX / self._imageWidth)
160
self._quad:setViewport(viewportX, viewportY, self.width, self.height)
163
update = function (self, elapsed)
164
-- move the animation frame forward
166
if self.currentSequence and not self.paused then
167
self.frameTimer = self.frameTimer - elapsed
169
if self.frameTimer <= 0 then
170
self.frameIndex = self.frameIndex + 1
172
if self.frameIndex > #self.currentSequence.frames then
173
if self.onEndSequence then self:onEndSequence(self.currentName) end
175
if self.currentSequence.loops ~= false then
178
self.frameIndex = self.frameIndex - 1
183
self.currentFrame = self.currentSequence.frames[self.frameIndex]
185
self.frameTimer = 1 / self.currentSequence.fps
189
Sprite.update(self, elapsed)
192
draw = function (self, x, y)
193
x = math.floor(x or self.x)
194
y = math.floor(y or self.y)
197
assert(type(x) == 'number', 'visible animation does not have a numeric x property')
198
assert(type(y) == 'number', 'visible animation does not have a numeric y property')
199
assert(type(self.width) == 'number', 'visible animation does not have a numeric width property')
200
assert(type(self.height) == 'number', 'visible animation does not have a numeric height property')
203
if not self.visible or not self.image or self.alpha <= 0 then return end
205
-- if our image changed, update the quad
207
if self._set.image ~= self.image then
211
-- set color if needed
213
local colored = self.alpha ~= 1 or self.tint[1] ~= 1 or self.tint[2] ~= 1 or self.tint[3] ~= 1
216
love.graphics.setColor(self.tint[1] * 255, self.tint[2] * 255, self.tint[3] * 255, self.alpha * 255)
221
local scaleX = self.scale * self.distort.x
222
local scaleY = self.scale * self.distort.y
224
if self.flipX then scaleX = scaleX * -1 end
225
if self.flipY then scaleY = scaleY * -1 end
227
love.graphics.drawq(self._imageObj, self._quad, x + self.width / 2, y + self.height / 2, self.rotation,
228
scaleX, scaleY, self.width / 2, self.height / 2)
233
love.graphics.setColor(255, 255, 255, 255)
237
__tostring = function (self)
238
local result = 'Animation (x: ' .. self.x .. ', y: ' .. self.y ..
239
', w: ' .. self.width .. ', h: ' .. self.height .. ', '
241
if self.currentName then
242
result = result .. 'playing ' .. self.currentName .. ', '
245
result = result .. ' frame ' .. self.currentFrame .. ', '
248
result = result .. 'active, '
250
result = result .. 'inactive, '
254
result = result .. 'visible, '
256
result = result .. 'invisible, '
260
result = result .. 'solid'
262
result = result .. 'not solid'