bzr branch
http://9ix.org/bzr/ld27
1
by Josh C
zoetrope 1.4 |
1 |
-- Class: Animation |
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. |
|
5 |
-- |
|
6 |
-- |
|
7 |
-- Event: onEndSequence |
|
8 |
-- Called whenever an animation sequence ends. It is passed the name |
|
9 |
-- of the sequence that just ended. |
|
10 |
-- |
|
11 |
-- Extends: |
|
12 |
-- <Sprite> |
|
13 |
||
14 |
Animation = Sprite:extend{ |
|
15 |
-- Property: paused |
|
16 |
-- Set this to true to freeze the animation on the current frame. |
|
17 |
paused = false, |
|
18 |
||
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 |
|
26 |
sequences = {}, |
|
27 |
||
28 |
-- Property: image |
|
29 |
-- A string filename to the image to use as a sprite strip. A sprite |
|
30 |
-- strip can have multiple rows of frames. |
|
31 |
||
32 |
-- Property: currentSequence |
|
33 |
-- A reference to the current animation sequence table. |
|
34 |
||
35 |
-- Property: currentName |
|
36 |
-- The name of the current animation sequence. |
|
37 |
||
38 |
-- Property: currentFrame |
|
39 |
-- The current frame being displayed; starts at 1. |
|
40 |
||
41 |
-- Property: frameIndex |
|
42 |
-- Numeric index of the current frame in the current sequence; starts at 1. |
|
43 |
||
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. |
|
47 |
||
48 |
-- private property: used to check whether the source image |
|
49 |
-- for our quad is up-to-date |
|
50 |
_set = {}, |
|
51 |
||
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. |
|
55 |
||
56 |
new = function (self, obj) |
|
57 |
obj = obj or {} |
|
58 |
self:extend(obj) |
|
59 |
obj:updateQuad() |
|
60 |
||
61 |
if obj.onNew then obj:onNew() end |
|
62 |
return obj |
|
63 |
end, |
|
64 |
||
65 |
-- Method: play |
|
66 |
-- Begins playing an animation in the sprite's library. |
|
67 |
-- If the animation is already playing, this has no effect. |
|
68 |
-- |
|
69 |
-- Arguments: |
|
70 |
-- name - name of the animation |
|
71 |
-- |
|
72 |
-- Returns: |
|
73 |
-- nothing |
|
74 |
||
75 |
play = function (self, name) |
|
76 |
if self.currentName == name and not self.paused then |
|
77 |
return |
|
78 |
end |
|
79 |
||
80 |
assert(self.sequences[name], 'no animation sequence named "' .. name .. '"') |
|
81 |
||
82 |
self.currentName = name |
|
83 |
self.currentSequence = self.sequences[name] |
|
84 |
self.frameIndex = 0 |
|
85 |
self.frameTimer = 0 |
|
86 |
self.paused = false |
|
87 |
end, |
|
88 |
||
89 |
-- Method: freeze |
|
90 |
-- Freezes the animation on the specified frame. |
|
91 |
-- |
|
92 |
-- Arguments: |
|
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. |
|
96 |
-- |
|
97 |
-- Returns: |
|
98 |
-- nothing |
|
99 |
||
100 |
freeze = function (self, index) |
|
101 |
if self.currentSequence then |
|
102 |
index = index or self.currentSequence[self.frameIndex] |
|
103 |
end |
|
104 |
||
105 |
index = index or self.currentFrame or 1 |
|
106 |
||
107 |
if self._set.image ~= self.image then |
|
108 |
self:updateQuad() |
|
109 |
end |
|
110 |
||
111 |
self.currentFrame = index |
|
112 |
self:updateFrame(index) |
|
113 |
self.paused = true |
|
114 |
end, |
|
115 |
||
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. |
|
119 |
-- |
|
120 |
-- Arguments: |
|
121 |
-- none |
|
122 |
-- |
|
123 |
-- Returns: |
|
124 |
-- nothing |
|
125 |
||
126 |
updateQuad = function (self) |
|
127 |
if self.image then |
|
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 |
|
131 |
||
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 |
|
136 |
end |
|
137 |
end, |
|
138 |
||
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. |
|
142 |
-- |
|
143 |
-- Arguments: |
|
144 |
-- none |
|
145 |
-- |
|
146 |
-- Returns: |
|
147 |
-- nothing |
|
148 |
||
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") |
|
152 |
||
153 |
if self._set.image ~= self.image then |
|
154 |
self:updateQuad() |
|
155 |
end |
|
156 |
||
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) |
|
161 |
end, |
|
162 |
||
163 |
update = function (self, elapsed) |
|
164 |
-- move the animation frame forward |
|
165 |
||
166 |
if self.currentSequence and not self.paused then |
|
167 |
self.frameTimer = self.frameTimer - elapsed |
|
168 |
||
169 |
if self.frameTimer <= 0 then |
|
170 |
self.frameIndex = self.frameIndex + 1 |
|
171 |
||
172 |
if self.frameIndex > #self.currentSequence.frames then |
|
173 |
if self.onEndSequence then self:onEndSequence(self.currentName) end |
|
174 |
||
175 |
if self.currentSequence.loops ~= false then |
|
176 |
self.frameIndex = 1 |
|
177 |
else |
|
178 |
self.frameIndex = self.frameIndex - 1 |
|
179 |
self.paused = true |
|
180 |
end |
|
181 |
end |
|
182 |
||
183 |
self.currentFrame = self.currentSequence.frames[self.frameIndex] |
|
184 |
self:updateFrame() |
|
185 |
self.frameTimer = 1 / self.currentSequence.fps |
|
186 |
end |
|
187 |
end |
|
188 |
||
189 |
Sprite.update(self, elapsed) |
|
190 |
end, |
|
191 |
||
192 |
draw = function (self, x, y) |
|
193 |
x = math.floor(x or self.x) |
|
194 |
y = math.floor(y or self.y) |
|
195 |
||
196 |
if STRICT then |
|
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') |
|
201 |
end |
|
202 |
||
203 |
if not self.visible or not self.image or self.alpha <= 0 then return end |
|
204 |
||
205 |
-- if our image changed, update the quad |
|
206 |
||
207 |
if self._set.image ~= self.image then |
|
208 |
self:updateQuad() |
|
209 |
end |
|
210 |
||
211 |
-- set color if needed |
|
212 |
||
213 |
local colored = self.alpha ~= 1 or self.tint[1] ~= 1 or self.tint[2] ~= 1 or self.tint[3] ~= 1 |
|
214 |
||
215 |
if colored then |
|
216 |
love.graphics.setColor(self.tint[1] * 255, self.tint[2] * 255, self.tint[3] * 255, self.alpha * 255) |
|
217 |
end |
|
218 |
||
219 |
-- draw the quad |
|
220 |
||
221 |
local scaleX = self.scale * self.distort.x |
|
222 |
local scaleY = self.scale * self.distort.y |
|
223 |
||
224 |
if self.flipX then scaleX = scaleX * -1 end |
|
225 |
if self.flipY then scaleY = scaleY * -1 end |
|
226 |
||
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) |
|
229 |
||
230 |
-- reset color |
|
231 |
||
232 |
if colored then |
|
233 |
love.graphics.setColor(255, 255, 255, 255) |
|
234 |
end |
|
235 |
end, |
|
236 |
||
237 |
__tostring = function (self) |
|
238 |
local result = 'Animation (x: ' .. self.x .. ', y: ' .. self.y .. |
|
239 |
', w: ' .. self.width .. ', h: ' .. self.height .. ', ' |
|
240 |
||
241 |
if self.currentName then |
|
242 |
result = result .. 'playing ' .. self.currentName .. ', ' |
|
243 |
end |
|
244 |
||
245 |
result = result .. ' frame ' .. self.currentFrame .. ', ' |
|
246 |
||
247 |
if self.active then |
|
248 |
result = result .. 'active, ' |
|
249 |
else |
|
250 |
result = result .. 'inactive, ' |
|
251 |
end |
|
252 |
||
253 |
if self.visible then |
|
254 |
result = result .. 'visible, ' |
|
255 |
else |
|
256 |
result = result .. 'invisible, ' |
|
257 |
end |
|
258 |
||
259 |
if self.solid then |
|
260 |
result = result .. 'solid' |
|
261 |
else |
|
262 |
result = result .. 'not solid' |
|
263 |
end |
|
264 |
||
265 |
return result .. ')' |
|
266 |
end |
|
267 |
} |