Advertisement
Snusmumriken

Love2d short animation lib

Aug 10th, 2016
448
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 9.23 KB | None | 0 0
  1. --[[
  2.     Reference:
  3.     Сетка спрайтов вида:
  4.      _|1|2|3|4|
  5.      1|x|x|x|x|
  6.      2|x|x|N|x|
  7.      3|x|x|x|x|,
  8.      
  9.     Где спрайт N имеет координаты 3, 2, если считать позицию по сетке,
  10.     или 7, если считать последовательно.
  11.     Создание сетки:
  12.     list = grid(image, 4, 3)
  13. ]]
  14.  
  15.  
  16. local function err(v, lvl, ...)
  17.     error(v:format(...), lvl)
  18. end
  19.  
  20.  
  21. local min, max, floor, ceil, sin, cos = math.min, math.max, math.floor, math.ceil, math.sin, math.cos
  22. local graphics = love.graphics
  23. grid = {}                                                                           -- Spritegrid class
  24. local animated = {}                                                     -- Assisted class, represent animated object
  25. local static = {}                                                           -- Assisted class, represent static object
  26. setmetatable(grid, {
  27.     __call = function(self, img, sx, sy, mx, my)                -- img - image, sx & sy - sprite count in grid
  28.         local o = {img = img}
  29.         o.batch = graphics.newSpriteBatch(o.img, 20000)
  30.         o.batchfree = {}                                                    -- Free id for reusing
  31.         o.animation = {}                                                    -- Animations list
  32.         o.sx = sx or 1                                                      -- One by default
  33.         o.sy = sy or 1
  34.         local imgx, imgy = o.img:getDimensions()
  35.         local mx, my = mx or 0, my or 0                     -- margin
  36.         o.sw, o.sh = floor(imgx / o.sx), floor(imgy / o.sy) -- One sprite dimensions
  37.         o.quad = {}                                                             -- Quad list
  38.         for j = 0, o.sy-1 do
  39.             for i = 0, o.sx-1 do
  40.                 table.insert(o.quad, graphics.newQuad(i*o.sw + mx, j*o.sh + my, o.sw - mx*2, o.sh - my*2, imgx, imgy))
  41.             end
  42.         end
  43.         self.__index = self
  44.         return setmetatable(o, {__index = self})
  45.     end
  46. })
  47.  
  48.     local function checkSequence(t, maxX, maxY)
  49.         local count = maxX * maxX
  50.         for i, position in ipairs(t) do
  51.             if position < 1 or position > count then
  52.                 err('Grid:newAnimation error: %d sprite not found, grid dimensions: [%d, %d]', 3, i, maxX, maxY)
  53.             end
  54.         end
  55.         return t
  56.     end
  57.  
  58.     local function linearize(t, y)
  59.         local buf = {}
  60.         if type(t[1]) == 'table' then
  61.                 for i, v in ipairs(t) do
  62.                     table.insert(buf, v[1] + (v[2]-1)*y)
  63.                 end
  64.         end
  65.         return #buf > 0 or t
  66.     end
  67.  
  68.     function grid:newAnimation(name, t, time, loop)
  69.         self.animation[name] = {
  70.             seq = checkSequence(linearize(t, self.sy), self.sx, self.sy),
  71.             count = #t,
  72.             delay = time/#t,
  73.             loop = loop
  74.         }
  75.     end
  76.    
  77.     function grid:getQuad(x, y)
  78.         return y and self.quad[(y-1)*self.sy+x]     --вычисление позиции квада в списке
  79.                           or self.quad[x]
  80.     end
  81.  
  82.     function grid:getFreeSequence(n, count)
  83.         local s, seq = 0, 0
  84.         for i, v in ipairs(table.sort(self.batchfree)) do
  85.             seq = s == (v - 1) and seq + 1 or 0
  86.             s = v
  87.             if seq == count then return v - count end
  88.         end
  89.     end
  90.  
  91.    
  92.    
  93.     function grid:add(n, ...)
  94.         if #self.batchfree > 0 then
  95.             table.sort(self.batchfree)
  96.             print('reuse sprite')
  97.             return self.batchfree[1], self.batch:set(self.batchfree[1], n and self.quad[n] or self.quad[1], ...), table.remove(self.batchfree, 1)
  98.         end
  99.         return self.batch:add(n and self.quad[n] or self.quad[1], ...)
  100.     end
  101.  
  102.    
  103.     function grid:free(n)
  104.         table.insert(self.batchfree, n)
  105.         self.batch:set(n, 0, 0, 0, 0, 0)
  106.     end
  107.  
  108.     function grid:update(id, name, time, ...)
  109.         self.batch:setColor(graphics.getColor())
  110.         local a = self.animation[name]
  111.         local x, y = 1, 1
  112.         local n = a.loop and a.seq[    ceil( time / a.delay % a.count)]
  113.                                             or a.seq[min(ceil( time / a.delay), a.count)]
  114.                                             or 1
  115.         self.batch:set(id, self:getQuad(n), ...)
  116.     end
  117.    
  118.     function grid:set(id, n, ...)
  119.         self.batch:set(id, self:getQuad(n), ...)
  120.     end
  121.  
  122.     function grid:getDimensions()
  123.         return self.sw, self.sh
  124.     end
  125.  
  126.     function grid:newDynamic(...)
  127.         return animated(self, ...)
  128.     end
  129.  
  130.     function grid:newStatic(n, ...)
  131.         return static(self, n, ...)
  132.     end
  133.  
  134.     function grid:draw(...)
  135.         collectgarbage()
  136.         graphics.draw(self.batch, ...)
  137.     end
  138.  
  139.     local function mt__gc(t, mt)        --collect garbage method trick
  140.         local mt = mt or getmetatable(t)
  141.         local prox = newproxy(true)
  142.         getmetatable(prox).__gc = function() mt.__gc(t) end
  143.         t[prox] = true
  144.         print('proxy')
  145.         return setmetatable(t, mt)
  146.     end
  147.    
  148.     setmetatable(animated, {
  149.         __call = function(self, grid, default, ...) -- grid as grid, t as table with sprite sequense, time as anim length and loop flag
  150.             local o = {timer = 0, grid = grid}
  151.             print(unpack({...}))
  152.             o.id = o.grid:add(1, ...)
  153.             o.name = default
  154.             self.__index = self
  155.             return mt__gc(o, {__index = self, __gc = function() o.grid:free(o.id) end})
  156.         end
  157.     })
  158.    
  159.     function animated:set(name)
  160.         self.timer = 0
  161.         self.name = name
  162.     end
  163.    
  164.     function animated:setSprite(n)
  165.         self.timer = self.grid.animation[self.name].delay*n
  166.     end
  167.    
  168.     function animated:get()
  169.         return self.name, self.timer
  170.     end
  171.  
  172.     function animated:getSize()
  173.         return self.grid:getSize()
  174.     end
  175.    
  176.     function animated:update(dt, ...)
  177.         if self.name then
  178.             self.timer = self.timer + dt
  179.         end
  180.         self.grid:update(self.id, self.name, self.timer, ...)
  181.     end
  182.  
  183.     setmetatable(static, {
  184.         __call = function(self, grid, sprite, ...) -- grid as grid, t as table with sprite sequense, time as anim length and loop flag
  185.             local o = {grid = grid, sprite = sprite}
  186.             o.id = o.grid:add(sprite, ...)
  187.             self.__index = self
  188.             return mt__gc(o, {__index = self, __gc = function() o.grid:free(o.id) end})
  189.         end
  190.     })
  191.    
  192.     function static:set(n)
  193.         self.sprite = n
  194.     end
  195.  
  196.     function static:update(dt, ...)
  197.         self.grid:set(self.id, self.sprite, ...)
  198.     end
  199.  
  200.     local t = {
  201.         body =          {type = 'static', state = 2, x = 10, y = 20, w = 30, h = 20, r = 0.3, sx = 35, sy = 20},
  202.         lefthand =  {type = 'dynamic', state = 'wawa', x = 10, y = 20, w = 30, h = 20, r = 0.3, sx = 35, sy = 20},
  203.         {drawable, x, y, r, sx, sy, ox, oy, kx, ky }
  204.     }
  205.  
  206.     local function drawunpack(t)
  207.         return t[1], t[2], t[3], t[4], t[5], t[6], t[7], t[8], t[9]
  208.     end
  209.  
  210.     local function rewrite(t1, t2)
  211.         for i, v in ipairs(t2) do   t1[i] = t2[i] or t1[i] end
  212.         return t1
  213.     end
  214.  
  215.  
  216.     local mapping = {x = 1, y = 2, r = 3, sx = 4, sy = 5, ox = 6, oy = 7, kx = 8, ky = 9}
  217.     local backmap = {'x', 'y', 'r', 'sx', 'sy', 'ox', 'oy', 'kx', 'ky'}
  218.     local default = {x = 0, y = 0, r = 0, sx = 1, sy = 1, ox = 0, oy = 0, kx = 0, ky = 0}
  219.  
  220.     local function setPos(...)
  221.         local t = {}
  222.         local m = {...}
  223.         for i = 1, #backmap do
  224.             t[backmap[i]] = m[i] or default[backmap[i]]
  225.         end
  226.         return t
  227.     end
  228.  
  229. posMT = function(...)
  230.     local o = {0,0,0,1,1,0,0,0,0}
  231.     return setmetatable(rewrite(o, {...}), {
  232.                     __index = function(self, key) return self[mapping[key]] end,
  233.                     __newindex = function(self, key, value) if mapping[key] then self[mapping[key]] = value else self[key] = value end end})
  234. end
  235.  
  236.     local st = {0,0,0,1,1,0,0,0,0}
  237.     animatedObject = {}
  238.     setmetatable(animatedObject, {
  239.         __call = function(self, ...)
  240.             local o = setPos(...)
  241.             o.flipped = {x = 1, y = 1}
  242.             o.item = {}
  243.             self.__index = self
  244.             return mt__gc(o, {__index = self})
  245.         end
  246.     })
  247.  
  248. function animatedObject:getItemPosition(v)
  249.     local flip = self.flipped
  250.     return flip.x and self.x + (self.w + v.x)*self.sx or self.x + v.x*self.sx,
  251.                  flip.y and self.y + (self.h + v.y)*self.sy or self.y + v.y*self.sy,
  252.                  self.r + v.r,
  253.                  flip.x and -self.sx or self.sx,
  254.                  flip.y and -self.sy or self.sy,
  255.                  v.ox,
  256.                  v.oy
  257.     end
  258.    
  259. function animatedObject:addItem(n, grid, state, default, ...)
  260.     if type(n) == 'string' then
  261.         self.item[name] = setPos(...)
  262.         v = self.item[name]
  263.         v.w, v.h = grid:getDimensions()
  264.         if not self.w or not self.h then self.w, self.h = grid:getDimensions() end
  265.         v.sprite = state == 'static'  and grid:newStatic(1, self:getItemPosition(v))
  266.                         or state == 'dynamic' and grid:newDynamic(default, self:getItemPosition(v))
  267.     elseif type(n) == 'table' then
  268.         self.item[n.name] = setPos(n.x or 0, n.y or 0, n.r or 0, n.sx or 1, n.sy or 1, n.ox or 0, n.oy or 0, n.kx or 0, n.ky or 0)
  269.         v = self.item[n.name]
  270.         v.w, v.h = n.grid:getDimensions()
  271.         if not self.w or not self.h then self.w, self.h = v.w, v.h end
  272.         v.sprite = n.type == 'static'  and n.grid:newStatic(n.default, self:getItemPosition(v))
  273.                         or n.type == 'dynamic' and n.grid:newDynamic(n.default, self:getItemPosition(v))
  274.     end
  275. end
  276.  
  277. function animatedObject:flip(ord, v)
  278.     ord = ord or 'x'
  279.     self.flipped[ord] = v
  280. end
  281.    
  282. function animatedObject:update(dt, x, y, r, sx, sy, ox, oy)     --обновление позиции
  283.     self.x = x or self.x
  284.     self.y = y or self.y
  285.     self.r = r or self.r
  286.     self.sx = sx or 1
  287.     self.sy = sy or 1
  288.     self.ox = ox or 0
  289.     self.oy = oy or 0
  290.     local flip = self.flip
  291.     for k, item in pairs(self.item) do
  292.         item.sprite:update(dt, self:getItemPosition(item))              -- dt - дельта времени, для анимаций, таймлайнов и всего такого
  293.     end
  294. end
  295.  
  296. function animatedObject:draw()                                                              -- отрисовка для дебага (если спрайт пуст - нарисует bounding box)
  297.     for k, v in pairs(self.item) do
  298.         love.graphics.rectangle(
  299.             'line',
  300.             self.flip and self.x + v.x + self.w - v.ox or self.x + v.x + v.ox,
  301.             self.flip and self.y + v.y + self.h - v.oy or self.y + v.y + v.oy,
  302.             self.flip and -v.w or v.w,
  303.             self.flip and -v.h or v.h)
  304.     end
  305. end
  306.  
  307. function animatedObject:setAnimation(item, animation)
  308.     self.item[item].sprite:set(animation)
  309. end
  310.  
  311. function grid:newObject(...)
  312.     return animatedObject(...)
  313. end
  314.  
  315. return grid
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement