init.lua 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. --[[
  2. The MIT License (MIT)
  3. Copyright (c) 2013 Patrick Rabier
  4. Permission is hereby granted, free of charge, to any person obtaining a copy of
  5. this software and associated documentation files (the "Software"), to deal in
  6. the Software without restriction, including without limitation the rights to
  7. use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
  8. the Software, and to permit persons to whom the Software is furnished to do so,
  9. subject to the following conditions:
  10. The above copyright notice and this permission notice shall be included in all
  11. copies or substantial portions of the Software.
  12. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  13. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
  14. FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
  15. COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
  16. IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  17. CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  18. ]]
  19. --
  20. -- LOVE2D ANIMATION
  21. --
  22. -- @todo document
  23. -- @todo error management
  24. -- @todo the path in the animation file should be relative to the file's location
  25. -- @todo more flexible events ?
  26. -- @todo load an animation with an already loaded image
  27. --
  28. --
  29. -- The animation class
  30. -- Loads an animation file (containing a path to the image)
  31. -- Check the animation file template for the format
  32. --
  33. -- @member filepath the path of the animation file
  34. -- @member descriptor the object loaded from the file (describes the animation frames)
  35. -- @member currentState the state the animation is in. Each state is a line in the image
  36. -- @member currentFrame the frame (of the current state) the animation is displaying
  37. -- @member tick the amount of time the current frame has been displayed
  38. -- @member speedMultiplier a multiplier used to control the animation speed programatically
  39. -- @member active whether the animation is running (or is paused)
  40. -- @member texture the image loaded
  41. -- @member x horizontal coordinate
  42. -- @member y vertical coordinate
  43. -- @member rotation rotation angle in radians
  44. -- @member relativeOriginX the x origin as a multiplier of the sprite's width
  45. -- (ex : 1 for right, 0 for left)
  46. -- @member relativeOriginY the y origin as a multiplier of the sprite's height
  47. -- @member visible boolean, whether the sprite is visible
  48. --
  49. LoveAnimation = {
  50. -- Members
  51. filepath = nil,
  52. descriptor = nil,
  53. currentState = nil,
  54. currentFrame = 0,
  55. tick = 0,
  56. speedMultiplier = 1,
  57. active = true,
  58. texture = nil,
  59. x = 0,
  60. y = 0,
  61. rotation = 0,
  62. relativeOriginX = 0,
  63. relativeOriginY = 0,
  64. visible = true,
  65. flipX = 1,
  66. _stateEndCallbacks = {},
  67. _stateStartCallbacks = {},
  68. }
  69. LoveAnimation.__index = LoveAnimation
  70. -- Static
  71. local __loadedDescriptors = {}
  72. -- Local function
  73. local check_descriptor_integrity = function(desc)
  74. -- @TODO
  75. end
  76. --
  77. -- @brief Creates a new animation
  78. -- @param filepath the file describing the animation
  79. -- @param imagePath [optional] an alternative image
  80. -- @return the animation object
  81. --
  82. function LoveAnimation.new(filepath, imagePath)
  83. local new_anim = {}
  84. setmetatable(new_anim, LoveAnimation)
  85. new_anim:init(filepath, imagePath)
  86. return new_anim
  87. end
  88. function LoveAnimation:init(filepath, imagePath)
  89. local desc = nil
  90. if __loadedDescriptors[filepath] then
  91. desc = __loadedDescriptors[filepath]
  92. else
  93. local chunk = love.filesystem.load(filepath)
  94. desc = chunk()
  95. check_descriptor_integrity(desc);
  96. __loadedDescriptors[filepath] = desc;
  97. end
  98. self.filepath = filepath
  99. self.descriptor = desc
  100. self.texture = imagePath and love.graphics.newImage(imagePath) or love.graphics.newImage(desc.imageSrc)
  101. self:resetAnimation()
  102. end
  103. --
  104. -- @brief Clones the animation (avoids reloading)
  105. -- @return the new animation object
  106. --
  107. function LoveAnimation:clone()
  108. local new_anim = {}
  109. setmetatable(new_anim, LoveAnimation)
  110. new_anim.filepath = self.filepath
  111. new_anim.descriptor = self.descriptor
  112. new_anim.texture = self.texture
  113. new_anim:resetAnimation()
  114. return new_anim
  115. end
  116. --
  117. -- @brief Updates the animation state and frame. Called in the update loop
  118. -- @param dt the elapsed time in seconds (floating point)
  119. --
  120. function LoveAnimation:update(dt)
  121. if self.active == false then
  122. return -- paused
  123. end
  124. self.tick = self.tick + dt * self.speedMultiplier
  125. local state_descriptor = self.descriptor.states[self.currentState]
  126. if self.tick > state_descriptor.switchDelay then
  127. -- switch to the next frame
  128. self.currentFrame = self.currentFrame + 1
  129. if self.currentFrame >= state_descriptor.frameCount then
  130. -- last frame reached, set next state
  131. if not state_descriptor.nextState then
  132. self.currentFrame = state_descriptor.frameCount - 1
  133. self.tick = 0
  134. return
  135. end
  136. self.currentFrame = 0
  137. --callbacks
  138. if state_descriptor.nextState ~= self.currentState then
  139. if self._stateEndCallbacks[self.currentState] then
  140. self._stateEndCallbacks[self.currentState](self, self.currentState)
  141. end
  142. if self._stateStartCallbacks[state_descriptor.nextState] then
  143. self._stateStartCallbacks[state_descriptor.nextState](self, state_descriptor.nextState)
  144. end
  145. end
  146. self.currentState = state_descriptor.nextState
  147. end
  148. -- reset tick
  149. self.tick = 0
  150. end
  151. end
  152. --
  153. -- @brief Draws the sprite on the screen. Called in the drawing loop
  154. --
  155. --
  156. function LoveAnimation:draw(scaleX, scaleY)
  157. if not self.visible then
  158. return
  159. end
  160. local state_descriptor = self.descriptor.states[self.currentState]
  161. local quad = nil
  162. -- we save the quads for each frame to avoid recreating them everytime
  163. if not state_descriptor.quads then
  164. state_descriptor.quads = {}
  165. end
  166. if not state_descriptor.quads[self.currentFrame] then
  167. -- the quad for the current frame has not been created
  168. quad = love.graphics.newQuad(
  169. (state_descriptor.offsetX or 0) + (self.currentFrame * state_descriptor.frameW),
  170. state_descriptor.offsetY or 0,
  171. state_descriptor.frameW,
  172. state_descriptor.frameH,
  173. self.texture:getWidth(),
  174. self.texture:getHeight())
  175. -- we save it
  176. state_descriptor.quads[self.currentFrame] = quad
  177. else
  178. quad = state_descriptor.quads[self.currentFrame]
  179. end
  180. love.graphics.draw(self.texture,
  181. quad,
  182. self.x,
  183. self.y,
  184. self.rotation,
  185. self.flipX * (scaleX or 1), -- negative scale to flip
  186. scaleY or 1, -- scale
  187. self.relativeOriginX * state_descriptor.frameW,
  188. self.relativeOriginY * state_descriptor.frameH,
  189. 0,0)
  190. end
  191. --
  192. -- @brief Sets the state of the animation
  193. -- @param state (string) a state specified in the animation file that we want to switch to
  194. -- e.g anim:setState("jump")
  195. --
  196. function LoveAnimation:setState(state)
  197. local current = self.currentState
  198. local next = state
  199. if self.descriptor.states[next] then
  200. if state ~= current then
  201. if self._stateEndCallbacks[current] then
  202. self._stateEndCallbacks[current](self, current)
  203. end
  204. if self._stateStartCallbacks[next] then
  205. self._stateStartCallbacks[next](self, next)
  206. end
  207. end
  208. self.currentState = state
  209. self.tick = 0
  210. self.currentFrame = 0
  211. end
  212. end
  213. --
  214. -- @brief Gets the state the animation is currently in
  215. -- @return (string) the current state
  216. --
  217. function LoveAnimation:getCurrentState()
  218. return self.currentState;
  219. end
  220. --
  221. -- @brief Gets the list of all states
  222. -- @return (array) the list of all possible states
  223. --
  224. function LoveAnimation:getAllStates()
  225. local states = {}
  226. for state in pairs(self.descriptor.states) do
  227. table.insert(states, state)
  228. end
  229. return states
  230. end
  231. --
  232. -- @brief Sets the frame of the animation state
  233. -- @param f (integer) the frame number
  234. --
  235. function LoveAnimation:setCurrentFrame(f)
  236. local state_descriptor = self.descriptor.states[self.currentState]
  237. if f < state_descriptor.frameCount then
  238. self.currentFrame = f
  239. else
  240. self.currentFrame = state_descriptor.frameCount - 1 -- Is that wise ?
  241. end
  242. end
  243. --
  244. -- @brief Allows to speed up the animation with a multiplier
  245. -- @param sm (positive floating point) the multiplier
  246. --
  247. function LoveAnimation:setSpeedMultiplier(sm)
  248. -- negative multiplier support ? backwards animations ? to think abt it
  249. self.speedMultiplier = math.abs(sm)
  250. end
  251. --
  252. -- @brief Returns the animation to it's inital state,frame,speed,rotation
  253. --
  254. --
  255. function LoveAnimation:resetAnimation()
  256. self.currentState = self.descriptor.defaultState
  257. self.tick = 0
  258. self.speedMultiplier = 1
  259. self.active = true
  260. self.currentFrame = 0
  261. self.rotation = 0
  262. end
  263. local _CheckCollision = function(ax1,ay1,aw,ah, bx1,by1,bw,bh)
  264. local ax2,ay2,bx2,by2 = ax1 + aw, ay1 + ah, bx1 + bw, by1 + bh
  265. return ax1 < bx2 and ax2 > bx1 and ay1 < by2 and ay2 > by1
  266. end
  267. --
  268. -- @brief Checks for a collision between the sprite quad and the specified rectangle
  269. -- @return true if the two quads intersect, false otherwise
  270. --
  271. function LoveAnimation:intersects(x,y,width,height)
  272. local h = height or 1
  273. local w = width or 1
  274. local state_descriptor = self.descriptor.states[self.currentState]
  275. return _CheckCollision(
  276. self.x - (self.relativeOriginX * state_descriptor.frameW),
  277. self.y - (self.relativeOriginY * state_descriptor.frameH),
  278. state_descriptor.frameW,
  279. state_descriptor.frameH,
  280. x,y,w,h
  281. )
  282. end
  283. --
  284. -- @brief Switches the pause
  285. --
  286. --
  287. function LoveAnimation:togglePause()
  288. self.active = not self.active
  289. end
  290. --
  291. -- @brief Pauses the animation
  292. --
  293. --
  294. function LoveAnimation:pause()
  295. self.active = false
  296. end
  297. --
  298. -- @brief Unpauses the animation
  299. --
  300. --
  301. function LoveAnimation:unpause()
  302. self.active = true
  303. end
  304. --
  305. -- @brief Get the sprite's current position and size
  306. --
  307. function LoveAnimation:getGeometry()
  308. return {
  309. x = self.x,
  310. y = self.y,
  311. width = self:getFrameWidth(),
  312. height = self:getFrameHeight()
  313. }
  314. end
  315. --
  316. -- @brief Sets the sprite's current position
  317. -- @param x the x coordinate of the sprite
  318. -- @param y the y coordinate of the sprite
  319. --
  320. function LoveAnimation:setPosition(x, y)
  321. self.x = x
  322. self.y = y
  323. end
  324. --
  325. -- @brief Rotates the sprite to the specified angle
  326. -- @param r rotation angle (according to the love drawing functions)
  327. --
  328. function LoveAnimation:setRotation(r)
  329. self.rotation = r
  330. end
  331. --
  332. -- @brief Sets the sprite as visible or invisible
  333. -- @param v (bool) true if visible, false if invisible
  334. --
  335. function LoveAnimation:setVisibility(v)
  336. self.visible = v
  337. end
  338. --
  339. -- @brief Sets the origin of the sprite (default is top left corner)
  340. -- @param ox the x coordinate
  341. -- @param oy the y coordinate
  342. --
  343. -- The coordinates are a percentage relative to the sprite.
  344. -- e.g : setRelativeOrigin(0,0) is the top left corner
  345. -- e.g : setRelativeOrigin(0.5,0.5) is the center of the sprite
  346. --
  347. function LoveAnimation:setRelativeOrigin(ox, oy)
  348. -- rename to setAnchorPoint (perhaps clearer)
  349. self.relativeOriginX = ox
  350. self.relativeOriginY = oy
  351. end
  352. --
  353. -- @brief Flips the sprite horizontally
  354. --
  355. -- attention : the flips is dependant on the origin
  356. --
  357. function LoveAnimation:toggleHorizontalFlip()
  358. self.flipX = -1 * self.flipX
  359. end
  360. --
  361. -- @brief Flips the sprite horizontally according to bool
  362. -- @param bool (bool) true if the sprite should be flipped horizontally
  363. --
  364. -- attention : the flips is dependant on the origin
  365. --
  366. function LoveAnimation:setHorizontalFlip(bool)
  367. self.flipX = bool and -1 or 1
  368. end
  369. --
  370. -- @brief Gets the width of the current frame
  371. -- @return the current width
  372. --
  373. function LoveAnimation:getFrameWidth()
  374. local state_descriptor = self.descriptor.states[self.currentState]
  375. return state_descriptor.frameW
  376. end
  377. --
  378. -- @brief Gets the height of the current frame
  379. -- @return the current height
  380. --
  381. function LoveAnimation:getFrameHeight()
  382. local state_descriptor = self.descriptor.states[self.currentState]
  383. return state_descriptor.frameH
  384. end
  385. --
  386. -- @brief Gets width & height of the current frame
  387. -- @return current dimension
  388. --
  389. function LoveAnimation:getFrameDimension()
  390. local state_descriptor = self.descriptor.states[self.currentState]
  391. return state_descriptor.frameW, state_descriptor.frameH
  392. end
  393. --
  394. -- EVENTS
  395. --
  396. --
  397. -- @brief Adds a listener to the animation. Called when the last frame of a state is reached
  398. -- @param state (string) The name of the state
  399. -- @param callback (function) The callback function, recieves the LoveAnimation object as param
  400. -- Set the callback to nil to stop receiving events
  401. --
  402. function LoveAnimation:onStateEnd(state, callback)
  403. self._stateEndCallbacks[state] = callback
  404. end
  405. --
  406. -- @brief Adds a listener to the animation. Called when the state starts
  407. -- @param state (string) The name of the state
  408. -- @param callback (function) The callback function, recieves the LoveAnimation object as param
  409. -- Set the callback to nil to stop receiving events
  410. --
  411. function LoveAnimation:onStateStart(state, callback)
  412. self._stateStartCallbacks[state] = callback
  413. end